diff --git a/LICENSE b/LICENSE index 9d65fc76cb949e..8ae68e30e40ca0 100644 --- a/LICENSE +++ b/LICENSE @@ -1316,6 +1316,58 @@ 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 0190e31b41a214..575860e1fd3495 100755 --- a/configure.py +++ b/configure.py @@ -117,6 +117,11 @@ choices=valid_os, help='operating system to build for ({0})'.format(', '.join(valid_os))) +parser.add_option('--experimental-quic', + action='store_true', + dest='experimental_quic', + help='enable experimental quic support') + parser.add_option('--gdb', action='store_true', dest='gdb', @@ -259,6 +264,48 @@ dest='shared_nghttp2_libpath', help='a directory to search for the shared nghttp2 DLLs') +shared_optgroup.add_option('--shared-ngtcp2', + action='store_true', + dest='shared_ngtcp2', + help='link to a shared ngtcp2 DLL instead of static linking') + +shared_optgroup.add_option('--shared-ngtcp2-includes', + action='store', + dest='shared_ngtcp2_includes', + help='directory containing ngtcp2 header files') + +shared_optgroup.add_option('--shared-ngtcp2-libname', + action='store', + dest='shared_ngtcp2_libname', + default='ngtcp2', + help='alternative lib name to link to [default: %default]') + +shared_optgroup.add_option('--shared-ngtcp2-libpath', + action='store', + dest='shared_ngtcp2_libpath', + help='a directory to search for the shared ngtcp2 DLLs') + +shared_optgroup.add_option('--shared-nghttp3', + action='store_true', + dest='shared_nghttp3', + help='link to a shared nghttp3 DLL instead of static linking') + +shared_optgroup.add_option('--shared-nghttp3-includes', + action='store', + dest='shared_nghttp3_includes', + help='directory containing nghttp3 header files') + +shared_optgroup.add_option('--shared-nghttp3-libname', + action='store', + dest='shared_nghttp3_libname', + default='nghttp3', + help='alternative lib name to link to [default: %default]') + +shared_optgroup.add_option('--shared-nghttp3-libpath', + action='store', + dest='shared_nghttp3_libpath', + help='a directory to search for the shared nghttp3 DLLs') + shared_optgroup.add_option('--shared-openssl', action='store_true', dest='shared_openssl', @@ -1146,6 +1193,14 @@ def configure_node(o): else: o['variables']['debug_nghttp2'] = 'false' + if options.experimental_quic: + if options.shared_openssl: + raise Exception('QUIC requires modified version of OpenSSL and cannot be' + ' enabled with --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) @@ -1273,6 +1328,8 @@ def without_ssl_error(option): without_ssl_error('--openssl-no-asm') if options.openssl_fips: without_ssl_error('--openssl-fips') + 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 new file mode 100644 index 00000000000000..37562ea58cd74c --- /dev/null +++ b/deps/nghttp3/COPYING @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000000000..0aee7749bae78e --- /dev/null +++ b/deps/nghttp3/lib/includes/config.h @@ -0,0 +1,39 @@ + +/* Edited to match src/node.h. */ +#include + +#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 // size_t, ssize_t +#endif // _WIN32 + +#ifdef _MSC_VER +# include +# define __builtin_popcount __popcnt +#endif + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ diff --git a/deps/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/nghttp3/lib/includes/nghttp3/nghttp3.h new file mode 100644 index 00000000000000..5c3a95c4c6f3b5 --- /dev/null +++ b/deps/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -0,0 +1,1759 @@ +/* + * 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 +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include +#include + +#include + +#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; + +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; + +/** + * @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, + size_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 size_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 size_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 the 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_header_list_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); + +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_HEADER_LIST_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; + +/** + * @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, size_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); + +/** + * @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); + +/** + * @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 new file mode 100644 index 00000000000000..8f3108c95fdf6b --- /dev/null +++ b/deps/nghttp3/lib/includes/nghttp3/version.h @@ -0,0 +1,46 @@ +/* + * 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.90" + +/** + * @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 0x00015a + +#endif /* NGHTTP3_VERSION_H */ diff --git a/deps/nghttp3/lib/nghttp3_buf.c b/deps/nghttp3/lib/nghttp3_buf.c new file mode 100644 index 00000000000000..aae075a73cc4be --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_buf.c @@ -0,0 +1,90 @@ +/* + * 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 new file mode 100644 index 00000000000000..493d81be315ba1 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_buf.h @@ -0,0 +1,74 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..457cd5cb6d1a35 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conn.c @@ -0,0 +1,3232 @@ +/* + * 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 +#include +#include + +#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 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; + nghttp3_node_id nid; + + conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); + if (conn == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init(&conn->root, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_ROOT, 0), + 0, NGHTTP3_DEFAULT_WEIGHT, NULL, mem); + + nghttp3_tnode_init(&conn->orphan_root, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_ROOT, 1), + 0, NGHTTP3_DEFAULT_WEIGHT, NULL, mem); + + rv = nghttp3_map_init(&conn->streams, mem); + if (rv != 0) { + goto streams_init_fail; + } + + rv = nghttp3_map_init(&conn->placeholders, mem); + if (rv != 0) { + goto placeholders_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); + + 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->placeholders); +placeholders_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_placeholder(nghttp3_map_entry *ent, void *ptr) { + nghttp3_placeholder *ph = nghttp3_struct_of(ent, nghttp3_placeholder, me); + const nghttp3_mem *mem = ptr; + + nghttp3_placeholder_del(ph, 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) { + 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); + + 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->placeholders, free_placeholder, + (void *)conn->mem); + nghttp3_map_free(&conn->placeholders); + + nghttp3_map_each_free(&conn->streams, free_stream, NULL); + nghttp3_map_free(&conn->streams); + + nghttp3_tnode_free(&conn->orphan_root); + nghttp3_tnode_free(&conn->root); + + 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_dependency(conn, &stream, stream_id, 0, + NULL); + } + 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_dependency(conn, &stream, stream_id, 0, + NULL); + 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); +} + +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}, NULL, NULL, NULL, 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: + 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; + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) && + pp->node.parent == &conn->orphan_root) { + nghttp3_tnode_remove(&pp->node); + nghttp3_tnode_insert(&pp->node, &stream->node); + } + } 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, NGHTTP3_DEFAULT_WEIGHT, &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, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); + 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; +} + +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; + } + + rv = nghttp3_tnode_squash(&pp->node); + if (rv != 0) { + return rv; + } + + 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, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); + 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; + + assert(srclen); + + 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_HEADER_LIST_SIZE: + dest->max_header_list_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) { + return nghttp3_conn_create_stream_dependency( + conn, pstream, stream_id, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); +} + +int nghttp3_conn_create_stream_dependency(nghttp3_conn *conn, + nghttp3_stream **pstream, + int64_t stream_id, uint32_t weight, + nghttp3_tnode *parent) { + nghttp3_stream *stream; + int rv; + nghttp3_stream_callbacks callbacks = { + conn_stream_acked_data, + }; + + rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, weight, parent, + &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_placeholder(nghttp3_conn *conn, + nghttp3_placeholder **pph, int64_t ph_id, + uint32_t weight, nghttp3_tnode *parent) { + nghttp3_placeholder *ph; + int rv; + + rv = nghttp3_placeholder_new(&ph, ph_id, conn->next_seq, weight, parent, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_insert(&conn->placeholders, &ph->me); + if (rv != 0) { + nghttp3_placeholder_del(ph, conn->mem); + return rv; + } + + ++conn->next_seq; + *pph = ph; + + return 0; +} + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, uint32_t weight, + nghttp3_tnode *parent) { + nghttp3_push_promise *pp; + int rv; + + rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, weight, parent, + 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_placeholder *nghttp3_conn_find_placeholder(nghttp3_conn *conn, + int64_t ph_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->placeholders, (key_type)ph_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_placeholder, 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_dependency(conn, &stream, stream_id, 0, NULL); + 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_dependency(conn, &stream, qenc_stream_id, 0, + NULL); + 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_dependency(conn, &stream, qdec_stream_id, 0, + NULL); + 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_stream_unschedule(stream); + } + + return ncnt; +} + +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { + nghttp3_tnode *node = nghttp3_tnode_get_next(&conn->root); + + if (node == NULL) { + node = nghttp3_tnode_get_next(&conn->orphan_root); + if (node == NULL) { + return NULL; + } + } + + if (node->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { + return nghttp3_struct_of(node, nghttp3_push_promise, node)->stream; + } + + return nghttp3_struct_of(node, nghttp3_stream, node); +} + +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)) { + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_schedule(stream); + } + nghttp3_stream_unschedule(stream); + } + + return 0; +} + +int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, + size_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_stream_schedule(stream); + } + + return 0; +} + +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, + NGHTTP3_DEFAULT_WEIGHT, &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_stream_schedule(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_dependency(conn, &stream, stream_id, 0, NULL); + 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->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; + } + + rv = nghttp3_tnode_squash(&pp->node); + if (rv != 0) { + return rv; + } + + 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; + + if (nghttp3_stream_bidi_or_push(stream)) { + nghttp3_stream_unschedule(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_stream_ensure_scheduled(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_stream_ensure_scheduled(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); + int rv; + + 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; + + rv = nghttp3_stream_squash(stream); + if (rv != 0) { + return rv; + } + + 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_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_header_list_size = NGHTTP3_VARINT_MAX; +} + +int nghttp3_placeholder_new(nghttp3_placeholder **pph, int64_t ph_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem) { + nghttp3_placeholder *ph; + nghttp3_node_id nid; + + ph = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_placeholder)); + if (ph == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &ph->node, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PLACEHOLDER, ph_id), seq, + weight, parent, mem); + + ph->me.key = (key_type)ph_id; + + *pph = ph; + + return 0; +} + +void nghttp3_placeholder_del(nghttp3_placeholder *ph, const nghttp3_mem *mem) { + if (ph == NULL) { + return; + } + + nghttp3_tnode_free(&ph->node); + + nghttp3_mem_free(mem, ph); +} + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, 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, weight, parent, mem); + + pp->me.key = (key_type)push_id; + pp->node.nid.id = push_id; + pp->http.status_code = -1; + pp->http.content_length = -1; + + if (parent->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM) { + pp->stream_id = parent->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 new file mode 100644 index 00000000000000..a29aeb89f994c5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conn.h @@ -0,0 +1,279 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 struct { + nghttp3_map_entry me; + nghttp3_tnode node; +} nghttp3_placeholder; + +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 client. */ + 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_tnode root; + nghttp3_tnode orphan_root; + nghttp3_conn_callbacks callbacks; + nghttp3_map streams; + nghttp3_map placeholders; + nghttp3_map pushes; + nghttp3_qpack_decoder qdec; + nghttp3_qpack_encoder qenc; + nghttp3_pq qpack_blocked_streams; + 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_placeholder *nghttp3_conn_find_placeholder(nghttp3_conn *conn, + int64_t ph_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_stream_dependency(nghttp3_conn *conn, + nghttp3_stream **pstream, + int64_t stream_id, uint32_t weight, + nghttp3_tnode *parent); + +int nghttp3_conn_create_placeholder(nghttp3_conn *conn, + nghttp3_placeholder **pph, int64_t ph_id, + uint32_t weight, nghttp3_tnode *parent); + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, uint32_t weight, + nghttp3_tnode *parent); + +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); + +/* + * 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_placeholder_new(nghttp3_placeholder **pph, int64_t ph_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem); + +void nghttp3_placeholder_del(nghttp3_placeholder *ph, const nghttp3_mem *mem); + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, 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 new file mode 100644 index 00000000000000..b8a7f8a4d0fa70 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conv.c @@ -0,0 +1,130 @@ +/* + * 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 +#include + +#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; +} diff --git a/deps/nghttp3/lib/nghttp3_conv.h b/deps/nghttp3/lib/nghttp3_conv.h new file mode 100644 index 00000000000000..d14bdaa281b12a --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conv.h @@ -0,0 +1,181 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH +# define nghttp3_ntohl64(N) be64toh(N) +# define nghttp3_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# define nghttp3_bswap64(N) \ + ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +# define nghttp3_ntohl64(N) nghttp3_bswap64(N) +# define nghttp3_htonl64(N) nghttp3_bswap64(N) +#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); + +#endif /* NGHTTP3_CONV_H */ diff --git a/deps/nghttp3/lib/nghttp3_debug.c b/deps/nghttp3/lib/nghttp3_debug.c new file mode 100644 index 00000000000000..4021b0dc469b66 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_debug.c @@ -0,0 +1,61 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..01ed918414cfe5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_debug.h @@ -0,0 +1,44 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..23033d4640d622 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_err.c @@ -0,0 +1,123 @@ +/* + * 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 new file mode 100644 index 00000000000000..2fa914f86b189e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_err.h @@ -0,0 +1,34 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP3_ERR_H */ diff --git a/deps/nghttp3/lib/nghttp3_frame.c b/deps/nghttp3/lib/nghttp3_frame.c new file mode 100644 index 00000000000000..479b794f9aebb9 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_frame.c @@ -0,0 +1,200 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..d9245d9fb30c3b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_frame.h @@ -0,0 +1,141 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_buf.h" + +/* + * 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 new file mode 100644 index 00000000000000..7057cfde2cff2b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_gaptr.c @@ -0,0 +1,131 @@ +/* + * 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 +#include + +#include "nghttp3_macro.h" + +int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { + int rv; + nghttp3_range range = {0, UINT64_MAX}; + nghttp3_ksl_key key; + + 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, nghttp3_ksl_key_ptr(&key, &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; + nghttp3_ksl_key key, old_key; + + it = + nghttp3_ksl_lower_bound_compar(&gaptr->gap, nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); + + for (; !nghttp3_ksl_it_end(&it);) { + k = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + m = nghttp3_range_intersect(&q, &k); + if (!nghttp3_range_len(&m)) { + break; + } + + if (nghttp3_range_eq(&k, &m)) { + nghttp3_ksl_remove(&gaptr->gap, &it, nghttp3_ksl_key_ptr(&key, &k)); + continue; + } + nghttp3_range_cut(&l, &r, &k, &m); + if (nghttp3_range_len(&l)) { + nghttp3_ksl_update_key(&gaptr->gap, nghttp3_ksl_key_ptr(&old_key, &k), + nghttp3_ksl_key_ptr(&key, &l)); + + if (nghttp3_range_len(&r)) { + rv = nghttp3_ksl_insert(&gaptr->gap, &it, nghttp3_ksl_key_ptr(&key, &r), + NULL); + if (rv != 0) { + return rv; + } + } + } else if (nghttp3_range_len(&r)) { + nghttp3_ksl_update_key(&gaptr->gap, nghttp3_ksl_key_ptr(&old_key, &k), + nghttp3_ksl_key_ptr(&key, &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); + nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + return r.begin; +} + +nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset) { + nghttp3_range q = {offset, offset + 1}; + nghttp3_ksl_key key; + return nghttp3_ksl_lower_bound_compar(&gaptr->gap, + nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); +} + +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, + size_t datalen) { + nghttp3_ksl_key key; + nghttp3_range q = {offset, offset + datalen}; + nghttp3_ksl_it it = + nghttp3_ksl_lower_bound_compar(&gaptr->gap, nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); + nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + nghttp3_range m = nghttp3_range_intersect(&q, &k); + return nghttp3_range_len(&m) == 0; +} diff --git a/deps/nghttp3/lib/nghttp3_gaptr.h b/deps/nghttp3/lib/nghttp3_gaptr.h new file mode 100644 index 00000000000000..6972b404432377 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_gaptr.h @@ -0,0 +1,98 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..c66d9c9d3069ae --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_http.c @@ -0,0 +1,731 @@ +/* + * 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 +#include + +#include "nghttp3_stream.h" +#include "nghttp3_macro.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))); +} + +static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int trailers, + int connect_protocol) { + 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: { + 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; +} + +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: { + 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 new file mode 100644 index 00000000000000..00b74f2a260c0e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_http.h @@ -0,0 +1,146 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..cd8fd82e6b2bb5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_idtr.c @@ -0,0 +1,88 @@ +/* + * 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 + +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 new file mode 100644 index 00000000000000..ba2808a11f5c68 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_idtr.h @@ -0,0 +1,99 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..35bae07d1e7d70 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ksl.c @@ -0,0 +1,751 @@ +/* + * 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 +#include +#include +#include + +#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); +} + +/* + * ksl_node_key assigns the pointer to key of |node| to key->ptr and + * returns |key|. + */ +static nghttp3_ksl_key *ksl_node_key(nghttp3_ksl_key *key, + nghttp3_ksl_node *node) { + key->ptr = &node->key; + return key; +} + +/* + * ksl_nth_node returns |n|th node under |blk|. + */ +static nghttp3_ksl_node *ksl_nth_node(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t n) { + return (nghttp3_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} + +nghttp3_ksl_node *nghttp3_ksl_nth_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t n) { + return ksl_nth_node(ksl, blk, n); +} + +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, 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 = 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 = ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, &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 = ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, &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 = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key->ptr); + 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; + nghttp3_ksl_key node_key; + + while (right - left > 1) { + mid = (left + right) / 2; + node = ksl_nth_node(ksl, blk, (size_t)mid); + if (compar(ksl_node_key(&node_key, node), 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; + nghttp3_ksl_key node_key; + 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) { + 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 = 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 = ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key->ptr); + 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 = 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(ksl_node_key(&node_key, node), key)) { + node = ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + ksl_node_set_key(ksl, node, key->ptr); + } + } + } + + 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 = ksl_nth_node(ksl, blk, i)->blk; + rblk = 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, ksl_nth_node(ksl, blk, i), + &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 = ksl_nth_node(ksl, blk, i - 1); + rnode = ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK); + assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK); + + dest = ksl_nth_node(ksl, lnode->blk, lnode->blk->n); + src = 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 = ksl_nth_node(ksl, blk, i); + rnode = 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 = ksl_nth_node(ksl, rnode->blk, 0); + src = ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); + + memcpy(dest, src, ksl->nodelen); + + --lnode->blk->n; + ksl_node_set_key(ksl, lnode, + &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); +} + +void 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 && + ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && + 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); + + assert(i < blk->n); + + if (blk->leaf) { + assert(i < blk->n); + 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; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP3_KSL_MIN_NBLK) { + if (i > 0 && + ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + } else if (i + 1 < blk->n && + ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + } 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 = 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 = 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 = 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 = 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; + nghttp3_ksl_key node_key; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, ksl_node_key(&node_key, node), old_key)); + ksl_node_set_key(ksl, node, new_key->ptr); + return; + } + + ksl_node_key(&node_key, node); + if (key_equal(ksl->compar, &node_key, old_key) || + ksl->compar(&node_key, new_key)) { + ksl_node_set_key(ksl, node, new_key->ptr); + } + + blk = node->blk; + } +} + +static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { + size_t i; + nghttp3_ksl_node *node; + nghttp3_ksl_key node_key; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *ksl_node_key(&node_key, node)->i); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, 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, 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 ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void nghttp3_ksl_it_next(nghttp3_ksl_it *it) { + assert(!nghttp3_ksl_it_end(it)); + + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} + +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_end(const nghttp3_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} + +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +nghttp3_ksl_key nghttp3_ksl_it_key(const nghttp3_ksl_it *it) { + nghttp3_ksl_key node_key; + + assert(it->i < it->blk->n); + + return *ksl_node_key(&node_key, ksl_nth_node(it->ksl, it->blk, it->i)); +} + +nghttp3_ksl_key *nghttp3_ksl_key_ptr(nghttp3_ksl_key *key, const void *ptr) { + key->ptr = ptr; + return key; +} + +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_range *a = lhs->ptr, *b = rhs->ptr; + 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->ptr, *b = rhs->ptr; + 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 new file mode 100644 index 00000000000000..89ca732aeb6fee --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ksl.h @@ -0,0 +1,336 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +/* + * Skip List using single key instead of range. + */ + +#define NGHTTP3_KSL_DEGR 8 +/* 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 union { + /* i is defined to retrieve int64_t key for convenience. */ + const int64_t *i; + /* ptr points to the key. */ + const void *ptr; +} 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 assumes that |key| does not exist in |ksl|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +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|. It assumes such + * the key is included in |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. + */ +void 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|. This + * function is provided for unit testing. + */ +nghttp3_ksl_node *nghttp3_ksl_nth_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t 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. + */ +void nghttp3_ksl_it_next(nghttp3_ksl_it *it); + +/* + * 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. + */ +int nghttp3_ksl_it_end(const nghttp3_ksl_it *it); + +/* + * 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. + */ +nghttp3_ksl_key nghttp3_ksl_it_key(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_key_ptr is a convenient function which initializes + * |key| with |ptr| and returns |key|. + */ +nghttp3_ksl_key *nghttp3_ksl_key_ptr(nghttp3_ksl_key *key, const void *ptr); + +/* + * 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 new file mode 100644 index 00000000000000..6ee704cc47dd98 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_macro.h @@ -0,0 +1,47 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#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 new file mode 100644 index 00000000000000..e383d2b0b8a3fc --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_map.c @@ -0,0 +1,213 @@ +/* + * 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 + +#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_entry *)); + if (map->table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp3_map_free(nghttp3_map *map) { + nghttp3_mem_free(map->mem, map->table); +} + +void nghttp3_map_each_free(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp3_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int nghttp3_map_each(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry, *next; + for (entry = map->table[i]; entry;) { + next = entry->next; + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + entry = next; + } + } + 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; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + return h & (mod - 1); +} + +static int insert(nghttp3_map_entry **table, uint32_t tablelen, + nghttp3_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + nghttp3_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(nghttp3_map *map, uint32_t new_tablelen) { + uint32_t i; + nghttp3_map_entry **new_table; + + new_table = + nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_entry *)); + if (new_table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp3_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + nghttp3_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +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 = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp3_map_entry *nghttp3_map_find(const nghttp3_map *map, key_type key) { + uint32_t h; + nghttp3_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int nghttp3_map_remove(nghttp3_map *map, key_type key) { + uint32_t h; + nghttp3_map_entry **dst; + + h = hash(key, map->tablelen); + + for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { + if ((*dst)->key != key) { + continue; + } + + *dst = (*dst)->next; + --map->size; + return 0; + } + return NGHTTP3_ERR_INVALID_ARGUMENT; +} + +void nghttp3_map_clear(nghttp3_map *map) { + uint32_t i; + + for (i = 0; i < map->tablelen; ++i) { + map->table[i] = NULL; + } + + map->size = 0; +} + +size_t nghttp3_map_size(const nghttp3_map *map) { return map->size; } diff --git a/deps/nghttp3/lib/nghttp3_map.h b/deps/nghttp3/lib/nghttp3_map.h new file mode 100644 index 00000000000000..0b7c0d28c37c7f --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_map.h @@ -0,0 +1,147 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.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_entry **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(const 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(const 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(const 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(const 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 new file mode 100644 index 00000000000000..e5f93f10bdabf2 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_mem.c @@ -0,0 +1,77 @@ +/* + * 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 new file mode 100644 index 00000000000000..55ef86b4f921c9 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_mem.h @@ -0,0 +1,45 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 new file mode 100644 index 00000000000000..5d09050ae63798 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_pq.c @@ -0,0 +1,168 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..e80b1bc43b9f3e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_pq.h @@ -0,0 +1,129 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..08303c05be06a0 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack.c @@ -0,0 +1,4056 @@ +/* + * 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 +#include +#include + +#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; + } + 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, + size_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, size_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 { + size_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->ptr; + const nghttp3_blocked_streams_key *b = rhs->ptr; + 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; +} + +size_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; + size_t min_cnt = SIZE_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|. |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, size_t max_cnt, + size_t min_cnt) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + nghttp3_qpack_header_block_ref *ref; + const nghttp3_mem *mem = encoder->ctx.mem; + size_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, base, max_cnt = 0, min_cnt = SIZE_MAX; + 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_header_block_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, 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); +} + +static uint32_t qpack_hash_name(const nghttp3_nv *nv) { + /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ + uint32_t h = 2166136261u; + size_t i; + + for (i = 0; i < nv->namelen; ++i) { + h ^= nv->name[i]; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h; +} + +/* + * 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 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; + } + + 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, + size_t min_cnt) { + size_t avail = 0; + size_t len; + size_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 == SIZE_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, size_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, + size_t absidx, size_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, + size_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, + size_t *pmax_cnt, size_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, size_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 { + hash = qpack_hash_name(nv); + } + + 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 (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, size_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, + size_t max_cnt, size_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); +} + +size_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, + size_t absidx) { + DEBUGF("qpack::encode: Indexed Header Field (static) absidx=%zu\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, + size_t absidx, size_t base) { + DEBUGF("qpack::encode: Indexed Header Field (dynamic) absidx=%zu base=%zu\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, + size_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, size_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 Header Field With Name Reference (static) " + "absidx=%zu 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, size_t absidx, + size_t base, const nghttp3_nv *nv) { + uint8_t fb; + + DEBUGF("qpack::encode: Literal Header Field With Name Reference (dynamic) " + "absidx=%zu base=%zu 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 Header Field 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, size_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%zu\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, size_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%zu\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, + size_t absidx) { + size_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=%zu\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, + size_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, + size_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, + size_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, size_t absidx) { + size_t relidx; + + assert(ctx->next_absidx > absidx); + + relidx = 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, size_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}; + nghttp3_ksl_key key; + + return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, + nghttp3_ksl_key_ptr(&key, &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_key key; + nghttp3_ksl_it it; + + /* This is purely debugging purpose only */ + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, + nghttp3_ksl_key_ptr(&key, &bsk)); + + assert(!nghttp3_ksl_it_end(&it)); + assert(nghttp3_ksl_it_get(&it) == stream); + + nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &key); +} + +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, + size_t max_cnt) { + nghttp3_blocked_streams_key bsk = {max_cnt, 0}; + nghttp3_ksl_key key; + nghttp3_ksl_it it; + + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, + nghttp3_ksl_key_ptr(&key, &bsk)); + + for (; !nghttp3_ksl_it_end(&it);) { + bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it).ptr; + nghttp3_ksl_remove(&encoder->blocked_streams, &it, + nghttp3_ksl_key_ptr(&key, &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=%zu " + "krcnt=%zu\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, + size_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_header_block_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, size_t ricnt, + size_t base) { + size_t max_ents = + encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + size_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; + int sign = base < ricnt; + size_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=%zu base=%zu icnt=%zu\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_HEADER_ACK\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_HEADER_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_HEADER_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 = 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 = nghttp3_min(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 %zu\n", + decoder->rstate.left); + nghttp3_qpack_decoder_set_dtable_cap(decoder, 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.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, + decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&decoder->rstate.name, 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; + 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.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, + decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&decoder->rstate.value, 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=%zu: " + "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=%zu\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); +} + +size_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=%zu base=%zu icnt=%zu\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 (decoder->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, sctx->rstate.left * 2 + 1, + mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&sctx->rstate.name, 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 (decoder->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, sctx->rstate.left * 2 + 1, + mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&sctx->rstate.value, 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_header_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_header_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) { + size_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; + size_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, + size_t *dest, size_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=%zu icnt=%zu\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=%zu base=%zu icnt=%zu\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=%zu base=%zu icnt=%zu\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=%zu\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=%zu 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); +} + +size_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 new file mode 100644 index 00000000000000..b3ccd7a5ee7601 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack.h @@ -0,0 +1,963 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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. */ + size_t absidx; + /* The hash value for header name (nv.name). */ + uint32_t hash; +}; + +/* The entry used for static table. */ +typedef struct { + size_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. */ + size_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. */ + size_t min_cnt; +} nghttp3_qpack_header_block_ref; + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, + size_t max_cnt, size_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); + +size_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. */ + size_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; + size_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 128 + +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_HEADER_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. */ + size_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, + size_t *pmax_cnt, size_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, size_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, size_t krcnt, + int allow_blocking); + +/* + * nghttp3_qpack_encoder_write_header_block_prefix writes Header Block + * 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_header_block_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, size_t ricnt, + size_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, + size_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, + size_t absidx, size_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, size_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, size_t absidx, + size_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, size_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, size_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, + size_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, + size_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); + +size_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, + size_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, + size_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, + size_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, size_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, size_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. */ + size_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. */ + size_t ricnt; + /* base is Base in Header Block Prefix. */ + size_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, + size_t *dest, size_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_header_ack writes Header + * 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_header_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 new file mode 100644 index 00000000000000..c36a68ededd1af --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman.c @@ -0,0 +1,122 @@ +/* + * 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 +#include +#include + +#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 new file mode 100644 index 00000000000000..0cab6ed93e6ccc --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman.h @@ -0,0 +1,108 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..0c104dbc0a0bd8 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c @@ -0,0 +1,4981 @@ +/* + * 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 new file mode 100644 index 00000000000000..0ce71480d72fec --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_range.c @@ -0,0 +1,62 @@ +/* + * 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 new file mode 100644 index 00000000000000..e65dd148354e48 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_range.h @@ -0,0 +1,81 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 new file mode 100644 index 00000000000000..1c31ecebf608d5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_rcbuf.c @@ -0,0 +1,109 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..feea8040005422 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_rcbuf.h @@ -0,0 +1,82 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..6eea9830da17e0 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ringbuf.c @@ -0,0 +1,151 @@ +/* + * 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 +#include +#ifdef WIN32 +# include +#endif + +#include "nghttp3_macro.h" + +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]; +} + +size_t nghttp3_ringbuf_len(nghttp3_ringbuf *rb) { return rb->len; } + +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 new file mode 100644 index 00000000000000..d11bb2b0318aae --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ringbuf.h @@ -0,0 +1,113 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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. */ +size_t nghttp3_ringbuf_len(nghttp3_ringbuf *rb); + +/* 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 new file mode 100644 index 00000000000000..3782aa72cd6e81 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_str.c @@ -0,0 +1,110 @@ +/* + * 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 +#include + +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 new file mode 100644 index 00000000000000..19c1d2c71b559b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_str.h @@ -0,0 +1,40 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..b75d3f7ba0ecc1 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_stream.c @@ -0,0 +1,1342 @@ +/* + * 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 +#include +#include + +#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, uint32_t weight, nghttp3_tnode *parent, + 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, + weight, parent, mem); + + 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->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_HEADER_LIST_SIZE; + iv[0].value = local_settings->max_header_list_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_is_active(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)); +} + +int nghttp3_stream_require_schedule(nghttp3_stream *stream) { + return nghttp3_stream_is_active(stream) || + nghttp3_tnode_has_active_descendant(&stream->node); +} + +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, size_t n) { + nghttp3_ringbuf *outq = &stream->outq; + size_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 = nghttp3_min(offset, 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; +} + +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_stream_schedule(nghttp3_stream *stream) { + int rv; + + rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), + stream->unscheduled_nwrite); + if (rv != 0) { + return rv; + } + + stream->unscheduled_nwrite = 0; + + return 0; +} + +int nghttp3_stream_ensure_scheduled(nghttp3_stream *stream) { + if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { + return 0; + } + + return nghttp3_stream_schedule(stream); +} + +void nghttp3_stream_unschedule(nghttp3_stream *stream) { + nghttp3_tnode_unschedule(stream_get_dependency_node(stream)); +} + +int nghttp3_stream_squash(nghttp3_stream *stream) { + nghttp3_tnode *node = stream_get_dependency_node(stream); + + if (!node->parent) { + return 0; + } + return nghttp3_tnode_squash(stream_get_dependency_node(stream)); +} + +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; +} + +void nghttp3_stream_clear_buffered_data(nghttp3_stream *stream) { + nghttp3_ringbuf *inq = &stream->inq; + nghttp3_buf *buf; + + for (; nghttp3_ringbuf_len(inq);) { + buf = nghttp3_ringbuf_get(inq, 0); + nghttp3_buf_free(buf, stream->mem); + nghttp3_ringbuf_pop_front(inq); + } +} + +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 new file mode 100644 index 00000000000000..116249777c6a78 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_stream.h @@ -0,0 +1,425 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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_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; +}; + +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. */ + size_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, uint32_t weight, nghttp3_tnode *parent, + 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, size_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); + +/* + * nghttp3_stream_schedule schedules |stream|. This function works + * whether |stream| has already been scheduled or not. If it has been + * scheduled already, it is rescheduled by delaying its pending + * penalty. + */ +int nghttp3_stream_schedule(nghttp3_stream *stream); + +/* + * nghttp3_stream_ensure_scheduled schedules |stream| if it has not + * been scheduled. + */ +int nghttp3_stream_ensure_scheduled(nghttp3_stream *stream); + +void nghttp3_stream_unschedule(nghttp3_stream *stream); + +/* + * nghttp3_stream_squash unschedules |stream| and removes it from + * dependency tree. + */ +int nghttp3_stream_squash(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); + +void nghttp3_stream_clear_buffered_data(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 new file mode 100644 index 00000000000000..b6f6b7874360b7 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_tnode.c @@ -0,0 +1,334 @@ +/* + * 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 + +#include "nghttp3_macro.h" +#include "nghttp3_stream.h" +#include "nghttp3_conn.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; +} + +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; +} + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_mem *mem) { + nghttp3_pq_init(&tnode->pq, cycle_less, mem); + + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + tnode->nid = *nid; + tnode->seq = seq; + tnode->cycle = 0; + tnode->pending_penalty = 0; + tnode->weight = weight; + tnode->parent = parent; + tnode->first_child = NULL; + tnode->num_children = 0; + tnode->active = 0; + + if (parent) { + tnode->next_sibling = parent->first_child; + parent->first_child = tnode; + ++parent->num_children; + } else { + tnode->next_sibling = NULL; + } +} + +void nghttp3_tnode_free(nghttp3_tnode *tnode) { nghttp3_pq_free(&tnode->pq); } + +int nghttp3_tnode_is_active(nghttp3_tnode *tnode) { + nghttp3_push_promise *pp; + + switch (tnode->nid.type) { + case NGHTTP3_NODE_ID_TYPE_STREAM: + return nghttp3_stream_is_active( + nghttp3_struct_of(tnode, nghttp3_stream, node)); + case NGHTTP3_NODE_ID_TYPE_PUSH: + pp = nghttp3_struct_of(tnode, nghttp3_push_promise, node); + return pp->stream && nghttp3_stream_is_active(pp->stream); + case NGHTTP3_NODE_ID_TYPE_UT: + /* For unit test */ + return tnode->active; + default: + return 0; + } +} + +static void tnode_unschedule(nghttp3_tnode *tnode) { + nghttp3_tnode *parent; + + for (parent = tnode->parent; parent; tnode = parent, parent = tnode->parent) { + assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&parent->pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + + if (nghttp3_tnode_is_active(parent) || !nghttp3_pq_empty(&parent->pq)) { + return; + } + } +} + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX || + !nghttp3_pq_empty(&tnode->pq)) { + return; + } + + tnode_unschedule(tnode); +} + +void nghttp3_tnode_unschedule_detach(nghttp3_tnode *tnode) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + return; + } + + tnode_unschedule(tnode); +} + +static int tnode_schedule(nghttp3_tnode *tnode, nghttp3_tnode *parent, + uint64_t base_cycle, size_t nwrite) { + uint64_t penalty = + (uint64_t)nwrite * NGHTTP3_MAX_WEIGHT + tnode->pending_penalty; + + tnode->cycle = base_cycle + penalty / tnode->weight; + tnode->pending_penalty = (uint32_t)(penalty % tnode->weight); + + return nghttp3_pq_push(&parent->pq, &tnode->pe); +} + +static uint64_t tnode_get_first_cycle(nghttp3_tnode *tnode) { + nghttp3_tnode *top; + + if (nghttp3_pq_empty(&tnode->pq)) { + return 0; + } + + top = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + return top->cycle; +} + +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, size_t nwrite) { + nghttp3_tnode *parent; + uint64_t cycle; + int rv; + + for (parent = tnode->parent; parent; tnode = parent, parent = tnode->parent) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + cycle = tnode_get_first_cycle(parent); + } else if (nwrite != 0) { + cycle = tnode->cycle; + nghttp3_pq_remove(&parent->pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + } else { + return 0; + } + + rv = tnode_schedule(tnode, parent, cycle, nwrite); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) { + return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX; +} + +nghttp3_tnode *nghttp3_tnode_get_next(nghttp3_tnode *tnode) { + if (nghttp3_pq_empty(&tnode->pq)) { + return NULL; + } + + tnode = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + + for (;;) { + if (nghttp3_tnode_is_active(tnode)) { + return tnode; + } + assert(!nghttp3_pq_empty(&tnode->pq)); + tnode = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + assert(tnode); + } +} + +void nghttp3_tnode_insert(nghttp3_tnode *tnode, nghttp3_tnode *parent) { + assert(tnode->parent == NULL); + assert(tnode->next_sibling == NULL); + assert(tnode->pe.index == NGHTTP3_PQ_BAD_INDEX); + + tnode->next_sibling = parent->first_child; + parent->first_child = tnode; + tnode->parent = parent; + ++parent->num_children; +} + +int nghttp3_tnode_insert_exclusive(nghttp3_tnode *tnode, + nghttp3_tnode *parent) { + nghttp3_tnode **p, *node; + int rv; + + for (node = parent->first_child; node; node = node->next_sibling) { + node->parent = tnode; + + if (node->pe.index == NGHTTP3_PQ_BAD_INDEX) { + continue; + } + + nghttp3_pq_remove(&parent->pq, &node->pe); + node->pe.index = NGHTTP3_PQ_BAD_INDEX; + } + + for (p = &tnode->first_child; *p; p = &(*p)->next_sibling) + ; + + *p = parent->first_child; + parent->first_child = NULL; + tnode->num_children += parent->num_children; + parent->num_children = 0; + + nghttp3_tnode_insert(tnode, parent); + + for (node = *p; node; node = node->next_sibling) { + if (nghttp3_tnode_is_active(node) || + nghttp3_tnode_has_active_descendant(node)) { + rv = nghttp3_tnode_schedule(node, 0); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +void nghttp3_tnode_remove(nghttp3_tnode *tnode) { + nghttp3_tnode *parent = tnode->parent, **p; + + assert(parent); + + if (tnode->pe.index != NGHTTP3_PQ_BAD_INDEX) { + nghttp3_tnode_unschedule_detach(tnode); + } + + for (p = &parent->first_child; *p != tnode; p = &(*p)->next_sibling) + ; + + *p = tnode->next_sibling; + --parent->num_children; + tnode->parent = tnode->next_sibling = NULL; +} + +int nghttp3_tnode_squash(nghttp3_tnode *tnode) { + nghttp3_tnode *parent = tnode->parent, *node, **p, *first, *end; + int rv; + + assert(parent); + + for (p = &parent->first_child; *p != tnode; p = &(*p)->next_sibling) + ; + + *p = tnode->first_child; + + for (; *p; p = &(*p)->next_sibling) { + node = *p; + node->parent = parent; + node->weight = + (uint32_t)(node->weight * tnode->weight / tnode->num_children); + if (node->weight == 0) { + node->weight = 1; + } + + if (node->pe.index == NGHTTP3_PQ_BAD_INDEX) { + continue; + } + + nghttp3_pq_remove(&tnode->pq, &node->pe); + node->pe.index = NGHTTP3_PQ_BAD_INDEX; + } + + *p = tnode->next_sibling; + + first = tnode->first_child; + end = tnode->next_sibling; + + if (tnode->pe.index != NGHTTP3_PQ_BAD_INDEX) { + nghttp3_tnode_unschedule(tnode); + assert(tnode->pe.index == NGHTTP3_PQ_BAD_INDEX); + } + + parent->num_children += tnode->num_children; + tnode->num_children = 0; + tnode->parent = tnode->next_sibling = tnode->first_child = NULL; + + for (node = first; node && node != end; node = node->next_sibling) { + if (nghttp3_tnode_is_active(node) || + nghttp3_tnode_has_active_descendant(node)) { + rv = nghttp3_tnode_schedule(node, 0); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +nghttp3_tnode *nghttp3_tnode_find_ascendant(nghttp3_tnode *tnode, + const nghttp3_node_id *nid) { + for (tnode = tnode->parent; tnode && !nghttp3_node_id_eq(nid, &tnode->nid); + tnode = tnode->parent) + ; + + return tnode; +} + +int nghttp3_tnode_has_active_descendant(nghttp3_tnode *tnode) { + return !nghttp3_pq_empty(&tnode->pq); +} diff --git a/deps/nghttp3/lib/nghttp3_tnode.h b/deps/nghttp3/lib/nghttp3_tnode.h new file mode 100644 index 00000000000000..8e1e5d5bdf98c8 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_tnode.h @@ -0,0 +1,155 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_pq.h" + +#define NGHTTP3_DEFAULT_WEIGHT 16 +#define NGHTTP3_MAX_WEIGHT 256 +#define NGHTTP3_TNODE_MAX_CYCLE_GAP ((1llu << 24) * 256 + 255) + +typedef enum { + NGHTTP3_NODE_ID_TYPE_STREAM = 0x00, + NGHTTP3_NODE_ID_TYPE_PUSH = 0x01, + NGHTTP3_NODE_ID_TYPE_PLACEHOLDER = 0x02, + NGHTTP3_NODE_ID_TYPE_ROOT = 0x03, + /* NGHTTP3_NODE_ID_TYPE_UT is defined for unit test */ + NGHTTP3_NODE_ID_TYPE_UT = 0xff, +} 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; + nghttp3_pq pq; + nghttp3_tnode *parent; + nghttp3_tnode *first_child; + nghttp3_tnode *next_sibling; + size_t num_children; + nghttp3_node_id nid; + uint64_t seq; + uint64_t cycle; + uint32_t pending_penalty; + uint32_t weight; + /* active is defined for unit test and is nonzero if this node is + active. */ + int active; +}; + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_mem *mem); + +void nghttp3_tnode_free(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_is_active returns nonzero if |tnode| is active. Only + * NGHTTP3_NODE_ID_TYPE_STREAM and NGHTTP3_NODE_ID_TYPE_PUSH (and + * NGHTTP3_NODE_ID_TYPE_UT for unit test) can become active. + */ +int nghttp3_tnode_is_active(nghttp3_tnode *tnode); + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_unschedule_detach works like + * nghttp3_tnode_unschedule, but it removes |tnode| even if tnode->pq + * is not empty. + */ +void nghttp3_tnode_unschedule_detach(nghttp3_tnode *tnode); + +/* + * 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, size_t nwrite); + +/* + * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. + */ +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_get_next returns node which has highest priority. + * This function returns NULL if there is no node. + */ +nghttp3_tnode *nghttp3_tnode_get_next(nghttp3_tnode *node); + +/* + * nghttp3_tnode_insert inserts |tnode| as a first child of |parent|. + * |tnode| might have its descendants. + */ +void nghttp3_tnode_insert(nghttp3_tnode *tnode, nghttp3_tnode *parent); + +/* + * nghttp3_tnode_insert_exclusive inserts |tnode| to |parent| as a + * distinct child. The existing direct children of |parent| become + * the children of |tnode|. + */ +int nghttp3_tnode_insert_exclusive(nghttp3_tnode *tnode, nghttp3_tnode *parent); + +/* + * nghttp3_tnode_remove removes |tnode| along with its subtree from + * its parent. + */ +void nghttp3_tnode_remove(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_squash removes |tnode| from its parent. The weight + * of |tnode| is distributed to the direct descendants of |tnode|. + * They are inserted to the former parent of |tnode|. + */ +int nghttp3_tnode_squash(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_find_ascendant returns an ascendant of |tnode| whose + * node ID is |nid|. If no such node exists, this function returns + * NULL. + */ +nghttp3_tnode *nghttp3_tnode_find_ascendant(nghttp3_tnode *tnode, + const nghttp3_node_id *nid); + +int nghttp3_tnode_has_active_descendant(nghttp3_tnode *tnode); + +#endif /* NGHTTP3_TNODE_H */ diff --git a/deps/nghttp3/lib/nghttp3_vec.c b/deps/nghttp3/lib/nghttp3_vec.c new file mode 100644 index 00000000000000..8d530a060dd502 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_vec.c @@ -0,0 +1,64 @@ +/* + * 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 new file mode 100644 index 00000000000000..c1a928e3e1d1ab --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_vec.h @@ -0,0 +1,35 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP3_VEC_H */ diff --git a/deps/nghttp3/lib/nghttp3_version.c b/deps/nghttp3/lib/nghttp3_version.c new file mode 100644 index 00000000000000..dfad4793c4bc11 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_version.c @@ -0,0 +1,39 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..3421ba7cf60b15 --- /dev/null +++ b/deps/nghttp3/nghttp3.gyp @@ -0,0 +1,62 @@ +{ + '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' + }, + }, + }], + ], + '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/COPYING b/deps/ngtcp2/COPYING new file mode 100644 index 00000000000000..9b367cdce71384 --- /dev/null +++ b/deps/ngtcp2/COPYING @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000000000..d405e0d1336986 --- /dev/null +++ b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -0,0 +1,561 @@ +/* + * 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 + +#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` dervies 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 uint8_t *plaintext, + size_t plaintextlen, const uint8_t *key, + 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 uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, 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 log. |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 uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, 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 uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, 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 uint8_t *key, + 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 uint8_t *key, + const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_key` derives the rx and tx keys + * from |rx_secret| and |tx_secret| respectively and installs new keys + * to |conn|. + * + * 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|. + * + * |level| specifies the encryption level. If |level| is + * NGTCP2_CRYPTO_LEVEL_EARLY, and if |side| is + * NGTCP2_CRYPTO_SIDE_CLIENT, |rx_secret| must be NULL. If |level| is + * NGTCP2_CRYPTO_LEVEL_EARLY, and if |side| is + * NGTCP2_CRYPTO_SIDE_SERVER, |tx_secret| must be NULL. Otherwise, + * |rx_secret| and |tx_secret| must not be NULL. + * + * |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_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_key( + ngtcp2_conn *conn, void *tls, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen, ngtcp2_crypto_side side); + +/** + * @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. + */ +NGTCP2_EXTERN 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, ngtcp2_crypto_side side); + +/** + * @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|. + * + * 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|. + * + * |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, uint8_t *rx_key, uint8_t *rx_iv, + 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, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, 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_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. + * + * |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 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, void *tls, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen); + +/** + * @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. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls, + ngtcp2_crypto_side side); + +/** + * @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); + +#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 new file mode 100644 index 00000000000000..7ccb383e3c8069 --- /dev/null +++ b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h @@ -0,0 +1,34 @@ +/* + * 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 + +/* 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 new file mode 100644 index 00000000000000..5e6a8493074a8e --- /dev/null +++ b/deps/ngtcp2/crypto/openssl/openssl.c @@ -0,0 +1,395 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include + +#include +#include +#include + +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(); + 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 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); + 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_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 uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, 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; + int rv = 0; + int len; + + 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)taglen, NULL)) || + !EVP_EncryptInit_ex(actx, NULL, NULL, key, 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)) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, 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; + int rv = 0; + int len; + const uint8_t *tag; + + if (taglen > ciphertextlen) { + return -1; + } + + ciphertextlen -= taglen; + tag = ciphertext + ciphertextlen; + + 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)taglen, + (uint8_t *)tag)) || + !EVP_DecryptInit_ex(actx, NULL, NULL, key, nonce) || + (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_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, + (uint8_t *)tag) || + !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len)))) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key, const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + const EVP_CIPHER *cipher = hp->native_handle; + EVP_CIPHER_CTX *actx; + int rv = 0; + int len; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, hp_key, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +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, void *tls, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + SSL *ssl = tls; + 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, + ngtcp2_crypto_side side) { + SSL *ssl = tls; + ngtcp2_transport_params_type exttype = + side == NGTCP2_CRYPTO_SIDE_CLIENT + ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS + : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; + 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; +} diff --git a/deps/ngtcp2/crypto/shared.c b/deps/ngtcp2/crypto/shared.c new file mode 100644 index 00000000000000..6304e59639de47 --- /dev/null +++ b/deps/ngtcp2/crypto/shared.c @@ -0,0 +1,488 @@ +/* + * 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 +#include + +#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_key( + ngtcp2_conn *conn, void *tls, 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, + ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen, ngtcp2_crypto_side side) { + const ngtcp2_crypto_ctx *ctx; + const ngtcp2_crypto_aead *aead; + const ngtcp2_crypto_md *md; + uint8_t rx_keybuf[64], rx_ivbuf[64], rx_hp_keybuf[64]; + uint8_t tx_keybuf[64], tx_ivbuf[64], tx_hp_keybuf[64]; + size_t keylen; + size_t ivlen; + int rv; + + 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; + } + + 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; + keylen = ngtcp2_crypto_aead_keylen(aead); + ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if ((level != NGTCP2_CRYPTO_LEVEL_EARLY || + side == NGTCP2_CRYPTO_SIDE_SERVER) && + ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, aead, md, rx_secret, secretlen) != 0) { + return -1; + } + + if ((level != NGTCP2_CRYPTO_LEVEL_EARLY || + side == NGTCP2_CRYPTO_SIDE_CLIENT) && + ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, aead, md, tx_secret, secretlen) != 0) { + return -1; + } + + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: + if (side == NGTCP2_CRYPTO_SIDE_CLIENT) { + rv = ngtcp2_conn_install_early_key(conn, tx_key, tx_iv, tx_hp_key, keylen, + ivlen); + } else { + rv = ngtcp2_conn_install_early_key(conn, rx_key, rx_iv, rx_hp_key, keylen, + ivlen); + } + if (rv != 0) { + return -1; + } + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + rv = ngtcp2_conn_install_handshake_key(conn, rx_key, rx_iv, rx_hp_key, + tx_key, tx_iv, tx_hp_key, keylen, + ivlen); + if (rv != 0) { + return -1; + } + break; + case NGTCP2_CRYPTO_LEVEL_APP: + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls, side); + if (rv != 0) { + return -1; + } + + rv = ngtcp2_conn_install_key(conn, rx_secret, tx_secret, rx_key, rx_iv, + rx_hp_key, tx_key, tx_iv, tx_hp_key, secretlen, + keylen, ivlen); + if (rv != 0) { + return -1; + } + + break; + default: + return -1; + } + + return 0; +} + +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, ngtcp2_crypto_side side) { + 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; + int rv; + + 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, side) != 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; + } + + rv = ngtcp2_conn_install_initial_key( + conn, rx_key, rx_iv, rx_hp_key, tx_key, tx_iv, tx_hp_key, + NGTCP2_CRYPTO_INITIAL_KEYLEN, NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + return -1; + } + + ngtcp2_conn_set_retry_aead(conn, ngtcp2_crypto_aead_retry(&retry_aead)); + + return 0; +} + +int ngtcp2_crypto_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, + uint8_t *tx_secret, uint8_t *rx_key, + uint8_t *rx_iv, 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; + + 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; + } + + return 0; +} + +int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_encrypt(dest, aead, plaintext, plaintextlen, key, 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 uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_decrypt(dest, aead, ciphertext, ciphertextlen, key, 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 uint8_t *hp_key, const uint8_t *sample) { + if (ngtcp2_crypto_hp_mask(dest, hp, hp_key, 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, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, + size_t secretlen, void *user_data) { + (void)conn; + (void)user_data; + + if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_key, rx_iv, + 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_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; + } + + spktlen = ngtcp2_pkt_write_connection_close( + dest, destlen, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, + &ctx.aead, tx_key, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, tx_hp_key); + if (spktlen < 0) { + return -1; + } + + 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_retry(&aead); + + spktlen = ngtcp2_pkt_write_retry(dest, destlen, dcid, scid, odcid, token, + tokenlen, ngtcp2_crypto_encrypt_cb, &aead); + if (spktlen < 0) { + return -1; + } + + return spktlen; +} diff --git a/deps/ngtcp2/crypto/shared.h b/deps/ngtcp2/crypto/shared.h new file mode 100644 index 00000000000000..87f3d8928f0a2e --- /dev/null +++ b/deps/ngtcp2/crypto/shared.h @@ -0,0 +1,72 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * @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); + +#endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/lib/includes/config.h b/deps/ngtcp2/lib/includes/config.h new file mode 100644 index 00000000000000..0aee7749bae78e --- /dev/null +++ b/deps/ngtcp2/lib/includes/config.h @@ -0,0 +1,39 @@ + +/* Edited to match src/node.h. */ +#include + +#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 // size_t, ssize_t +#endif // _WIN32 + +#ifdef _MSC_VER +# include +# define __builtin_popcount __popcnt +#endif + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h new file mode 100644 index 00000000000000..a2f7d67a5ce2bd --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -0,0 +1,2889 @@ +/* + * 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 +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include +#include + +#include + +#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 0xff00001bu +/* NGTCP2_PROTO_VER_MAX is the highest QUIC version the library + supports. */ +#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER + +/* NGTCP2_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 NGTCP2_ALPN_H3 "\x5h3-27" + +#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_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 \ + "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7\xd2\x43\x2b\xb4\x63\x65\xbe\xf9" \ + "\xf5\x02" + +/* 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) + +#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_STREAM_MORE = -240, + NGTCP2_ERR_RETRY = -241, + 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_SERVER_BUSY 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_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 NGTCP2_DURATION_TICK resolution. + */ +typedef uint64_t ngtcp2_tstamp; + +/* + * ngtcp2_duration is a period of time in NGTCP2_DURATION_TICK + * 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 + +/** + * @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; + uint8_t *token; + size_t tokenlen; + /** + * 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; + +/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ +#define NGTCP2_RETRY_TAGLEN 16 + +typedef struct ngtcp2_pkt_retry { + ngtcp2_cid odcid; + const uint8_t *token; + size_t tokenlen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; +} ngtcp2_pkt_retry; + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_transport_param_id : uint16_t { +#else +typedef enum ngtcp2_transport_param_id { +#endif + NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID = 0x0000, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, + NGTCP2_TRANSPORT_PARAM_MAX_PACKET_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_ID_MAX = UINT16_MAX +} 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; + +#define NGTCP2_MAX_PKT_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_connection_id is the client initial connection ID. + Server must specify this field and set + original_connection_id_present to nonzero if it sent Retry + packet. */ + ngtcp2_cid original_connection_id; + /* 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_packet_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 original_connection_id_present; + uint8_t preferred_address_present; + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_transport_params; + +/* 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, ...); + +typedef void (*ngtcp2_qlog_write)(void *user_data, 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. */ + 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; + /* initial_ts is an initial timestamp given to the library. */ + ngtcp2_tstamp initial_ts; + /* log_printf is a function that the library uses to write logs. + NULL means no logging output. */ + ngtcp2_printf log_printf; + /* token is a token received in Client Initial packet and + successfully validated. Only server application may specify this + field. Server then verifies that all Client Initial packets have + this token. `ngtcp2_conn_server_new` makes a copy of token. */ + ngtcp2_vec token; +} ngtcp2_settings; + +/** + * @struct + * + * ngtcp2_rcvry_stat holds various statistics, and computed data for + * recovery from packet loss. + * + * Everything is NGTCP2_DURATION_TICK resolution. + */ +typedef struct ngtcp2_rcvry_stat { + ngtcp2_duration latest_rtt; + ngtcp2_duration min_rtt; + ngtcp2_duration smoothed_rtt; + ngtcp2_duration rttvar; + 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. It is indexed by + ngtcp2_crypto_level. No last_tx_pkt_ts for 0RTT packet. */ + ngtcp2_tstamp last_tx_pkt_ts[3]; +} ngtcp2_rcvry_stat; + +typedef struct ngtcp2_cc_stat { + uint64_t cwnd; + uint64_t ssthresh; + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t bytes_in_flight; +} ngtcp2_cc_stat; + +/** + * @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 is + opaque to the ngtcp2 library. */ + uint8_t *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 { + uint8_t local_addrbuf[128]; + uint8_t remote_addrbuf[128]; + 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; + +/** + * @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; +} 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, + const ngtcp2_pkt_retry *retry, + 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 + * encryption key is passed as |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 uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, 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 + * decryption key is passed as |key| of length |keylen|. 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 uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, 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 key is passed as |hp_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 uint8_t *hp_key, const uint8_t *sample); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stream_data` is invoked when stream data is + * received. The stream is specified by |stream_id|. If |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, int64_t stream_id, + int fin, 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, size_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, size_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)(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + ngtcp2_rand_ctx ctx, void *user_data); + +/** + * @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. 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, key and IV + * to the buffer pointed by |rx_secret|, |rx_key| and |rx_iv| + * respectively. Similarly, write encryption secret, key and IV to + * the buffer pointed by |tx_secret|, |tx_key| and |tx_iv|. 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, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, + 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); + +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; +} 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 uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key); + +/** + * @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. + * + * 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); + +/** + * @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. + */ +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_key| of length |keylen|, IV + * |rx_iv| of length |rx_ivlen|, and packet header protection key + * |rx_hp_key| of length |keylen| to decrypt incoming Initial packets. + * Similarly, |tx_key|, |tx_iv| and |tx_hp_key| are for encrypt + * outgoing packets and are the same length with the rx counterpart . + * If they have already been set, they are overwritten. + * + * 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 uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_handshake_key` installs packet protection + * keying materials for Handshake packets. |rx_key| of length + * |keylen|, IV |rx_iv| of length |rx_ivlen|, and packet header + * protection key |rx_hp_key| of length |keylen| to decrypt incoming + * Handshake packets. Similarly, |tx_key|, |tx_iv| and |tx_hp_key| + * are for encrypt outgoing packets and are the same length with the + * rx counterpart. + * + * 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_handshake_key( + ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen); + +/** + * @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 key + * |key| of length |keylen|, IV |iv| of length |ivlen|, and packet + * header protection key |hp_key| of length |keylen| to encrypt (for + * client)or decrypt (for server) 0RTT packets. + * + * 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 uint8_t *key, + const uint8_t *iv, + const uint8_t *hp_key, + size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_key` installs packet protection keying + * materials for Short packets. |rx_secret| of length |secretlen| is + * the decryption secret. |rx_key| of length |keylen|, IV |rx_iv| of + * length |rx_ivlen|, and packet header protection key |rx_hp_key| of + * length |keylen| to decrypt incoming Short packets. Similarly, + * |tx_secret| of length |secretlen| is the encryption secret. + * |tx_key|, |tx_iv| and |tx_hp_key| are used to encrypt outgoing + * packets and are the same length with the rx counterpart. + * + * 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_key( + ngtcp2_conn *conn, const uint8_t *rx_secret, const uint8_t *tx_secret, + const uint8_t *rx_key, const uint8_t *rx_iv, const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, const uint8_t *tx_hp_key, + size_t secretlen, size_t keylen, size_t ivlen); + +/** + * @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 do 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_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; + +/** + * @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, int fin, + 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 + * |fin| is nonzero, fin flag is set in 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 |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. + * + * - The function returns :enum:`NGTCP2_ERR_WRITE_STREAM_MORE`. 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_STREAM_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). + * + * 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_STREAM_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, int fin, + 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 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 APPLICATION_CLOSE frame 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 APPLICATION_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, + size_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, + size_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_bytes_in_flight` returns the number of bytes which + * is the sum of outgoing QUIC packet length in flight. This does not + * include a packet which only includes ACK frames. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_bytes_in_flight(ngtcp2_conn *conn); + +/** + * @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_rcvry_stat` returns a pointer to the object which + * stores recovery information. + */ +NGTCP2_EXTERN const ngtcp2_rcvry_stat * +ngtcp2_conn_get_rcvry_stat(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_cc_stat` returns a pointer to the object which + * stores congestion controller information. + */ +NGTCP2_EXTERN const ngtcp2_cc_stat *ngtcp2_conn_get_cc_stat(ngtcp2_conn *conn); + +/** + * @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_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_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_set_retry_aead` sets |aead| for Retry integrity tag + * verification. It must be AEAD_AES_128_GCM. 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. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead); + +/** + * @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_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 void *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 void *local_addr, + size_t local_addrlen, void *local_user_data, + const void *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: + * + * * max_packet_size = NGTCP2_MAX_PKT_SIZE + * * ack_delay_component = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT + * * max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY + */ +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); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/lib/includes/ngtcp2/version.h new file mode 100644 index 00000000000000..85850725bfa9e2 --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/version.h @@ -0,0 +1,45 @@ +/* + * 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.90" + +/** + * @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 0x00015a + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h.in b/deps/ngtcp2/lib/includes/ngtcp2/version.h.in new file mode 100644 index 00000000000000..fc7459636dd140 --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/version.h.in @@ -0,0 +1,45 @@ +/* + * 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 "@PACKAGE_VERSION@" + +/** + * @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 @PACKAGE_VERSION_NUM@ + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/lib/ngtcp2_acktr.c new file mode 100644 index 00000000000000..e27d05c787d659 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_acktr.c @@ -0,0 +1,331 @@ +/* + * 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 + +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 *lhs->i > *rhs->i; +} + +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; + ngtcp2_ksl_key key, old_key; + + if (ngtcp2_ksl_len(&acktr->ents)) { + it = ngtcp2_ksl_lower_bound(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), + ngtcp2_ksl_key_ptr(&old_key, &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, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + ngtcp2_acktr_entry_del(ent, acktr->mem); + added = 1; + } else { + ngtcp2_ksl_update_key(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), + ngtcp2_ksl_key_ptr(&old_key, &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, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &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; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_lower_bound(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + assert(*ngtcp2_ksl_it_key(&it).i == (int64_t)ent->pkt_num); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&acktr->ents, &it, + ngtcp2_ksl_key_ptr(&key, &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); +} + +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_key key; + + ngtcp2_ksl_remove(&acktr->ents, it, ngtcp2_ksl_key_ptr(&key, &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; + ngtcp2_ksl_key key; + + 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, + ngtcp2_ksl_key_ptr(&key, &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; + } + } + + 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 new file mode 100644 index 00000000000000..0efd2156ac9117 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_acktr.h @@ -0,0 +1,215 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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_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 new file mode 100644 index 00000000000000..13a787bdea0302 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_addr.c @@ -0,0 +1,57 @@ +/* + * 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 + +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const void *addr, + size_t addrlen, void *user_data) { + dest->addrlen = addrlen; + dest->addr = (uint8_t *)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 void *addr, + size_t addrlen) { + dest->addrlen = addrlen; + if (addrlen) { + memcpy(dest->addr, addr, addrlen); + } +} + +int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { + return a->addrlen == b->addrlen && memcmp(a->addr, b->addr, a->addrlen) == 0; +} + +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 new file mode 100644 index 00000000000000..db8b7144082a14 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_addr.h @@ -0,0 +1,60 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 void *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 new file mode 100644 index 00000000000000..373f23d91aed7c --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_buf.c @@ -0,0 +1,44 @@ +/* + * 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 new file mode 100644 index 00000000000000..fe3d06a1c9733d --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_buf.h @@ -0,0 +1,79 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..02864bffa0ec41 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cc.c @@ -0,0 +1,137 @@ +/* + * 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 "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_rst.h" + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_tstamp ts_sent) { + pkt->pkt_num = pkt_num; + pkt->pktlen = pktlen; + pkt->ts_sent = ts_sent; + + return pkt; +} + +void ngtcp2_default_cc_init(ngtcp2_default_cc *cc, ngtcp2_cc_stat *ccs, + ngtcp2_rst *rst, ngtcp2_log *log) { + cc->log = log; + cc->ccs = ccs; + cc->rst = rst; + cc->max_delivery_rate = 0.; + cc->min_rtt = 0; + cc->min_rtt_ts = 0; + cc->target_cwnd = 0; +} + +void ngtcp2_default_cc_free(ngtcp2_default_cc *cc) { (void)cc; } + +static int default_cc_in_congestion_recovery(ngtcp2_default_cc *cc, + ngtcp2_tstamp sent_time) { + return sent_time <= cc->ccs->congestion_recovery_start_ts; +} + +void ngtcp2_default_cc_on_pkt_acked(ngtcp2_default_cc *cc, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_cc_stat *ccs = cc->ccs; + + if (default_cc_in_congestion_recovery(cc, pkt->ts_sent)) { + return; + } + + if (cc->target_cwnd && ccs->cwnd >= cc->target_cwnd) { + return; + } + + if (ccs->cwnd < ccs->ssthresh) { + ccs->cwnd += pkt->pktlen; + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " acked, slow start cwnd=%lu", pkt->pkt_num, + ccs->cwnd); + return; + } + + ccs->cwnd += NGTCP2_MAX_DGRAM_SIZE * pkt->pktlen / ccs->cwnd; +} + +void ngtcp2_default_cc_congestion_event(ngtcp2_default_cc *cc, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts) { + ngtcp2_cc_stat *ccs = cc->ccs; + + if (default_cc_in_congestion_recovery(cc, ts_sent)) { + return; + } + ccs->congestion_recovery_start_ts = ts; + ccs->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS; + ccs->cwnd = ngtcp2_max(ccs->cwnd, NGTCP2_MIN_CWND); + ccs->ssthresh = ccs->cwnd; + + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "reduce cwnd because of packet loss cwnd=%lu", ccs->cwnd); +} + +void ngtcp2_default_cc_handle_persistent_congestion(ngtcp2_default_cc *cc, + ngtcp2_duration loss_window, + ngtcp2_duration pto) { + ngtcp2_cc_stat *ccs = cc->ccs; + ngtcp2_duration congestion_period = + pto * NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + + if (loss_window >= congestion_period) { + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "persistent congestion loss_window=%" PRIu64 + " congestion_period=%" PRIu64, + loss_window, congestion_period); + + ccs->cwnd = NGTCP2_MIN_CWND; + } +} + +void ngtcp2_default_cc_on_ack_recv(ngtcp2_default_cc *cc, + ngtcp2_duration latest_rtt, + ngtcp2_tstamp ts) { + /* TODO Use sliding window */ + if (latest_rtt && (cc->min_rtt == 0 || cc->min_rtt > latest_rtt)) { + cc->min_rtt = latest_rtt; + cc->min_rtt_ts = ts; + } + + /* TODO Use sliding window */ + cc->max_delivery_rate = + ngtcp2_max(cc->max_delivery_rate, cc->rst->rs.delivery_rate); + + if (cc->min_rtt && cc->max_delivery_rate > 1e-9) { + uint64_t target_cwnd = + (uint64_t)(2.89 * cc->max_delivery_rate * (double)cc->min_rtt); + cc->target_cwnd = ngtcp2_max(NGTCP2_MIN_CWND, target_cwnd); + + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "target_cwnd=%lu max_delivery_rate=%.02f min_rtt=%lu", + cc->target_cwnd, cc->max_delivery_rate * 1000000000, + cc->min_rtt); + } +} diff --git a/deps/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/lib/ngtcp2_cc.h new file mode 100644 index 00000000000000..33f2815c78f688 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cc.h @@ -0,0 +1,92 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#define NGTCP2_MAX_DGRAM_SIZE 1200 +#define NGTCP2_MIN_CWND (2 * NGTCP2_MAX_DGRAM_SIZE) +#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1 +#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3 + +struct ngtcp2_log; +typedef struct ngtcp2_log ngtcp2_log; + +struct ngtcp2_rst; +typedef struct ngtcp2_rst ngtcp2_rst; + +/* 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; + /* ts_sent is the timestamp when packet is sent. */ + ngtcp2_tstamp ts_sent; +} ngtcp2_cc_pkt; + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_tstamp ts_sent); + +/* ngtcp2_default_cc is the default congestion controller. */ +struct ngtcp2_default_cc { + ngtcp2_log *log; + ngtcp2_cc_stat *ccs; + ngtcp2_rst *rst; + double max_delivery_rate; + ngtcp2_duration min_rtt; + ngtcp2_tstamp min_rtt_ts; + uint64_t target_cwnd; +}; + +typedef struct ngtcp2_default_cc ngtcp2_default_cc; + +void ngtcp2_default_cc_init(ngtcp2_default_cc *cc, ngtcp2_cc_stat *ccs, + ngtcp2_rst *rst, ngtcp2_log *log); + +void ngtcp2_default_cc_free(ngtcp2_default_cc *cc); + +void ngtcp2_default_cc_on_pkt_acked(ngtcp2_default_cc *cc, + const ngtcp2_cc_pkt *pkt); + +void ngtcp2_default_cc_congestion_event(ngtcp2_default_cc *cc, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts); + +void ngtcp2_default_cc_handle_persistent_congestion(ngtcp2_default_cc *cc, + ngtcp2_duration loss_window, + ngtcp2_duration pto); + +void ngtcp2_default_cc_on_ack_recv(ngtcp2_default_cc *cc, + ngtcp2_duration latest_rtt, + ngtcp2_tstamp ts); + +#endif /* NGTCP2_CC_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/lib/ngtcp2_cid.c new file mode 100644 index 00000000000000..b0670a9c6f0579 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cid.c @@ -0,0 +1,118 @@ +/* + * 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 +#include + +#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 new file mode 100644 index 00000000000000..fe5576ae468efa --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cid.h @@ -0,0 +1,137 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..eca7f04ca33a23 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conn.c @@ -0,0 +1,9502 @@ +/* + * 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 +#include +#include + +#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, + int fin, 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, strm->stream_id, fin, 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 int crypto_offset_less(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + return *lhs->i < *rhs->i; +} + +static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_crypto_level crypto_level, + ngtcp2_rst *rst, ngtcp2_default_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, crypto_level, &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_crypto_level crypto_level, + ngtcp2_rst *rst, ngtcp2_default_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, crypto_level, 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_vec_del(pktns->crypto.rx.hp_key, mem); + ngtcp2_vec_del(pktns->crypto.tx.hp_key, 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 int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return ngtcp2_cid_less(lhs->ptr, rhs->ptr); +} + +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; +} + +static void rcvry_stat_reset(ngtcp2_rcvry_stat *rcs) { + memset(rcs, 0, sizeof(*rcs)); + rcs->min_rtt = UINT64_MAX; + // Initializes them with UINT64_MAX. + memset(rcs->last_tx_pkt_ts, 0xff, sizeof(rcs->last_tx_pkt_ts)); +} + +static void cc_stat_reset(ngtcp2_cc_stat *ccs) { + memset(ccs, 0, sizeof(*ccs)); + ccs->cwnd = ngtcp2_min(10 * NGTCP2_MAX_DGRAM_SIZE, + ngtcp2_max(2 * NGTCP2_MAX_DGRAM_SIZE, 14720)); + ccs->ssthresh = UINT64_MAX; +} + +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; + ngtcp2_ksl_key key; + 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_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); + } + + ngtcp2_rst_init(&(*pconn)->rst); + + ngtcp2_default_cc_init(&(*pconn)->cc, &(*pconn)->ccs, &(*pconn)->rst, + &(*pconn)->log); + + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_CRYPTO_LEVEL_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_CRYPTO_LEVEL_HANDSHAKE, + &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + mem); + if (rv != 0) { + goto fail_hs_pktns_init; + } + + rv = pktns_init(&(*pconn)->pktns, NGTCP2_CRYPTO_LEVEL_APP, &(*pconn)->rst, + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + if (rv != 0) { + goto fail_pktns_init; + } + + (*pconn)->local.settings = *settings; + + if (server && 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 (params->active_connection_id_limit == 0) { + (*pconn)->local.settings.transport_params.active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + } + + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_scident; + } + + ngtcp2_scid_init(scident, 0, scid, + params->stateless_reset_token_present + ? params->stateless_reset_token + : NULL); + + rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); + if (rv != 0) { + goto fail_scid_set_insert; + } + + scident = NULL; + + if (server && params->preferred_address_present) { + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_scident; + } + + ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, + params->preferred_address.stateless_reset_token); + + rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); + if (rv != 0) { + goto fail_scid_set_insert; + } + + scident = NULL; + + (*pconn)->scid.last_seq = 1; + } + + ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); + ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); + + (*pconn)->oscid = *scid; + (*pconn)->callbacks = *callbacks; + (*pconn)->version = version; + (*pconn)->mem = mem; + (*pconn)->user_data = user_data; + (*pconn)->rx.unsent_max_offset = (*pconn)->rx.max_offset = + params->initial_max_data; + (*pconn)->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi; + (*pconn)->remote.bidi.max_streams = params->initial_max_streams_bidi; + (*pconn)->remote.uni.unsent_max_streams = params->initial_max_streams_uni; + (*pconn)->remote.uni.max_streams = params->initial_max_streams_uni; + (*pconn)->idle_ts = settings->initial_ts; + (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; + + rcvry_stat_reset(&(*pconn)->rcs); + cc_stat_reset(&(*pconn)->ccs); + + ngtcp2_qlog_start(&(*pconn)->qlog, &settings->qlog.odcid, server); + ngtcp2_qlog_parameters_set_transport_params( + &(*pconn)->qlog, &(*pconn)->local.settings.transport_params, + /* local = */ 1); + + return 0; + +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: + ngtcp2_default_cc_free(&(*pconn)->cc); + 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_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; + + 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; + ngtcp2_transport_params *params; + + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, + user_data, 1); + if (rv != 0) { + return rv; + } + (*pconn)->server = 1; + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; + (*pconn)->local.bidi.next_stream_id = 1; + (*pconn)->local.uni.next_stream_id = 3; + + params = &(*pconn)->local.settings.transport_params; + if (dcid->datalen == 0) { + /* Client uses zero-length Connection ID */ + params->active_connection_id_limit = 0; + } + + 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 size_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) { + size_t fc_credits = conn_fc_credits(conn, strm); + return ngtcp2_min(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); + + ngtcp2_mem_free(conn->mem, conn->token.begin); + 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_vec_del(conn->early.hp_key, 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); + + ngtcp2_default_cc_free(&conn->cc); + + 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_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) { + ngtcp2_duration initial_delay = + conn->local.settings.transport_params.max_ack_delay; + + if (conn->rcs.smoothed_rtt == 0) { + return initial_delay; + } + + return ngtcp2_min(initial_delay, conn->rcs.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_acktr *acktr, uint8_t type, + ngtcp2_tstamp ts, ngtcp2_duration ack_delay, + uint64_t ack_delay_exponent) { + /* TODO Measure an actual size of ACK bloks to find the best default + value. */ + const size_t initial_max_ack_blks = 8; + int64_t last_pkt_num; + ngtcp2_ack_blk *blk; + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *rpkt; + ngtcp2_ack *ack; + size_t blk_idx; + 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; + + rpkt = ngtcp2_ksl_it_get(&it); + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + ack->type = NGTCP2_FRAME_ACK; + ack->largest_ack = rpkt->pkt_num; + ack->first_ack_blklen = rpkt->len - 1; + if (type == NGTCP2_PKT_SHORT) { + ack->ack_delay_unscaled = ts - rpkt->tstamp; + ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / + (1UL << ack_delay_exponent); + } else { + ack->ack_delay_unscaled = 0; + ack->ack_delay = 0; + } + ack->num_blks = 0; + + ngtcp2_ksl_it_next(&it); + + 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); + if (rv != 0) { + return rv; + } + + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + conn->rcs.last_tx_pkt_ts[rtb->crypto_level] = 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_left returns the number of bytes the local endpoint can + * sent at this time. + */ +static uint64_t conn_cwnd_left(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = ngtcp2_conn_get_bytes_in_flight(conn); + uint64_t cwnd = + conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? NGTCP2_MIN_CWND + : conn->ccs.cwnd; + + /* We might send more than bytes_in_flight if probe packets are + involved. */ + if (bytes_in_flight >= cwnd) { + return 0; + } + return cwnd - bytes_in_flight; +} + +/* + * 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; +} + +/* + * conn_cryptofrq_top returns the element which sits on top of the + * queue. The queue must not be empty. + */ +static ngtcp2_frame_chain *conn_cryptofrq_top(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + ngtcp2_ksl_it it; + (void)conn; + + assert(ngtcp2_ksl_len(&pktns->crypto.tx.frq)); + + it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); + return ngtcp2_ksl_it_get(&it); +} + +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; + size_t base_offset, end_base_offset; + ngtcp2_ksl_it gapit; + ngtcp2_range gap; + ngtcp2_rtb *rtb = &pktns->rtb; + ngtcp2_vec *v; + int rv; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + *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, + ngtcp2_ksl_key_ptr(&key, &fr->offset)); + + idx = 0; + offset = fr->offset; + base_offset = 0; + + gapit = + ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset, offset); + gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr; + 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 -= 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; + 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 -= end_base_offset; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &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 = end_base_offset; + } + fr->data[0].base += base_offset; + fr->data[0].len -= 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; + ngtcp2_ksl_key key; + + 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, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &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_key key; + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + int rv; + + it = ngtcp2_ksl_lower_bound(&conn->scid.set, + ngtcp2_ksl_key_ptr(&key, &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 || type != NGTCP2_PKT_INITIAL) { + return 0; + } + + if (!conn->early.ckm || early_datalen == 0) { + return 1; + } + 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; + + 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_key = pktns->crypto.tx.hp_key; + 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 (type == NGTCP2_PKT_INITIAL && ngtcp2_buf_len(&conn->token)) { + hd.token = conn->token.pos; + hd.tokenlen = ngtcp2_buf_len(&conn->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; + } + + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(&ppe); + left = ngtcp2_pkt_crypto_max_datalen( + conn_cryptofrq_top(conn, pktns)->fr.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_CRYPTO_PKT; + } + + rv = conn_create_ack_frame(conn, &ackfr, &pktns->acktr, 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; + } + } + + /* 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->rcs, &conn->ccs); + + ++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->acktr, 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); +} + +/* + * 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; + + /* Actullay client never send ACK for server Initial. This is + because once it gets server Initial, it gets Handshake tx key and + discards Initial key. */ + 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; + } + + 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; + + nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, + early_datalen, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + if (!conn->server && nwrite) { + 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; + + return res; +} + +static ngtcp2_ssize conn_write_server_handshake(ngtcp2_conn *conn, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; + + nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + + /* Acknowledge 0-RTT packet here. */ + if (conn->pktns.crypto.tx.ckm) { + nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_SHORT, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + } + + 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; + + return win < 2 * inc || inc >= 10 * NGTCP2_MAX_DGRAM_SIZE; +} + +/* + * 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; + + return conn->local.settings.transport_params.initial_max_data < 2 * inc || + inc >= 10 * NGTCP2_MAX_DGRAM_SIZE; +} + +/* + * 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) { + size_t n, 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 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_key key; + 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, ngtcp2_ksl_key_ptr(&key, &cid)); + if (!ngtcp2_ksl_it_end(&it) && + ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it).ptr, &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, + ngtcp2_ksl_key_ptr(&key, &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_rcvry_stat *rcs = &conn->rcs; + ngtcp2_duration var = ngtcp2_max(4 * rcs->rttvar, NGTCP2_GRANULARITY); + ngtcp2_duration max_ack_delay = + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) + ? conn->remote.transport_params.max_ack_delay + : NGTCP2_DEFAULT_MAX_ACK_DELAY; + return rcs->smoothed_rtt + var + max_ack_delay; +} + +/* + * conn_remove_retired_connection_id removes the already retired + * connection ID. It waits RTT * 2 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); + ngtcp2_scid *scid; + ngtcp2_ksl_key key; + 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, + ngtcp2_ksl_key_ptr(&key, &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_STREAM_MORE indicates that more stream DATA + may come and it should be encoded into the current packet. */ + NGTCP2_WRITE_PKT_FLAG_STREAM_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 to |data_strm|. If |fin| is + * set to nonzero, it signals that the given data is the final portion + * of the stream. |datav| vector of length |datavcnt| specify stream + * data to send. If no stream data to send, set |strm| to NULL. The + * number of bytes sent to the stream is assigned to |*pdatalen|. If + * 0 length STREAM data is sent, 0 is assigned to |*pdatalen|. The + * caller should initialize |*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_ssize *pdatalen, + uint8_t type, ngtcp2_strm *data_strm, + int fin, const ngtcp2_vec *datav, + size_t datavcnt, 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 = ngtcp2_vec_len(datav, datavcnt); + 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 stream_more = (flags & NGTCP2_WRITE_PKT_FLAG_STREAM_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; + + /* Return 0 if destlen is less than minimum packet length which can + trigger Stateless Reset */ + if (destlen < min_pktlen) { + return 0; + } + + if (data_strm) { + ndatalen = conn_enforce_flow_control(conn, data_strm, datalen); + /* 0 length STREAM frame is allowed */ + if (ndatalen || datalen == 0) { + send_stream = 1; + } else { + stream_blocked = 1; + } + } + + 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_key = pktns->crypto.tx.hp_key; + + /* 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_key = conn->early.hp_key; + 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->acktr, 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; + } + } + + for (pfrc = &pktns->tx.frq; *pfrc;) { + 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; + } + + if ((*pfrc)->fr.type == NGTCP2_FRAME_RETIRE_CONNECTION_ID) { + assert(conn->dcid.num_retire_queued); + --conn->dcid.num_retire_queued; + } + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + } + + if (rv != NGTCP2_ERR_NOBUF) { + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_crypto_max_datalen( + conn_cryptofrq_top(conn, pktns)->fr.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; + } + } + + /* 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; + 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; + 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; + 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; + } + + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_stream_max_datalen( + strm->stream_id, ngtcp2_strm_streamfrq_top(strm)->fr.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; + + 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->acktr, 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); + } + } + } + } 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(data_strm->stream_id, + data_strm->tx.offset, ndatalen, + left)) != (size_t)-1 && + (ndatalen || datalen == 0)) { + datacnt = ngtcp2_vec_copy_at_most( + data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, datav, datavcnt, 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 = data_strm->stream_id; + nfrc->fr.stream.offset = data_strm->tx.offset; + nfrc->fr.stream.datacnt = datacnt; + ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt); + + fin = 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; + + data_strm->tx.offset += ndatalen; + conn->tx.offset += ndatalen; + + if (fin) { + ngtcp2_strm_shutdown(data_strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + + if (pdatalen) { + *pdatalen = (ngtcp2_ssize)ndatalen; + } + } else { + send_stream = 0; + } + + if (pkt_empty) { + assert(rv == 0 || NGTCP2_ERR_NOBUF == rv); + if (rv == 0 && stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + return 0; + } + + if (stream_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 (stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + if (send_stream) { + return NGTCP2_ERR_WRITE_STREAM_MORE; + } + } + + if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + if (pktns->rtb.probe_pkt_left || + 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); + } 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 || + (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; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); + + 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; + } + + 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) && + (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; + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++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; + size_t pktlen; + + 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_key = pktns->crypto.tx.hp_key; + 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) { + if (fr->type == NGTCP2_FRAME_ACK) { + /* If PADDING is added, the packet suddenly gets CWND + limited. */ + pktlen = ngtcp2_ppe_pktlen(&ppe); + if (pktlen > conn_cwnd_left(conn)) { + return 0; + } + } + + 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; + } + + 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->rcs, &conn->ccs); + + ++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_write_probe_ping writes probe packet containing PING frame + * (and optionally ACK frame) to the buffer pointed by |dest| of + * length |destlen|. Probe packet is always Short packet. This + * function might return 0 if it cannot write packet (e.g., |destlen| + * is too small). + * + * This function returns the number of bytes written to |dest|, 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_probe_ping(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_crypto_cc cc; + ngtcp2_frame_chain *frc = NULL; + ngtcp2_rtb_entry *ent; + ngtcp2_frame *ackfr = NULL, lfr; + int rv; + ngtcp2_ssize nwrite; + + assert(pktns->crypto.tx.ckm); + + cc.aead_overhead = conn->crypto.aead_overhead; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_key = pktns->crypto.tx.hp_key; + + ngtcp2_pkt_hd_init( + &hd, + (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE, + NGTCP2_PKT_SHORT, &conn->dcid.current.cid, NULL, + 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 = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame(conn, &ppe, &hd, &frc->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + rv = 0; + goto fail; + } + + rv = conn_create_ack_frame( + conn, &ackfr, &pktns->acktr, NGTCP2_PKT_SHORT, ts, + conn_compute_ack_delay(conn), + conn->local.settings.transport_params.ack_delay_exponent); + if (rv != 0) { + goto fail; + } + + if (ackfr) { + rv = conn_ppe_write_frame(conn, &ppe, &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); + } + } + + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + if (lfr.padding.len) { + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(&ppe, NULL); + if (nwrite < 0) { + rv = (int)nwrite; + goto fail; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); + + rv = ngtcp2_rtb_entry_new( + &ent, &hd, frc, ts, (size_t)nwrite, + NGTCP2_RTB_FLAG_PROBE | NGTCP2_RTB_FLAG_ACK_ELICITING, conn->mem); + if (rv != 0) { + goto fail; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); + if (rv != 0) { + ngtcp2_rtb_entry_del(ent, conn->mem); + return rv; + } + + if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { + conn_restart_timer_on_write(conn, ts); + } + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++pktns->tx.last_pkt_num; + + return nwrite; + +fail: + ngtcp2_frame_chain_del(frc, conn->mem); + + return rv; +} + +/* + * conn_write_probe_pkt writes a QUIC Short packet as probe packet. + * The packet is written to the buffer pointed by |dest| of length + * |destlen|. This function can send new stream data. In order to + * send stream data, specify the underlying stream to |strm|. If + * |fin| is set to nonzero, it signals that the given data is the + * final portion of the stream. |datav| vector of length |datavcnt| + * specify stream data to send. If no stream data to send, set |strm| + * to NULL. The number of bytes sent to the stream is assigned to + * |*pdatalen|. If 0 length STREAM data is sent, 0 is assigned to + * |*pdatalen|. The caller should initialize |*pdatalen| to -1. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest|, 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_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, + ngtcp2_strm *strm, int fin, + const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "transmit probe pkt left=%zu", + conn->pktns.rtb.probe_pkt_left); + + /* a probe packet is not blocked by cwnd. */ + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, NGTCP2_WRITE_PKT_FLAG_NONE, ts); + if (nwrite == 0 || nwrite == NGTCP2_ERR_STREAM_DATA_BLOCKED) { + nwrite = conn_write_probe_ping(conn, dest, destlen, ts); + } + if (nwrite <= 0) { + return nwrite; + } + + --conn->pktns.rtb.probe_pkt_left; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", + nwrite); + + return nwrite; +} + +/* + * 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 && (ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) || + ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || + (hs_pktns && (ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) || + 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(conn, lfr.path_challenge.data, + sizeof(lfr.path_challenge.data), + NGTCP2_RAND_CTX_PATH_CHALLENGE, conn->user_data); + 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 * NGTCP2_DEFAULT_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, + /* fin = */ 0, /* 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; + + 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; + } + + /* TODO Just move to the terminal state for now in order not to send + CONNECTION_CLOSE frame. */ + conn->state = NGTCP2_CS_DRAINING; + + nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen); + + ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); + + if (conn->callbacks.recv_version_negotiation) { + rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, + conn->user_data); + } + + if (p != sv) { + ngtcp2_mem_free(conn->mem, p); + } + + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +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; +} + +/* + * 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. + */ +static int 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; + ngtcp2_ksl_key key; + 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, + ngtcp2_ksl_key_ptr(&key, &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; + uint8_t *p; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_rtb *in_rtb = &in_pktns->rtb; + uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; + ngtcp2_frame_chain *frc = NULL; + + if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + return 0; + } + + 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); + 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.tokenlen == 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->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + + assert(conn->callbacks.recv_retry); + + rv = conn->callbacks.recv_retry(conn, hd, &retry, 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. */ + + ngtcp2_rtb_remove_all(rtb, &frc); + + rv = conn_resched_frames(conn, &conn->pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + frc = NULL; + ngtcp2_rtb_remove_all(in_rtb, &frc); + + rv = conn_resched_frames(conn, in_pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + assert(conn->token.begin == NULL); + + p = ngtcp2_mem_malloc(conn->mem, retry.tokenlen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + ngtcp2_buf_init(&conn->token, p, retry.tokenlen); + + ngtcp2_cpymem(conn->token.begin, retry.token, retry.tokenlen); + conn->token.pos = conn->token.begin; + conn->token.last = conn->token.pos + retry.tokenlen; + + conn_reset_congestion_state(conn); + + return 0; +} + +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts) { + ngtcp2_frame_chain *frc = NULL; + int rv; + ngtcp2_duration pto = conn_compute_pto(conn); + + ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, &frc, rcs, pto, ts); + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frc, conn->mem); + 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_rcvry_stat *rcs = &conn->rcs; + ngtcp2_ssize num_acked; + + 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, 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->rcs, ts); + if (rv != 0) { + return rv; + } + + rcs->pto_count = 0; + pktns->rtb.probe_pkt_left = 0; + + 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, payload, payloadlen, ckm->key.base, 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_vec *hp_key, + 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_key->base, 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; + + 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) { + rcvry_stat_reset(&conn->rcs); + cc_stat_reset(&conn->ccs); + + if (conn->hs_pktns) { + conn->hs_pktns->rtb.cc_pkt_num = conn->hs_pktns->tx.last_pkt_num + 1; + } + conn->pktns.rtb.cc_pkt_num = conn->pktns.tx.last_pkt_num + 1; +} + +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) { + int rv; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + ngtcp2_range range; + + 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; + } + + 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) { + it = ngtcp2_ksl_begin(&pktns->rx.pngap.gap); + range = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + ngtcp2_ksl_remove(&pktns->rx.pngap.gap, NULL, + ngtcp2_ksl_key_ptr(&key, &range)); + } + + return 0; +} + +static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + uint64_t bytes_in_flight; + + bytes_in_flight = ngtcp2_rtb_get_bytes_in_flight(&pktns->rtb); + + assert(conn->ccs.bytes_in_flight >= bytes_in_flight); + + conn->ccs.bytes_in_flight -= bytes_in_flight; + + pktns_del(pktns, conn->mem); +} + +/* + * conn_discard_initial_state discards state for Initial packet number + * space. + */ +static void conn_discard_initial_state(ngtcp2_conn *conn) { + if (!conn->in_pktns) { + return; + } + + conn_discard_pktns(conn, conn->in_pktns); + conn->in_pktns = NULL; +} + +/* + * conn_discard_handshake_state discards state for Handshake packet + * number space. + */ +static void conn_discard_handshake_state(ngtcp2_conn *conn) { + if (!conn->hs_pktns) { + return; + } + + conn_discard_pktns(conn, conn->hs_pktns); + conn->hs_pktns = NULL; +} + +/* + * 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->tokenlen && + ngtcp2_cmemeq(token->base, hd->token, 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; + const ngtcp2_vec *hp_key; + 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; + } + + /* TODO Do not change state here? */ + 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; + } + + 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) { + rv = conn_call_recv_client_initial(conn, &hd.dcid); + if (rv != 0) { + return rv; + } + } + } else if (hd.tokenlen != 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_key = pktns->crypto.rx.hp_key; + + assert(ckm); + assert(hp_mask); + assert(decrypt); + + nwrite = decrypt_hp(&hd, plain_hdpkt, sizeof(plain_hdpkt), hp, pkt, pktlen, + (size_t)nread, ckm, hp_key, 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->rcid = hd.dcid; + } else { + conn->dcid.current.cid = hd.scid; + } + conn->odcid = 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: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + 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); + 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->rcs, &conn->ccs); + + 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->hs_recved += 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; + } + + switch (nread) { + case NGTCP2_ERR_DISCARD_PKT: + return 0; + case NGTCP2_ERR_DRAINING: + return NGTCP2_ERR_DRAINING; + } + + if (nread != NGTCP2_ERR_CRYPTO && (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) { + break; + } + 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; + + 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; + + rv = conn_call_recv_stream_data(conn, strm, + (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + rx_offset == strm->rx.last_offset, + 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) { + 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 = 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_rob_remove_prefix(&crypto->rx.rob, 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, size_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) { + 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); + + 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) { + size_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); + } + + if (fr_end_offset == rx_offset) { + rv = conn_call_recv_stream_data(conn, strm, 1, rx_offset, NULL, 0); + if (rv != 0) { + return rv; + } + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, + NGTCP2_NO_ERROR); + } + } + } 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; + } + } + + if (fr->offset <= rx_offset) { + size_t ncut = 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_rob_remove_prefix(&strm->rx.rob, 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) { + rv = conn_call_recv_stream_data(conn, strm, fin, 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 (*punsent_max_remote_streams < NGTCP2_MAX_STREAMS - n) { + *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) { + 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 + */ +static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { + (void)conn; + + if (fr->tokenlen == 0) { + return NGTCP2_ERR_FRAME_ENCODING; + } + /* TODO Not implemented yet*/ + 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) { + uint8_t buf[128]; + ngtcp2_addr addr; + int rv; + ngtcp2_duration timeout; + ngtcp2_pv *pv; + ngtcp2_dcid *dcid; + + ngtcp2_addr_init(&addr, 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 = conn_compute_pto(conn); + timeout = + ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_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) { + 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; + + conn_discard_handshake_state(conn); + + if (conn->callbacks.handshake_confirmed) { + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + 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); + 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; + size_t secretlen, keylen, ivlen; + + 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; + keylen = rx_ckm->key.len; + ivlen = rx_ckm->iv.len; + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm, + secretlen, keylen, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm, + secretlen, keylen, 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, + new_rx_ckm->key.base, new_rx_ckm->iv.base, new_tx_ckm->key.base, + 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; + } + + 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; + } + + 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; + + 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 = conn_compute_pto(conn); + timeout = + ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_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: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + 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); + 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, ts); + if (rv != 0) { + return rv; + } + + conn_restart_timer_on_read(conn, ts); + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + 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; + const ngtcp2_vec *hp_key; + 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->odcid, &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_key = pktns->crypto.rx.hp_key; + 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_key = conn->early.hp_key; + 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_key = pktns->crypto.rx.hp_key; + 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_key, 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.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; + } + break; + 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; + } + } + + 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); + + 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) { + if (hd.type == NGTCP2_PKT_HANDSHAKE) { + 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; + } + } else { + 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: + 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: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STREAM: + rv = conn_recv_stream(conn, &fr->stream); + 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); + 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); + 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->rcs, &conn->ccs); + + 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; + + 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; +} + +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 ngtcp2_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. */ + return conn->hs_recved * 3 - conn->hs_sent; +} + +/* + * 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; +} + +int ngtcp2_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; + } + + conn_discard_initial_state(conn); + } + + 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_rob_first_gap_offset(&conn->in_pktns->crypto.strm.rx.rob) == 0) { + if (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); + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + return 0; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + + 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); + + rv = conn_enqueue_handshake_done(conn); + if (rv != 0) { + return rv; + } + + return 0; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } +} + +/* + * 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, NULL, 0, + NULL, 0, NGTCP2_WRITE_PKT_FLAG_NONE, ts); +} + +/* + * 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; + uint64_t cwnd; + size_t origlen = destlen; + size_t server_hs_tx_left; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + size_t pending_early_datalen; + ngtcp2_dcid *dcid; + ngtcp2_preferred_addr *paddr; + + cwnd = conn_cwnd_left(conn); + destlen = ngtcp2_min(destlen, cwnd); + + 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; + + return nwrite + early_spktlen; + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite) { + return nwrite; + } + } + + 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; + } + 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); + } + + 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; + } + + if (conn->remote.transport_params.preferred_address_present) { + rv = conn_select_preferred_addr(conn); + if (rv != 0) { + return rv; + } + } + + return res; + case NGTCP2_CS_SERVER_INITIAL: + nwrite = conn_write_server_handshake(conn, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + if (nwrite) { + conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE; + conn->hs_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 (rcs->loss_detection_timer) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + rcs->loss_detection_timer = 0; + } + return 0; + } + + destlen = ngtcp2_min(destlen, server_hs_tx_left); + origlen = ngtcp2_min(origlen, server_hs_tx_left); + + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite < 0) { + return nwrite; + } + + /* Coalesce packets, because server might send Initial as + probe, and Handshake as non-probe. */ + res += nwrite; + dest += nwrite; + if (destlen <= (size_t)nwrite) { + goto server_wait_handshake_done; + } + destlen -= (size_t)nwrite; + origlen -= (size_t)nwrite; + } + + nwrite = conn_write_server_handshake(conn, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + /* TODO Write 1RTT ACK packet if we have received 0RTT packet */ + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + origlen -= (size_t)nwrite; + + nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + server_wait_handshake_done: + conn->hs_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; + } +} + +ngtcp2_ssize ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + return conn_write_handshake(conn, dest, destlen, 0, ts); +} + +ngtcp2_ssize ngtcp2_conn_client_write_handshake( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, int fin, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts) { + ngtcp2_strm *strm = NULL; + int send_stream = 0; + ngtcp2_ssize spktlen, early_spktlen; + uint64_t cwnd; + int was_client_initial; + size_t datalen = ngtcp2_vec_len(datav, datavcnt); + 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 (stream_id != -1 && + !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + 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; + } + + send_stream = conn_retry_early_payloadlen(conn) == 0 && + /* 0 length STREAM frame is allowed */ + (datalen == 0 || + (datalen > 0 && (strm->tx.max_offset - strm->tx.offset) && + (conn->tx.max_offset - conn->tx.offset))); + if (send_stream) { + early_datalen = + ngtcp2_min(datalen, strm->tx.max_offset - strm->tx.offset); + early_datalen = + ngtcp2_min(early_datalen, conn->tx.max_offset - conn->tx.offset) + + NGTCP2_STREAM_OVERHEAD; + + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; + } + } else { + strm = 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; + } + + cwnd = conn_cwnd_left(conn); + + dest += spktlen; + destlen -= (size_t)spktlen; + destlen = ngtcp2_min(destlen, cwnd); + + early_spktlen = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_0RTT, + strm, fin, datav, datavcnt, wflags, ts); + + if (early_spktlen < 0) { + switch (early_spktlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + return spktlen; + case NGTCP2_ERR_WRITE_STREAM_MORE: + conn->pkt.was_client_initial = was_client_initial; + conn->pkt.hs_spktlen = spktlen; + break; + } + return early_spktlen; + } + + return spktlen + early_spktlen; +} + +void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; +} + +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->tokenlen == 0 && p->dcid.datalen < 8) { + 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 uint8_t *rx_key, + const uint8_t *rx_iv, + const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, + size_t ivlen) { + ngtcp2_pktns *pktns = conn->in_pktns; + int rv; + + assert(pktns); + + if (pktns->crypto.rx.hp_key) { + ngtcp2_vec_del(pktns->crypto.rx.hp_key, conn->mem); + pktns->crypto.rx.hp_key = NULL; + } + if (pktns->crypto.rx.ckm) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + } + if (pktns->crypto.tx.hp_key) { + ngtcp2_vec_del(pktns->crypto.tx.hp_key, conn->mem); + pktns->crypto.tx.hp_key = NULL; + } + if (pktns->crypto.tx.ckm) { + 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, rx_key, keylen, + rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, tx_key, keylen, + tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_handshake_key( + ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen) { + ngtcp2_pktns *pktns = conn->hs_pktns; + int rv; + + assert(pktns); + assert(!pktns->crypto.rx.hp_key); + assert(!pktns->crypto.rx.ckm); + assert(!pktns->crypto.tx.hp_key); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, rx_key, keylen, + rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, tx_key, keylen, + tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, const uint8_t *key, + const uint8_t *iv, const uint8_t *hp_key, + size_t keylen, size_t ivlen) { + int rv; + + assert(!conn->early.hp_key); + assert(!conn->early.ckm); + + rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, key, keylen, iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&conn->early.hp_key, hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_key(ngtcp2_conn *conn, const uint8_t *rx_secret, + const uint8_t *tx_secret, const uint8_t *rx_key, + const uint8_t *rx_iv, const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t secretlen, + size_t keylen, size_t ivlen) { + ngtcp2_pktns *pktns = &conn->pktns; + int rv; + + assert(!pktns->crypto.rx.hp_key); + assert(!pktns->crypto.rx.ckm); + assert(!pktns->crypto.tx.hp_key); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, rx_secret, secretlen, rx_key, + keylen, rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, tx_secret, secretlen, tx_key, + keylen, tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + 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); + + 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->rcs.loss_detection_timer) { + return conn->rcs.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); + 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 res = ngtcp2_min(t1, t2); + return ngtcp2_min(res, t3); +} + +int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + + ngtcp2_conn_cancel_expired_ack_delay_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; +} + +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); +} + +/* + * 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_TRANSPORT_PARAM + * Transport parameters are invalid. + */ +static int +conn_client_validate_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params) { + if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + if (!params->original_connection_id_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_connection_id)) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + } else if (params->original_connection_id_present) { + 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; + } + + 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, + /* local = */ 0); + + if (conn->pktns.crypto.rx.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, + /* local = */ 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_ord_stream_id(conn->local.bidi.next_stream_id) > + conn->local.bidi.max_streams) { + 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_ord_stream_id(conn->local.uni.next_stream_id) > + conn->local.uni.max_streams) { + 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, int fin, + 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, fin, &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, int fin, + const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { + ngtcp2_strm *strm = NULL; + ngtcp2_ssize nwrite; + uint64_t cwnd; + 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; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (pdatalen) { + *pdatalen = -1; + } + + 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 = + ngtcp2_conn_client_write_handshake(conn, dest, destlen, pdatalen, flags, + stream_id, fin, datav, datavcnt, 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) { + nwrite = ngtcp2_conn_write_handshake(conn, dest, destlen, ts); + if (nwrite) { + return nwrite; + } + } + if (conn->state != NGTCP2_CS_POST_HANDSHAKE && + conn->pktns.crypto.tx.ckm == NULL) { + return 0; + } + 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; + } + + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { + rv = conn_prepare_key_update(conn, ts); + if (rv != 0) { + return rv; + } + } + + rv = conn_remove_retired_connection_id(conn, ts); + if (rv != 0) { + return rv; + } + + 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; + } + + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; + } + } + + cwnd = conn_cwnd_left(conn); + destlen = ngtcp2_min(destlen, cwnd); + + 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, pdatalen, NGTCP2_PKT_SHORT, + strm, fin, datav, datavcnt, wflags, ts); + goto fin; + } + + if (conn->pv) { + nwrite = conn_write_path_challenge(conn, path, dest, origlen, ts); + if (nwrite) { + goto fin; + } + } + + if (res == 0) { + if (conn_handshake_remnants_left(conn)) { + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite) { + return nwrite; + } + } else { + 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) { + nwrite = conn_write_probe_pkt(conn, dest, origlen, pdatalen, strm, fin, + datav, datavcnt, ts); + goto fin; + } + + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, wflags, ts); + if (nwrite) { + assert(nwrite != NGTCP2_ERR_NOBUF); + goto fin; + } + + if (res == 0) { + return conn_write_ack_pkt(conn, dest, origlen, NGTCP2_PKT_SHORT, ts); + } + +fin: + conn->pkt.hs_spktlen = 0; + + if (nwrite >= 0) { + 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; +} + +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; + ngtcp2_ssize res = 0, nwrite; + ngtcp2_frame fr; + uint8_t pkt_type; + int bundle = 0; + uint8_t bundle_pkt_type; + + 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_CLOSING: + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + 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->state == NGTCP2_CS_POST_HANDSHAKE) { + 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; + } + + if (conn->server && pkt_type == NGTCP2_PKT_HANDSHAKE && in_pktns) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_INITIAL; + } else if (pkt_type == NGTCP2_PKT_SHORT && hs_pktns && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_HANDSHAKE; + } + + if (bundle) { + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, dest, destlen, bundle_pkt_type, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_FLAG_NONE, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + } + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, dest + res, destlen - (size_t)res, 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; + } + + conn->state = NGTCP2_CS_CLOSING; + + return res; +} + +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_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_POST_HANDSHAKE: + break; + default: + return NGTCP2_ERR_INVALID_STATE; + } + + 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; + } + + if (nwrite == 0) { + return NGTCP2_ERR_NOBUF; + } + + conn->state = NGTCP2_CS_CLOSING; + + return nwrite; +} + +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_rob_first_gap_offset(&strm->rx.rob) == 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, + size_t datalen) { + ngtcp2_strm *top; + + if (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, + size_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, size_t datalen) { + if (NGTCP2_MAX_VARINT < (uint64_t)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); +} + +size_t ngtcp2_conn_get_bytes_in_flight(ngtcp2_conn *conn) { + return conn->ccs.bytes_in_flight; +} + +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; + ngtcp2_frame_chain *frc = NULL; + int rv; + + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + + ngtcp2_rtb_remove_all(rtb, &frc); + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + return rv; +} + +void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + + rcs->latest_rtt = rtt; + + if (rcs->smoothed_rtt == 0) { + rcs->min_rtt = rtt; + rcs->smoothed_rtt = rtt; + rcs->rttvar = rtt / 2; + return; + } + + rcs->min_rtt = ngtcp2_min(rcs->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 > rcs->min_rtt + ack_delay) { + rtt -= ack_delay; + } + + rcs->rttvar = + (rcs->rttvar * 3 + (rcs->smoothed_rtt < rtt ? rtt - rcs->smoothed_rtt + : rcs->smoothed_rtt - rtt)) / + 4; + rcs->smoothed_rtt = (rcs->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)(rcs->latest_rtt / NGTCP2_MILLISECONDS), + (uint64_t)(rcs->min_rtt / NGTCP2_MILLISECONDS), + rcs->smoothed_rtt / NGTCP2_MILLISECONDS, + rcs->rttvar / NGTCP2_MILLISECONDS, + (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); +} + +const ngtcp2_rcvry_stat *ngtcp2_conn_get_rcvry_stat(ngtcp2_conn *conn) { + return &conn->rcs; +} + +const ngtcp2_cc_stat *ngtcp2_conn_get_cc_stat(ngtcp2_conn *conn) { + return &conn->ccs; +} + +static ngtcp2_pktns *conn_get_earliest_loss_time_pktns(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_pktns *res = in_pktns; + + if (hs_pktns && hs_pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || hs_pktns->rtb.loss_time < res->rtb.loss_time)) { + res = hs_pktns; + } + if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || pktns->rtb.loss_time < res->rtb.loss_time)) { + res = pktns; + } + + return res; +} + +static ngtcp2_tstamp conn_get_earliest_last_tx_pkt_ts(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_tstamp earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + ngtcp2_pktns *pktns = ns[0]; + size_t i; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (!pktns || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + pktns = ns[i]; + } + } + + return earliest_ts; +} + +static ngtcp2_pktns * +conn_get_earliest_non_null_last_tx_pktns(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_pktns *res = ns[0]; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + size_t i, earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (res == NULL || + (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (earliest_ts == UINT64_MAX || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + res = ns[i]; + } + } + + return res; +} + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + 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 = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_tstamp last_tx_pkt_ts; + + if (earliest_pktns && earliest_pktns->rtb.loss_time != UINT64_MAX) { + rcs->loss_detection_timer = earliest_pktns->rtb.loss_time; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", + rcs->loss_detection_timer); + return; + } + + if ((!in_pktns || ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) == 0) && + (!hs_pktns || ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) == 0) && + ngtcp2_rtb_num_ack_eliciting(&pktns->rtb) == 0 && + (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { + if (rcs->loss_detection_timer) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + rcs->loss_detection_timer = 0; + } + return; + } + + if (rcs->smoothed_rtt == 0) { + timeout = 2 * NGTCP2_DEFAULT_INITIAL_RTT; + } else { + timeout = conn_compute_pto(conn); + } + timeout *= 1ULL << rcs->pto_count; + + last_tx_pkt_ts = conn_get_earliest_last_tx_pkt_ts(conn); + + if (last_tx_pkt_ts == UINT64_MAX) { + last_tx_pkt_ts = ts; + } + + rcs->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, + rcs->loss_detection_timer, last_tx_pkt_ts, + (uint64_t)(timeout / NGTCP2_MILLISECONDS)); +} + +/* + * conn_handshake_pkt_lost is called when handshake packets which + * belong to |pktns| are lost. + * + * 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_crypto_timeout(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + ngtcp2_frame_chain *frc = NULL; + int rv; + + rv = ngtcp2_rtb_on_crypto_timeout(&pktns->rtb, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + return 0; +} + +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + int rv; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *loss_pktns = conn_get_earliest_loss_time_pktns(conn); + 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: + rcs->loss_detection_timer = 0; + return 0; + default: + break; + } + + if (!rcs->loss_detection_timer) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer fired"); + + if (loss_pktns && loss_pktns->rtb.loss_time != UINT64_MAX) { + rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, rcs, 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 { + conn_on_crypto_timeout(conn, in_pktns); + in_pktns->rtb.probe_pkt_left = 1; + } + } else { + earliest_pktns = conn_get_earliest_non_null_last_tx_pktns(conn); + + assert(earliest_pktns); + + switch (earliest_pktns->rtb.crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + assert(in_pktns); + rv = conn_on_crypto_timeout(conn, in_pktns); + if (rv != 0) { + return rv; + } + 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_CRYPTO_LEVEL_HANDSHAKE: + assert(hs_pktns); + rv = conn_on_crypto_timeout(conn, hs_pktns); + if (rv != 0) { + return rv; + } + hs_pktns->rtb.probe_pkt_left = 1; + break; + case NGTCP2_CRYPTO_LEVEL_APP: + conn->pktns.rtb.probe_pkt_left = 2; + break; + default: + assert(0); + } + } + + ++rcs->pto_count; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu", + rcs->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; + ngtcp2_ksl_key key; + 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, + ngtcp2_ksl_key_ptr(&key, &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; +} + +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; +} + +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->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); + + return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); +} + +ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { + return conn_compute_pto(conn); +} + +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) { + conn->crypto.retry_aead = *aead; +} + +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_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); +} + +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->transport_params.max_packet_size = NGTCP2_MAX_PKT_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 uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key) { + ngtcp2_pkt_hd hd; + ngtcp2_crypto_km ckm; + ngtcp2_crypto_cc cc; + ngtcp2_vec hp_key_vec; + 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.key, key, 16); + ngtcp2_vec_init(&ckm.iv, iv, 12); + ngtcp2_vec_init(&hp_key_vec, hp_key, 16); + 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_key = &hp_key_vec; + 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 new file mode 100644 index 00000000000000..3b17a3d230ada7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conn.h @@ -0,0 +1,708 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 10 + +/* + * 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 { + 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; + /* + * 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_key is header protection key. */ + ngtcp2_vec *hp_key; + } tx; + + struct { + /* ckm is a cryptographic key, and iv to decrypt incoming + packets. */ + ngtcp2_crypto_km *ckm; + /* hp_key is header protection key. */ + ngtcp2_vec *hp_key; + } 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. Client uses this field to validate + original_connection_id transport parameter. */ + ngtcp2_cid rcid; + /* oscid is the source connection ID initially used by the local + endpoint. */ + ngtcp2_cid oscid; + /* odcid is the destination connection ID initially negotiated + during handshake. It is used to receive late handshake packets + after handshake completion. */ + ngtcp2_cid odcid; + 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; + /* retire_prior_to is the larget 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; + } rx; + + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_vec *hp_key; + } 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; + + 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. */ + ngtcp2_crypto_aead retry_aead; + /* 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_rcvry_stat rcs; + ngtcp2_cc_stat ccs; + ngtcp2_pv *pv; + ngtcp2_log log; + ngtcp2_qlog qlog; + ngtcp2_rst rst; + ngtcp2_default_cc cc; + /* token is an address validation token received from server. */ + ngtcp2_buf token; + /* hs_recved is the number of bytes received from client before its + address is validated. This field is only used by server to + ensure "3 times received data" rule. */ + size_t hs_recved; + /* hs_sent is the number of bytes sent from server during handshake. + This field is only used by server to ensure "3 times received + data" rule. */ + size_t hs_sent; + 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; +}; + +/** + * @function + * + * `ngtcp2_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. + * + * The application should call `ngtcp2_conn_write_handshake` (or + * `ngtcp2_conn_client_write_handshake` for client session) to make + * handshake go forward after calling this function. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * 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: (TBD). + */ +int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_handshake` performs QUIC cryptographic handshake + * by writing handshake packets. It may write a packet in the given + * buffer pointed by |dest| whose capacity is given as |destlen|. + * Application must ensure that the buffer pointed by |dest| is not + * empty. + * + * Application should keep calling this function repeatedly until it + * returns zero, or negative error code. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * During handshake, application can send 0-RTT data (or its response) + * using `ngtcp2_conn_write_stream`. + * `ngtcp2_conn_client_write_handshake` is generally efficient because + * it can coalesce Handshake packet and 0-RTT packet into one UDP + * packet. + * + * 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 to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +ngtcp2_ssize ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_client_write_handshake` is just like + * `ngtcp2_conn_write_handshake`, but it is for client only, and can + * write 0-RTT data. This function can coalesce handshake packet and + * 0-RTT packet into single UDP packet, thus it is generally more + * efficient than the combination of `ngtcp2_conn_write_handshake` and + * `ngtcp2_conn_write_stream`. + * + * |stream_id|, |fin|, |datav|, and |datavcnt| are stream identifier + * 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. If there is no 0RTT data to send, pass negative + * integer to |stream_id|. The amount of 0RTT data sent is assigned + * to |*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 + * |*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). + */ +ngtcp2_ssize ngtcp2_conn_client_write_handshake( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, int fin, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts); + +/* + * 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_rcvry_stat *rcs, 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_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); + +#endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/lib/ngtcp2_conv.c new file mode 100644 index 00000000000000..7811c84d0bc19a --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conv.c @@ -0,0 +1,251 @@ +/* + * 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 +#include + +#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 ntohl(n); +} + +uint32_t ngtcp2_get_uint24(const uint8_t *p) { + uint32_t n = 0; + memcpy(((uint8_t *)&n) + 1, p, 3); + return ntohl(n); +} + +uint16_t ngtcp2_get_uint16(const uint8_t *p) { + uint16_t n; + memcpy(&n, p, 2); + return 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 ntohs(n.n16); + case 4: + memcpy(&n, p, 4); + n.b[0] &= 0x3f; + return 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 = htonl(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) { + n = htonl(n); + return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) { + n = 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 new file mode 100644 index 00000000000000..f3336f44a92dad --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conv.h @@ -0,0 +1,261 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +#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 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 */ + +/* + * 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 new file mode 100644 index 00000000000000..dd65ff7e3039fb --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_crypto.c @@ -0,0 +1,649 @@ +/* + * 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 +#include + +#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 uint8_t *key, size_t keylen, + const uint8_t *iv, size_t ivlen, + const ngtcp2_mem *mem) { + int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, keylen, ivlen, mem); + if (rv != 0) { + return rv; + } + + if (secretlen) { + memcpy((*pckm)->secret.base, secret, secretlen); + } + memcpy((*pckm)->key.base, key, keylen); + memcpy((*pckm)->iv.base, iv, ivlen); + + return 0; +} + +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, + size_t keylen, size_t ivlen, + const ngtcp2_mem *mem) { + size_t len; + uint8_t *p; + + len = sizeof(ngtcp2_crypto_km) + secretlen + keylen + 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)->key.base = p; + (*pckm)->key.len = keylen; + p += keylen; + (*pckm)->iv.base = p; + (*pckm)->iv.len = ivlen; + (*pckm)->pkt_num = -1; + (*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); +} + +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: + 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 == 0 || + 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->original_connection_id_present) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID) + + ngtcp2_put_varint_len(params->original_connection_id.datalen) + + params->original_connection_id.datalen; + } + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + 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_packet_size != NGTCP2_MAX_PKT_SIZE) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE, + params->max_packet_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) { + 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->original_connection_id_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID); + p = ngtcp2_put_varint(p, params->original_connection_id.datalen); + p = ngtcp2_cpymem(p, params->original_connection_id.data, + params->original_connection_id.datalen); + } + } + + 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_packet_size != NGTCP2_MAX_PKT_SIZE) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE, + params->max_packet_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 ((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); +} + +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; + uint8_t scb[8192]; + size_t scb_idx; + size_t scb_shift; + + p = data; + end = data + datalen; + + /* Set default values */ + 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_packet_size = NGTCP2_MAX_PKT_SIZE; + params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params->stateless_reset_token_present = 0; + params->preferred_address_present = 0; + memset(¶ms->preferred_address, 0, sizeof(params->preferred_address)); + 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->original_connection_id_present = 0; + + if (datalen == 0) { + return 0; + } + + memset(scb, 0, sizeof(scb)); + + for (; (size_t)(end - p) >= 2;) { + nread = decode_varint(¶m_type, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + + scb_idx = param_type / 8; + scb_shift = param_type % 8; + + if (scb[scb_idx] & (1 << scb_shift)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + scb[scb_idx] |= (uint8_t)(1 << scb_shift); + 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_PACKET_SIZE: + nread = decode_varint_param(¶ms->max_packet_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 != 0 && + 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_CONNECTION_ID: + 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 (valuelen < NGTCP2_MIN_CIDLEN || valuelen > NGTCP2_MAX_CIDLEN || + (size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + ngtcp2_cid_init(¶ms->original_connection_id, p, valuelen); + params->original_connection_id_present = 1; + p += valuelen; + 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; + } + + return 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/lib/ngtcp2_crypto.h new file mode 100644 index 00000000000000..8dc1ef9f39d344 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_crypto.h @@ -0,0 +1,102 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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_vec key; + 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; + /* 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 uint8_t *key, size_t keylen, + 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 keylen, 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; + const ngtcp2_crypto_km *ckm; + const ngtcp2_vec *hp_key; + 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 new file mode 100644 index 00000000000000..ae2edb35ee428f --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_err.c @@ -0,0 +1,136 @@ +/* + * 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_STREAM_MORE: + return "ERR_WRITE_STREAM_MORE"; + case NGTCP2_ERR_RETRY: + return "ERR_RETRY"; + 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 new file mode 100644 index 00000000000000..9229f5425a63cf --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_err.h @@ -0,0 +1,34 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGTCP2_ERR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/lib/ngtcp2_gaptr.c new file mode 100644 index 00000000000000..4cbd898a292dc1 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_gaptr.c @@ -0,0 +1,127 @@ +/* + * 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 + +int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + int rv; + ngtcp2_range range = {0, UINT64_MAX}; + ngtcp2_ksl_key key; + + 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, ngtcp2_ksl_key_ptr(&key, &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; + ngtcp2_ksl_key key, old_key; + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); + + for (; !ngtcp2_ksl_it_end(&it);) { + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + m = ngtcp2_range_intersect(&q, &k); + if (!ngtcp2_range_len(&m)) { + break; + } + + if (ngtcp2_range_eq(&k, &m)) { + ngtcp2_ksl_remove(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &k)); + continue; + } + ngtcp2_range_cut(&l, &r, &k, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k), + ngtcp2_ksl_key_ptr(&key, &l)); + + if (ngtcp2_range_len(&r)) { + rv = ngtcp2_ksl_insert(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &r), + NULL); + if (rv != 0) { + return rv; + } + } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k), + ngtcp2_ksl_key_ptr(&key, &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).ptr; + return r.begin; +} + +ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { + ngtcp2_range q = {offset, offset + 1}; + ngtcp2_ksl_key key; + return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, + ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); +} + +int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, + size_t datalen) { + ngtcp2_ksl_key key; + ngtcp2_range q = {offset, offset + datalen}; + ngtcp2_ksl_it it = + ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); + ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/lib/ngtcp2_gaptr.h new file mode 100644 index 00000000000000..03181449deaf02 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_gaptr.h @@ -0,0 +1,96 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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); + +#endif /* NGTCP2_GAPTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/lib/ngtcp2_idtr.c new file mode 100644 index 00000000000000..f04806b4a8b22f --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_idtr.c @@ -0,0 +1,86 @@ +/* + * 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 + +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 new file mode 100644 index 00000000000000..63f332e64f9cd0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_idtr.h @@ -0,0 +1,95 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..7936a269127c21 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ksl.c @@ -0,0 +1,749 @@ +/* + * 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 +#include +#include +#include + +#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); +} + +/* + * ksl_node_key assigns the pointer to key of |node| to key->ptr and + * returns |key|. + */ +static ngtcp2_ksl_key *ksl_node_key(ngtcp2_ksl_key *key, + ngtcp2_ksl_node *node) { + key->ptr = &node->key; + return key; +} + +/* + * ksl_nth_node returns |n|th node under |blk|. + */ +static ngtcp2_ksl_node *ksl_nth_node(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t n) { + return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} + +ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t n) { + return ksl_nth_node(ksl, blk, n); +} + +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, 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 = 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 = ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, &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 = ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, &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 = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key->ptr); + 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; + ngtcp2_ksl_key node_key; + + while (right - left > 1) { + mid = (left + right) / 2; + node = ksl_nth_node(ksl, blk, (size_t)mid); + if (compar(ksl_node_key(&node_key, node), 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; + ngtcp2_ksl_key node_key; + 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) { + 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 = 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 = ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key->ptr); + 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 = 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(ksl_node_key(&node_key, node), key)) { + node = ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + ksl_node_set_key(ksl, node, key->ptr); + } + } + } + + 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 = ksl_nth_node(ksl, blk, i)->blk; + rblk = 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, ksl_nth_node(ksl, blk, i), + &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 = ksl_nth_node(ksl, blk, i - 1); + rnode = ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK); + assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK); + + dest = ksl_nth_node(ksl, lnode->blk, lnode->blk->n); + src = 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 = ksl_nth_node(ksl, blk, i); + rnode = 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 = ksl_nth_node(ksl, rnode->blk, 0); + src = ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); + + memcpy(dest, src, ksl->nodelen); + + --lnode->blk->n; + ksl_node_set_key(ksl, lnode, + &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); +} + +void 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 && + ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && + 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); + + assert(i < blk->n); + + if (blk->leaf) { + assert(i < blk->n); + 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; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGTCP2_KSL_MIN_NBLK) { + if (i > 0 && + ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + } else if (i + 1 < blk->n && + ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + } 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 = 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 = 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 = 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 = 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; + ngtcp2_ksl_key node_key; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, ksl_node_key(&node_key, node), old_key)); + ksl_node_set_key(ksl, node, new_key->ptr); + return; + } + + ksl_node_key(&node_key, node); + if (key_equal(ksl->compar, &node_key, old_key) || + ksl->compar(&node_key, new_key)) { + ksl_node_set_key(ksl, node, new_key->ptr); + } + + blk = node->blk; + } +} + +static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { + size_t i; + ngtcp2_ksl_node *node; + ngtcp2_ksl_key node_key; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *ksl_node_key(&node_key, node)->i); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, 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, 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 ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) { + assert(!ngtcp2_ksl_it_end(it)); + + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} + +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_end(const ngtcp2_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} + +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) { + ngtcp2_ksl_key node_key; + + assert(it->i < it->blk->n); + + return *ksl_node_key(&node_key, ksl_nth_node(it->ksl, it->blk, it->i)); +} + +ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr) { + key->ptr = ptr; + return key; +} + +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + const ngtcp2_range *a = lhs->ptr, *b = rhs->ptr; + 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->ptr, *b = rhs->ptr; + 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 new file mode 100644 index 00000000000000..c9ce128a2209b2 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ksl.h @@ -0,0 +1,334 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +/* + * Skip List using single key instead of range. + */ + +#define NGTCP2_KSL_DEGR 8 +/* 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 union { + /* i is defined to retrieve int64_t key for convenience. */ + const int64_t *i; + /* ptr points to the key. */ + const void *ptr; +} 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 assumes that |key| does not exist in |ksl|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +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|. It assumes such + * the key is included in |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. + */ +void 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|. This + * function is provided for unit testing. + */ +ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t 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. + */ +void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it); + +/* + * 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. + */ +int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it); + +/* + * 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. + */ +ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_key_ptr is a convenient function which initializes |key| + * with |ptr| and returns |key|. + */ +ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr); + +/* + * 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 new file mode 100644 index 00000000000000..11f335d974c3eb --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_log.c @@ -0,0 +1,730 @@ +/* + * 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 +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include + +#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 + * + * + * + * : + * Log level. I=Info, W=Warning, E=Error + * + * : + * Timestamp relative to ngtcp2_log.ts field in milliseconds + * resolution. + * + * : + * Source Connection ID in hex string. + * + * : + * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto, + * con=connection(catch all) + * + * # Frame event + * + * () () + * + * : + * Flow direction. tx=transmission, rx=reception + * + * : + * Packet number. + * + * : + * Packet name. (e.g., Initial, Handshake, S01) + * + * : + * Packet type in hex string. + * + * : + * Frame name. (e.g., STREAM, ACK, PING) + * + * : + * 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_SERVER_BUSY: + return "SERVER_BUSY"; + 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_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=%" PRIu64 " 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=%" PRIu64), + 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=%" PRIu64 " 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->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->tokenlen > 64) { + p = ngtcp2_encode_hex(buf, fr->token, 64); + p[128] = '*'; + p[129] = '\0'; + } else { + p = ngtcp2_encode_hex(buf, fr->token, fr->tokenlen); + } + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s token_len=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->tokenlen); +} + +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))); + } + + if (params->original_connection_id_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " original_connection_id=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + cid, params->original_connection_id.data, + params->original_connection_id.datalen)); + } + } + + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%u"), + 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=%u"), + 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=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_bidi_streams=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_uni_streams=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%u"), + NGTCP2_LOG_TP_HD_FIELDS, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_packet_size=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->max_packet_size); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " ack_delay_exponent=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%u"), + 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 new file mode 100644 index 00000000000000..bb9737e354b019 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_log.h @@ -0,0 +1,99 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" + +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; + +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_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...); + +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 new file mode 100644 index 00000000000000..e2603aae15dd04 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_macro.h @@ -0,0 +1,53 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#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 new file mode 100644 index 00000000000000..bd214ff87b5046 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_map.c @@ -0,0 +1,210 @@ +/* + * 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 + +#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_entry *)); + if (map->table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void ngtcp2_map_free(ngtcp2_map *map) { 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; + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + ngtcp2_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int ngtcp2_map_each(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry, *next; + for (entry = map->table[i]; entry;) { + next = entry->next; + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + entry = next; + } + } + 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; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + return h & (mod - 1); +} + +static int insert(ngtcp2_map_entry **table, uint32_t tablelen, + ngtcp2_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + ngtcp2_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(ngtcp2_map *map, uint32_t new_tablelen) { + uint32_t i; + ngtcp2_map_entry **new_table; + + new_table = + ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_entry *)); + if (new_table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + ngtcp2_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + ngtcp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +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 = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(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) { + uint32_t h; + ngtcp2_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { + uint32_t h; + ngtcp2_map_entry **dst; + + h = hash(key, map->tablelen); + + for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { + if ((*dst)->key != key) { + continue; + } + + *dst = (*dst)->next; + --map->size; + return 0; + } + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +void ngtcp2_map_clear(ngtcp2_map *map) { + uint32_t i; + + for (i = 0; i < map->tablelen; ++i) { + map->table[i] = 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 new file mode 100644 index 00000000000000..86f23a16a7e5dd --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_map.h @@ -0,0 +1,145 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.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_entry **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 new file mode 100644 index 00000000000000..2c036ad1634b05 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_mem.c @@ -0,0 +1,75 @@ +/* + * 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 new file mode 100644 index 00000000000000..cdecf8763a5f36 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_mem.h @@ -0,0 +1,43 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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_net.h b/deps/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 00000000000000..9c121ea7f21547 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,91 @@ +/* + * 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_NET_H +#define NGTCP2_NET_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#include + +#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 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 */ + +#endif /* NGTCP2_NET_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/lib/ngtcp2_path.c new file mode 100644 index 00000000000000..ebda947c6d6379 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_path.c @@ -0,0 +1,68 @@ +/* + * 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 + +#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 void *local_addr, + size_t local_addrlen, void *local_user_data, + const void *remote_addr, size_t remote_addrlen, + void *remote_user_data) { + ngtcp2_addr_init(&ps->path.local, ps->local_addrbuf, 0, local_user_data); + ngtcp2_addr_init(&ps->path.remote, 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, ps->local_addrbuf, 0, NULL); + ngtcp2_addr_init(&ps->path.remote, ps->remote_addrbuf, 0, NULL); +} diff --git a/deps/ngtcp2/lib/ngtcp2_path.h b/deps/ngtcp2/lib/ngtcp2_path.h new file mode 100644 index 00000000000000..1b2e2f87c8c780 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_path.h @@ -0,0 +1,63 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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_pipeack.c b/deps/ngtcp2/lib/ngtcp2_pipeack.c new file mode 100644 index 00000000000000..f44bdf07bbb485 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pipeack.c @@ -0,0 +1,155 @@ +/* + * 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_pipeack.h" + +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_rcvry.h" + +void ngtcp2_pipeack_init(ngtcp2_pipeack *pipeack, ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + + memset(pipeack, 0, sizeof(*pipeack)); + + /* Initialize value to UINT64_MAX so that it does not restrict CWND + growth. It serves as "undefined" value. */ + pipeack->value = UINT64_MAX; + pipeack->ts = ts; + pipeack->len = 1; + + sample = &pipeack->samples[pipeack->pos]; + sample->value = 0; + sample->ts = ts; +} + +static ngtcp2_duration get_rtt(const ngtcp2_rcvry_stat *rcs) { + if (rcs->smoothed_rtt == 0) { + return NGTCP2_DEFAULT_INITIAL_RTT; + } + return rcs->smoothed_rtt; +} + +/* + * compute_sampling_period returns pipeACK sampling period. + */ +static ngtcp2_duration compute_sampling_period(ngtcp2_duration rtt) { + return ngtcp2_max(rtt * 3, NGTCP2_SECONDS); +} + +/* + * compute_measurement_period returns the measurement period using + * pipeACK sampling period |speriod|. + */ +static ngtcp2_duration compute_measurement_period(ngtcp2_duration speriod) { + return speriod / NGTCP2_PIPEACK_NUM_SAMPLES; +} + +static void pipeack_update_sample(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + ngtcp2_duration rtt = get_rtt(rcs); + ngtcp2_duration speriod = compute_sampling_period(rtt); + ngtcp2_duration mperiod = compute_measurement_period(speriod); + + if (pipeack->len == 0) { + pipeack->len = 1; + + sample = &pipeack->samples[pipeack->pos]; + sample->ts = ts; + sample->value = acked_bytes; + + return; + } + + sample = &pipeack->samples[pipeack->pos]; + if (sample->ts + mperiod <= ts) { + ts = sample->ts + (ts - sample->ts) / mperiod * mperiod; + pipeack->pos = (pipeack->pos - 1) & (NGTCP2_PIPEACK_NUM_SAMPLES - 1); + + if (pipeack->len < NGTCP2_PIPEACK_NUM_SAMPLES) { + ++pipeack->len; + } + + sample = &pipeack->samples[pipeack->pos]; + sample->ts = ts; + sample->value = acked_bytes; + + return; + } + + sample->value = ngtcp2_max(sample->value, acked_bytes); +} + +void ngtcp2_pipeack_update(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts) { + uint64_t value; + ngtcp2_duration d = ts - pipeack->ts; + ngtcp2_duration rtt = get_rtt(rcs); + + pipeack->acked_bytes += acked_bytes; + + if (d < rtt) { + return; + } + + value = pipeack->acked_bytes * rtt / d; + pipeack->acked_bytes = 0; + pipeack->ts = ts; + + pipeack_update_sample(pipeack, value, rcs, ts); +} + +void ngtcp2_pipeack_update_value(ngtcp2_pipeack *pipeack, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + ngtcp2_duration rtt = get_rtt(rcs); + ngtcp2_duration speriod = compute_sampling_period(rtt); + ngtcp2_duration mperiod = compute_measurement_period(speriod); + size_t i; + uint64_t res = 0; + + for (i = pipeack->len; i > 0; --i) { + sample = &pipeack->samples[(pipeack->pos + i - 1) & + (NGTCP2_PIPEACK_NUM_SAMPLES - 1)]; + if (sample->ts + speriod <= ts) { + --pipeack->len; + continue; + } + if (sample->ts + mperiod > ts) { + break; + } + + res = ngtcp2_max(res, sample->value); + } + + if (res == 0) { + pipeack->value = UINT64_MAX; + } else { + pipeack->value = res; + } +} diff --git a/deps/ngtcp2/lib/ngtcp2_pipeack.h b/deps/ngtcp2/lib/ngtcp2_pipeack.h new file mode 100644 index 00000000000000..d7a34a936bbb57 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pipeack.h @@ -0,0 +1,89 @@ +/* + * 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_PIPEACK_H +#define NGTCP2_PIPEACK_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_pipeack_sample { + /* + * value is the maximum cumulative acknowledged bytes per RTT in + * this sample. + */ + uint64_t value; + /* ts is the time when this sample started. */ + ngtcp2_tstamp ts; +} ngtcp2_pipeack_sample; + +#define NGTCP2_PIPEACK_NUM_SAMPLES 4 + +/* + * ngtcp2_pipeack implements pipeACK measurement described in RFC + * 7661. + */ +typedef struct ngtcp2_pipeack { + /* value is the pre-computed maximum pipeACK value in the effective + sample period. */ + uint64_t value; + /* acked_bytes is the accumulated acked_bytes per RTT. */ + uint64_t acked_bytes; + /* ts is the timestamp when the accumulation to acked_bytes + started. */ + ngtcp2_tstamp ts; + /* pos points to the latest sample in samples. */ + size_t pos; + /* len is the number of effective from pos. samples is treated as + ring buffer and its index will wrap when pos + len goes beyond + NGTCP2_PIPEACK_NUM_SAMPLES - 1. */ + size_t len; + ngtcp2_pipeack_sample samples[NGTCP2_PIPEACK_NUM_SAMPLES]; +} ngtcp2_pipeack; + +/* + * ngtcp2_pipeack_init initializes the object pointed by |pipeack|. + */ +void ngtcp2_pipeack_init(ngtcp2_pipeack *pipeack, ngtcp2_tstamp ts); + +/* + * ngtcp2_pipeack_update notifies the acknowledged bytes |acked_bytes| + * to |pipeack|. + */ +void ngtcp2_pipeack_update(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts); + +/* + * ngtcp2_pipeack_update_value computes maximum pipeACK value. + * Samples which have timestamp <= |ts| - pipeACK sampling period are + * discarded. + */ +void ngtcp2_pipeack_update_value(ngtcp2_pipeack *pipeack, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts); + +#endif /* NGTCP2_PIPEACK_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/lib/ngtcp2_pkt.c new file mode 100644 index 00000000000000..059022bf2ca8d0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pkt.c @@ -0,0 +1,2252 @@ +/* + * 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 +#include +#include + +#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 = NULL; + hd->tokenlen = 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; + + 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; + } + + tokenlen = ngtcp2_get_varint(&ntokenlen, p); + len += tokenlen; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + 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 = (uint8_t *)token; + dest->tokenlen = tokenlen; + p += ntokenlen + tokenlen; + + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + case NGTCP2_PKT_RETRY: + dest->len = 0; + break; + default: + dest->len = ngtcp2_get_varint(&n, p); + 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->tokenlen) + hd->tokenlen; + } + + 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->tokenlen); + if (hd->tokenlen) { + p = ngtcp2_cpymem(p, hd->token, hd->tokenlen); + } + } + + 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: + 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; + + 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; + } + + datalen = ngtcp2_get_varint(&ndatalen, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + } 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; + + 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; + } + + num_blks = ngtcp2_get_varint(&nnum_blks, p); + len += num_blks * (1 + 1); + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + 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; + + 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; + } + + reasonlen = ngtcp2_get_varint(&nreasonlen, p); + len += reasonlen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + 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; + + 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; + } + + datalen = ngtcp2_get_varint(&ndatalen, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + 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; + + 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; + } + + datalen = ngtcp2_get_varint(&n, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_NEW_TOKEN; + dest->tokenlen = datalen; + p += n; + dest->token = p; + p += dest->tokenlen; + + 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->tokenlen) + fr->tokenlen; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_NEW_TOKEN; + + p = ngtcp2_put_varint(p, fr->tokenlen); + if (fr->tokenlen) { + p = ngtcp2_cpymem(p, fr->token, fr->tokenlen); + } + + 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 = payload; + dest->tokenlen = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN); + ngtcp2_cpymem(dest->tag, payload + dest->tokenlen, 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; +} + +static const uint8_t retry_key[] = + "\x4d\x32\xec\xdb\x2a\x21\x33\xc8\x41\xe4\x04\x3d\xf2\x7d\x44\x30"; +static const uint8_t retry_nonce[] = + "\x4d\x16\x11\xd0\x55\x13\xa5\x52\xc5\x87\xd5\x75"; + +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) { + 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, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(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) { + 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, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(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) { + len = ngtcp2_min(len, 4611686018427387903lu); + 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); + + if (left <= n) { + return (size_t)-1; + } + + left -= n; + + if (left > 8 + 1073741823 && len > 1073741823) { + len = ngtcp2_min(len, 4611686018427387903lu); + 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 (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 new file mode 100644 index 00000000000000..962218057afcca --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pkt.h @@ -0,0 +1,1118 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 255 + +/* 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 + +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_DURATION_TICK / + * 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; + size_t tokenlen; + const uint8_t *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 & + * ~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 & + * ~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); + +#endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/lib/ngtcp2_ppe.c new file mode 100644 index 00000000000000..e4aab22ca33f83 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ppe.c @@ -0,0 +1,230 @@ +/* + * 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 +#include + +#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->tokenlen) + hd->tokenlen; + } + 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, payload, payloadlen, cc->ckm->key.base, + 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_key->base, + 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 new file mode 100644 index 00000000000000..ca6b21dce0b41a --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ppe.h @@ -0,0 +1,153 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..5e1003d7942e53 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pq.c @@ -0,0 +1,164 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..b7e4a77ba5bb70 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pq.h @@ -0,0 +1,126 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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_psl.c b/deps/ngtcp2/lib/ngtcp2_psl.c new file mode 100644 index 00000000000000..54a8e894d503a3 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_psl.c @@ -0,0 +1,621 @@ +/* + * 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_psl.h" + +#include +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +int ngtcp2_psl_init(ngtcp2_psl *psl, const ngtcp2_mem *mem) { + ngtcp2_psl_blk *head; + + psl->mem = mem; + psl->head = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_psl_blk)); + if (!psl->head) { + return NGTCP2_ERR_NOMEM; + } + psl->front = psl->head; + psl->n = 0; + + head = psl->head; + + head->next = NULL; + head->n = 1; + head->leaf = 1; + head->nodes[0].range.begin = UINT64_MAX; + head->nodes[0].range.end = UINT64_MAX; + head->nodes[0].data = NULL; + + return 0; +} + +/* + * free_blk frees |blk| recursively. + */ +static void free_blk(ngtcp2_psl_blk *blk, const ngtcp2_mem *mem) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + free_blk(blk->nodes[i].blk, mem); + } + } + + ngtcp2_mem_free(mem, blk); +} + +void ngtcp2_psl_free(ngtcp2_psl *psl) { + if (!psl) { + return; + } + + free_blk(psl->head, psl->mem); +} + +/* + * psl_split_blk splits |blk| into 2 ngtcp2_psl_blk objects. The new + * ngtcp2_psl_blk is always the "right" block. + * + * It returns the pointer to the ngtcp2_psl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static ngtcp2_psl_blk *psl_split_blk(ngtcp2_psl *psl, ngtcp2_psl_blk *blk) { + ngtcp2_psl_blk *rblk; + + rblk = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, &blk->nodes[blk->n - rblk->n], + sizeof(ngtcp2_psl_node) * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGTCP2_PSL_MIN_NBLK); + assert(rblk->n >= NGTCP2_PSL_MIN_NBLK); + + return rblk; +} + +/* + * psl_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 psl_split_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_blk *lblk = blk->nodes[i].blk, *rblk; + + rblk = psl_split_blk(psl, lblk); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + memmove(&blk->nodes[i + 2], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + blk->nodes[i + 1].blk = rblk; + + ++blk->n; + + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + blk->nodes[i + 1].range = rblk->nodes[rblk->n - 1].range; + + return 0; +} + +/* + * psl_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 psl_split_head(ngtcp2_psl *psl) { + ngtcp2_psl_blk *rblk = NULL, *lblk, *nhead = NULL; + + rblk = psl_split_blk(psl, psl->head); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + lblk = psl->head; + + nhead = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (nhead == NULL) { + ngtcp2_mem_free(psl->mem, rblk); + return NGTCP2_ERR_NOMEM; + } + nhead->next = NULL; + nhead->n = 2; + nhead->leaf = 0; + + nhead->nodes[0].range = lblk->nodes[lblk->n - 1].range; + nhead->nodes[0].blk = lblk; + nhead->nodes[1].range = rblk->nodes[rblk->n - 1].range; + nhead->nodes[1].blk = rblk; + + psl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose range is |range| 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_PSL_MAX_NBLK. + */ +static void insert_node(ngtcp2_psl_blk *blk, size_t i, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_node *node; + + assert(blk->n < NGTCP2_PSL_MAX_NBLK); + + memmove(&blk->nodes[i + 1], &blk->nodes[i], + sizeof(ngtcp2_psl_node) * (blk->n - i)); + + node = &blk->nodes[i]; + node->range = *range; + node->data = data; + + ++blk->n; +} + +static int range_intersect(const ngtcp2_range *a, const ngtcp2_range *b) { + return ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end); +} + +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + int rv; + + if (blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_head(psl); + if (rv != 0) { + return rv; + } + blk = psl->head; + } + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + assert(!range_intersect(&node->range, range)); + + if (blk->leaf) { + insert_node(blk, i, range, data); + ++psl->n; + if (it) { + ngtcp2_psl_it_init(it, blk, i); + } + return 0; + } + + if (node->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i); + if (rv != 0) { + return rv; + } + if (node->range.begin < range->begin) { + node = &blk->nodes[i + 1]; + } + } + + blk = node->blk; + } +} + +/* + * remove_node removes the node included in |blk| at the index of |i|. + */ +static void remove_node(ngtcp2_psl_blk *blk, size_t i) { + memmove(&blk->nodes[i], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * psl_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 |psl| by 1. + * + * This function returns the pointer to the merged block. + */ +static ngtcp2_psl_blk *psl_merge_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, + size_t i) { + ngtcp2_psl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = blk->nodes[i].blk; + rblk = blk->nodes[i + 1].blk; + + assert(lblk->n + rblk->n < NGTCP2_PSL_MAX_NBLK); + + memcpy(&lblk->nodes[lblk->n], &rblk->nodes[0], + sizeof(ngtcp2_psl_node) * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + + ngtcp2_mem_free(psl->mem, rblk); + + if (psl->head == blk && blk->n == 2) { + ngtcp2_mem_free(psl->mem, psl->head); + psl->head = lblk; + } else { + remove_node(blk, i + 1); + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + } + + return lblk; +} + +/* + * psl_relocate_node replaces the key at the index |*pi| in + * *pblk->nodes with something other without violating contract. It + * might involve merging 2 nodes or moving a node to left or right. + * + * It assigns the index of the block in |*pblk| where the node is + * moved to |*pi|. If merging 2 nodes occurs and it becomes new head, + * the new head is assigned to |*pblk| and it still contains the key. + * The caller should handle this situation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_relocate_node(ngtcp2_psl *psl, ngtcp2_psl_blk **pblk, + size_t *pi) { + ngtcp2_psl_blk *blk = *pblk; + size_t i = *pi; + ngtcp2_psl_node *node = &blk->nodes[i]; + ngtcp2_psl_node *rnode = &blk->nodes[i + 1]; + size_t j; + int rv; + + assert(i + 1 < blk->n); + + if (node->blk->n == NGTCP2_PSL_MIN_NBLK && + node->blk->n + rnode->blk->n < NGTCP2_PSL_MAX_NBLK) { + j = node->blk->n - 1; + blk = psl_merge_node(psl, blk, i); + if (blk != psl->head) { + return 0; + } + *pblk = blk; + i = j; + if (blk->leaf) { + *pi = i; + return 0; + } + node = &blk->nodes[i]; + rnode = &blk->nodes[i + 1]; + + if (node->blk->n == NGTCP2_PSL_MIN_NBLK && + node->blk->n + rnode->blk->n < NGTCP2_PSL_MAX_NBLK) { + j = node->blk->n - 1; + blk = psl_merge_node(psl, blk, i); + assert(blk != psl->head); + *pi = j; + return 0; + } + } + + if (node->blk->n < rnode->blk->n) { + node->blk->nodes[node->blk->n] = rnode->blk->nodes[0]; + memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1], + sizeof(ngtcp2_psl_node) * (rnode->blk->n - 1)); + --rnode->blk->n; + ++node->blk->n; + node->range = node->blk->nodes[node->blk->n - 1].range; + *pi = i; + return 0; + } + + if (rnode->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i + 1); + if (rv != 0) { + return rv; + } + } + + memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0], + sizeof(ngtcp2_psl_node) * rnode->blk->n); + + rnode->blk->nodes[0] = node->blk->nodes[node->blk->n - 1]; + ++rnode->blk->n; + + --node->blk->n; + + node->range = node->blk->nodes[node->blk->n - 1].range; + + *pi = i + 1; + return 0; +} + +/* + * shift_left moves the first node in blk->nodes[i]->blk->nodes to + * blk->nodes[i - 1]->blk->nodes. + */ +static void shift_left(ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_node *lnode, *rnode; + + assert(i > 0); + + lnode = &blk->nodes[i - 1]; + rnode = &blk->nodes[i]; + + assert(lnode->blk->n < NGTCP2_PSL_MAX_NBLK); + assert(rnode->blk->n > NGTCP2_PSL_MIN_NBLK); + + lnode->blk->nodes[lnode->blk->n] = rnode->blk->nodes[0]; + lnode->range = lnode->blk->nodes[lnode->blk->n].range; + ++lnode->blk->n; + + --rnode->blk->n; + memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1], + sizeof(ngtcp2_psl_node) * rnode->blk->n); +} + +/* + * shift_right moves the last node in blk->nodes[i]->blk->nodes to + * blk->nodes[i + 1]->blk->nodes. + */ +static void shift_right(ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_node *lnode, *rnode; + + assert(i < blk->n - 1); + + lnode = &blk->nodes[i]; + rnode = &blk->nodes[i + 1]; + + assert(lnode->blk->n > NGTCP2_PSL_MIN_NBLK); + assert(rnode->blk->n < NGTCP2_PSL_MAX_NBLK); + + memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0], + sizeof(ngtcp2_psl_node) * rnode->blk->n); + ++rnode->blk->n; + rnode->blk->nodes[0] = lnode->blk->nodes[lnode->blk->n - 1]; + + --lnode->blk->n; + lnode->range = lnode->blk->nodes[lnode->blk->n - 1].range; +} + +int ngtcp2_psl_remove(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head, *lblk, *rblk; + ngtcp2_psl_node *node; + size_t i, j; + int rv; + + if (!blk->leaf && blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_head(psl); + if (rv != 0) { + return rv; + } + blk = psl->head; + } + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + if (blk->leaf) { + assert(i < blk->n); + remove_node(blk, i); + --psl->n; + if (it) { + if (blk->n == i) { + ngtcp2_psl_it_init(it, blk->next, 0); + } else { + ngtcp2_psl_it_init(it, blk, i); + } + } + return 0; + } + + if (node->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i); + if (rv != 0) { + return rv; + } + if (node->range.begin < range->begin) { + ++i; + node = &blk->nodes[i]; + } + } + + if (ngtcp2_range_eq(&node->range, range)) { + rv = psl_relocate_node(psl, &blk, &i); + if (rv != 0) { + return rv; + } + if (!blk->leaf) { + node = &blk->nodes[i]; + blk = node->blk; + } + } else if (node->blk->n == NGTCP2_PSL_MIN_NBLK) { + j = i == 0 ? 0 : i - 1; + + lblk = blk->nodes[j].blk; + rblk = blk->nodes[j + 1].blk; + + if (lblk->n + rblk->n < NGTCP2_PSL_MAX_NBLK) { + blk = psl_merge_node(psl, blk, j); + } else { + if (i == j) { + shift_left(blk, j + 1); + } else { + shift_right(blk, j); + } + blk = node->blk; + } + } else { + blk = node->blk; + } + } +} + +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin && + !range_intersect(&node->range, range); + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + ngtcp2_psl_it it = {blk, i}; + return it; + } + + blk = node->blk; + } +} + +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + assert(old_range->begin <= new_range->begin); + assert(new_range->end <= old_range->end); + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < old_range->begin; + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + assert(ngtcp2_range_eq(&node->range, old_range)); + node->range = *new_range; + return; + } + + if (ngtcp2_range_eq(&node->range, old_range)) { + node->range = *new_range; + } else { + assert(!range_intersect(&node->range, old_range)); + } + + blk = node->blk; + } +} + +static void psl_print(ngtcp2_psl *psl, const ngtcp2_psl_blk *blk, + size_t level) { + size_t i; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + fprintf(stderr, " [%" PRIu64 ", %" PRIu64 ")", blk->nodes[i].range.begin, + blk->nodes[i].range.end); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + psl_print(psl, blk->nodes[i].blk, level + 1); + } +} + +void ngtcp2_psl_print(ngtcp2_psl *psl) { psl_print(psl, psl->head, 0); } + +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl) { + ngtcp2_psl_it it = {psl->front, 0}; + return it; +} + +size_t ngtcp2_psl_len(ngtcp2_psl *psl) { return psl->n; } + +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, + size_t i) { + it->blk = blk; + it->i = i; +} + +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it) { + return it->blk->nodes[it->i].data; +} + +void ngtcp2_psl_it_next(ngtcp2_psl_it *it) { + assert(!ngtcp2_psl_it_end(it)); + + if (++it->i == it->blk->n) { + it->blk = it->blk->next; + it->i = 0; + } +} + +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it) { + ngtcp2_range end = {UINT64_MAX, UINT64_MAX}; + return ngtcp2_range_eq(&end, &it->blk->nodes[it->i].range); +} + +ngtcp2_range ngtcp2_psl_it_range(const ngtcp2_psl_it *it) { + return it->blk->nodes[it->i].range; +} diff --git a/deps/ngtcp2/lib/ngtcp2_psl.h b/deps/ngtcp2/lib/ngtcp2_psl.h new file mode 100644 index 00000000000000..f0310a87e41bab --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_psl.h @@ -0,0 +1,231 @@ +/* + * 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_PSL_H +#define NGTCP2_PSL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#include "ngtcp2_range.h" + +/* + * Skip List implementation inspired by + * https://github.com/jabr/olio/blob/master/skiplist.c + */ + +#define NGTCP2_PSL_DEGR 8 +/* NGTCP2_PSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGTCP2_PSL_MAX_NBLK (2 * NGTCP2_PSL_DEGR - 1) +/* NGTCP2_PSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGTCP2_PSL_MIN_NBLK (NGTCP2_PSL_DEGR - 1) + +struct ngtcp2_psl_node; +typedef struct ngtcp2_psl_node ngtcp2_psl_node; + +struct ngtcp2_psl_blk; +typedef struct ngtcp2_psl_blk ngtcp2_psl_blk; + +/* + * ngtcp2_psl_node is a node which contains either ngtcp2_psl_blk or + * opaque data. If a node is an internal node, it contains + * ngtcp2_psl_blk. Otherwise, it has data. The invariant is that the + * range of internal node dictates the maximum range in its + * descendants, and the corresponding leaf node must exist. + */ +struct ngtcp2_psl_node { + ngtcp2_range range; + union { + ngtcp2_psl_blk *blk; + void *data; + }; +}; + +/* + * ngtcp2_psl_blk contains ngtcp2_psl_node objects. + */ +struct ngtcp2_psl_blk { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_psl_blk *next; + /* 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; + ngtcp2_psl_node nodes[NGTCP2_PSL_MAX_NBLK]; +}; + +struct ngtcp2_psl_it; +typedef struct ngtcp2_psl_it ngtcp2_psl_it; + +/* + * ngtcp2_psl_it is a forward iterator to iterate nodes. + */ +struct ngtcp2_psl_it { + const ngtcp2_psl_blk *blk; + size_t i; +}; + +struct ngtcp2_psl; +typedef struct ngtcp2_psl ngtcp2_psl; + +/* + * ngtcp2_psl is a deterministic paged skip list. + */ +struct ngtcp2_psl { + /* head points to the root block. */ + ngtcp2_psl_blk *head; + /* front points to the first leaf block. */ + ngtcp2_psl_blk *front; + size_t n; + const ngtcp2_mem *mem; +}; + +/* + * ngtcp2_psl_init initializes |psl|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_init(ngtcp2_psl *psl, const ngtcp2_mem *mem); + +/* + * ngtcp2_psl_free frees resources allocated for |psl|. If |psl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |psl| itself. + */ +void ngtcp2_psl_free(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_insert inserts |range| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function assumes that the existing ranges do not intersect + * with |range|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data); + +/* + * ngtcp2_psl_remove removes the |range| from |psl|. It assumes such + * the range is included in |psl|. + * + * 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. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_remove(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range); + +/* + * ngtcp2_psl_update_range replaces the range of nodes which has + * |old_range| with |new_range|. |old_range| must include + * |new_range|. + */ +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range); + +/* + * ngtcp2_psl_lower_bound returns the iterator which points to the + * first node whose range intersects with |range|. If there is no + * such node, it returns the iterator which satisfies + * ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range); + +/* + * ngtcp2_psl_begin returns the iterator which points to the first + * node. If there is no node in |psl|, it returns the iterator which + * satisfies ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl); + +/* + * ngtcp2_psl_len returns the number of elements stored in |ksl|. + */ +size_t ngtcp2_psl_len(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_print prints its internal state in stderr. This + * function should be used for the debugging purpose only. + */ +void ngtcp2_psl_print(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_it_init initializes |it|. + */ +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, size_t i); + +/* + * ngtcp2_psl_it_get returns the data associated to the node which + * |it| points to. If this function is called when + * ngtcp2_psl_it_end(it) returns nonzero, it returns NULL. + */ +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_next advances the iterator by one. It is undefined + * if this function is called when ngtcp2_psl_it_end(it) returns + * nonzero. + */ +void ngtcp2_psl_it_next(ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_range returns the range of the node which |it| points + * to. It is OK to call this function when ngtcp2_psl_it_end(it) + * returns nonzero. In this case, this function returns {UINT64_MAX, + * UINT64_MAX}. + */ +ngtcp2_range ngtcp2_psl_it_range(const ngtcp2_psl_it *it); + +#endif /* NGTCP2_PSL_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/lib/ngtcp2_pv.c new file mode 100644 index 00000000000000..171f02dc43dd29 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pv.c @@ -0,0 +1,155 @@ +/* + * 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 + +#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 new file mode 100644 index 00000000000000..0f1734ec3e1bf5 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pv.h @@ -0,0 +1,163 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..2f6d2390c9afee --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_qlog.c @@ -0,0 +1,1202 @@ +/* + * 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 + +#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, 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, 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"} + */ +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 26 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "new_token")); + /* TODO Write token here */ + *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, 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 + 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 local) { + 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"), + local ? ngtcp2_vec_lit(&value, "local") + : ngtcp2_vec_lit(&value, "remote")); + *p++ = ','; + if (params->original_connection_id_present) { + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "original_connection_id"), + ¶ms->original_connection_id); + *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_packet_size"), + params->max_packet_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, buf, (size_t)(p - buf)); +} + +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_cc_stat *ccs) { + 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 (rcs->min_rtt != UINT64_MAX) { + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "min_rtt"), rcs->min_rtt); + *p++ = ','; + } + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "smoothed_rtt"), + rcs->smoothed_rtt); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "latest_rtt"), + rcs->latest_rtt); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "rtt_variance"), + rcs->rttvar); + *p++ = ','; + /* TODO max_ack_delay? */ + p = write_pair_number(p, ngtcp2_vec_lit(&name, "pto_count"), rcs->pto_count); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "congestion_window"), + ccs->cwnd); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "bytes_in_flight"), + ccs->bytes_in_flight); + if (ccs->ssthresh != UINT64_MAX) { + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "ssthresh"), ccs->ssthresh); + } + + *p++ = '}'; + *p++ = ']'; + *p++ = ','; + + qlog->write(qlog->user_data, 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, buf, (size_t)(p - buf)); +} diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/lib/ngtcp2_qlog.h new file mode 100644 index 00000000000000..160cacd2322ed7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_qlog.h @@ -0,0 +1,134 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 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. 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 local); + +/* + * ngtcp2_qlog_metrics_updated writes metrics_updated event of + * recovery category. + */ +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_cc_stat *ccs); + +/* + * 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 new file mode 100644 index 00000000000000..9379496b7d4b53 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_range.c @@ -0,0 +1,61 @@ +/* + * 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 new file mode 100644 index 00000000000000..96b9a54b28f2d6 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_range.h @@ -0,0 +1,80 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * 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 new file mode 100644 index 00000000000000..254c3c797a0301 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rcvry.h @@ -0,0 +1,44 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +/* 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 + +#define NGTCP2_DEFAULT_INITIAL_RTT (500 * NGTCP2_MILLISECONDS) + +#endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/lib/ngtcp2_ringbuf.c new file mode 100644 index 00000000000000..6f5e61a2d69bd0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ringbuf.c @@ -0,0 +1,106 @@ +/* + * 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 +#ifdef WIN32 +# include +#endif + +#include "ngtcp2_macro.h" + +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]; +} + +size_t ngtcp2_ringbuf_len(ngtcp2_ringbuf *rb) { return rb->len; } + +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 new file mode 100644 index 00000000000000..05e9a577f1948e --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ringbuf.h @@ -0,0 +1,110 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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. */ +size_t ngtcp2_ringbuf_len(ngtcp2_ringbuf *rb); + +/* 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 new file mode 100644 index 00000000000000..ab7f44ecb88cb7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rob.c @@ -0,0 +1,341 @@ +/* + * 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 +#include + +#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; + ngtcp2_ksl_key key; + + 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, + ngtcp2_ksl_key_ptr(&key, &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; + ngtcp2_ksl_key key; + + for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &d->range), d); + if (rv != 0) { + ngtcp2_rob_data_del(d, rob->mem); + return rv; + } + } + + n = ngtcp2_min(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; + ngtcp2_ksl_key key, old_key; + + it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, ngtcp2_ksl_key_ptr(&key, &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, ngtcp2_ksl_key_ptr(&key, &g->range)); + ngtcp2_rob_gap_del(g, rob->mem); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + 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, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&key, &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, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &r)); + g->range = r; + } + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + 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; + ngtcp2_ksl_key key, old_key; + + 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, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &r)); + g->range.begin = offset; + break; + } + ngtcp2_ksl_remove(&rob->gapksl, &it, ngtcp2_ksl_key_ptr(&key, &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, ngtcp2_ksl_key_ptr(&key, &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 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_ksl_key key; + 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, ngtcp2_ksl_key_ptr(&key, &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 new file mode 100644 index 00000000000000..c6a039ce408de6 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rob.h @@ -0,0 +1,203 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..c9c994e2bae16b --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rst.c @@ -0,0 +1,107 @@ +/* + * 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_rst_init(ngtcp2_rst *rst) { + ngtcp2_rs *rs = &rst->rs; + + rst->delivered = 0; + rst->delivered_ts = 0; + rst->first_sent_ts = 0; + rst->app_limited = 0; + + rs->delivery_rate = 0.; + 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_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_cc_stat *ccs) { + if (ccs->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, const ngtcp2_rcvry_stat *rcs) { + 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 < rcs->min_rtt) { + rs->interval = UINT64_MAX; + return 0; + } + + if (rs->interval) { + rs->delivery_rate = (double)rs->delivered / (double)rs->interval; + } + + return 1; +} + +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, const ngtcp2_cc_stat *ccs) { + (void)rst; + (void)ccs; + /* TODO Not implemented */ +} diff --git a/deps/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/lib/ngtcp2_rst.h new file mode 100644 index 00000000000000..b7137b79c957b8 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rst.h @@ -0,0 +1,69 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +struct ngtcp2_rtb_entry; +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; + +typedef struct ngtcp2_rs { + double delivery_rate; + 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; + +/* + * ngtcp2_rst implements delivery rate estimation described in + * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00 + */ +typedef struct ngtcp2_rst { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t app_limited; + ngtcp2_rs rs; +} ngtcp2_rst; + +void ngtcp2_rst_init(ngtcp2_rst *rst); + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_cc_stat *ccs); +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, const ngtcp2_rcvry_stat *rcs); +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, const ngtcp2_cc_stat *ccs); + +#endif /* NGTCP2_RST_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/lib/ngtcp2_rtb.c new file mode 100644 index 00000000000000..83936f2aa69013 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rtb.c @@ -0,0 +1,670 @@ +/* + * 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 +#include + +#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); +} + +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, frc); +} + +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { frc->next = 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_mem_free(mem, frc); + frc = next; + } +} + +static void frame_chain_insert(ngtcp2_frame_chain **pfrc, + ngtcp2_frame_chain *frc) { + ngtcp2_frame_chain **plast; + + assert(frc); + + for (plast = &frc; *plast; plast = &(*plast)->next) + ; + + *plast = *pfrc; + *pfrc = frc; +} + +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)->pktlen = pktlen; + (*pent)->flags = flags; + + 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 *lhs->i > *rhs->i; +} + +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, ngtcp2_rst *rst, + ngtcp2_default_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->loss_time = UINT64_MAX; + rtb->probe_pkt_left = 0; + rtb->crypto_level = crypto_level; + rtb->cc_pkt_num = 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_rst_on_pkt_sent(rtb->rst, ent, rtb->cc->ccs); + + assert(rtb->cc_pkt_num <= ent->hd.pkt_num); + + rtb->cc->ccs->bytes_in_flight += ent->pktlen; + + ngtcp2_rst_update_app_limited(rtb->rst, rtb->cc->ccs); + + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + ++rtb->num_ack_eliciting; + } +} + +static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + assert(rtb->num_ack_eliciting); + --rtb->num_ack_eliciting; + } + + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + assert(rtb->cc->ccs->bytes_in_flight >= ent->pktlen); + rtb->cc->ccs->bytes_in_flight -= ent->pktlen; + } +} + +static void rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rtb_entry *ent) { + 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_CRYPTO_TIMEOUT_RETRANSMITTED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " CRYPTO has already been retransmitted", + ent->hd.pkt_num); + } else if (ent->frc) { + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + /* TODO Reconsider the order of pfrc */ + frame_chain_insert(pfrc, ent->frc); + ent->frc = NULL; + } + } + + ngtcp2_rtb_entry_del(ent, rtb->mem); +} + +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { + int rv; + ngtcp2_ksl_key key; + + ent->next = NULL; + + rv = ngtcp2_ksl_insert(&rtb->ents, NULL, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num), ent); + if (rv != 0) { + return rv; + } + + rtb_on_add(rtb, ent); + + 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 *ent) { + ngtcp2_ksl_key key; + + ngtcp2_ksl_remove(&rtb->ents, it, ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + rtb_on_remove(rtb, ent); + ngtcp2_rtb_entry_del(ent, rtb->mem); +} + +static int rtb_call_acked_stream_offset(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; + size_t datalen; + ngtcp2_strm *crypto = rtb->crypto; + + for (frc = ent->frc; frc; frc = frc->next) { + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id); + if (strm == NULL) { + break; + } + prev_stream_offset = + ngtcp2_gaptr_first_gap_offset(&strm->tx.acked_offset); + rv = ngtcp2_gaptr_push( + &strm->tx.acked_offset, 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_gaptr_first_gap_offset(&strm->tx.acked_offset); + 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_gaptr_first_gap_offset(&crypto->tx.acked_offset); + rv = ngtcp2_gaptr_push( + &crypto->tx.acked_offset, 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_gaptr_first_gap_offset(&crypto->tx.acked_offset); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0) { + break; + } + + rv = conn->callbacks.acked_crypto_offset(conn, rtb->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; + } + } + return 0; +} + +static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts) { + ngtcp2_cc_pkt pkt; + + ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); + + ngtcp2_default_cc_on_pkt_acked( + rtb->cc, ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, ent->ts)); +} + +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, + 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_ksl_key key; + ngtcp2_ssize num_acked = 0; + int largest_pkt_acked = 0; + int rtt_updated = 0; + ngtcp2_tstamp largest_pkt_sent_ts = 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, + ngtcp2_ksl_key_ptr(&key, &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);) { + key = ngtcp2_ksl_it_key(&it); + if (min_ack <= *key.i && *key.i <= largest_ack) { + ent = ngtcp2_ksl_it_get(&it); + if (conn) { + rv = rtb_call_acked_stream_offset(rtb, ent, conn); + if (rv != 0) { + return rv; + } + if (largest_ack == *key.i) { + largest_pkt_sent_ts = ent->ts; + largest_pkt_acked = 1; + } + if (!rtt_updated && largest_pkt_acked && + (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + rtt_updated = 1; + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, + fr->ack_delay_unscaled); + } + rtb_on_pkt_acked(rtb, ent, ts); + /* At this point, it is invalided because rtb->ents might be + modified. */ + } + rtb_remove(rtb, &it, ent); + ++num_acked; + continue; + } + break; + } + + 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, + ngtcp2_ksl_key_ptr(&key, &largest_ack)); + if (ngtcp2_ksl_it_end(&it)) { + break; + } + + for (; !ngtcp2_ksl_it_end(&it);) { + key = ngtcp2_ksl_it_key(&it); + if (*key.i < min_ack) { + break; + } + ent = ngtcp2_ksl_it_get(&it); + if (conn) { + rv = rtb_call_acked_stream_offset(rtb, ent, conn); + if (rv != 0) { + return rv; + } + if (!rtt_updated && largest_pkt_acked && + (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + rtt_updated = 1; + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, + fr->ack_delay_unscaled); + } + rtb_on_pkt_acked(rtb, ent, ts); + } + rtb_remove(rtb, &it, ent); + ++num_acked; + } + + ++i; + } + + if (conn) { + ngtcp2_rst_on_ack_recv(rtb->rst, &conn->rcs); + ngtcp2_default_cc_on_ack_recv(rtb->cc, + rtt_updated ? conn->rcs.latest_rtt : 0, ts); + } + + return num_acked; +} + +static int rtb_pkt_lost(ngtcp2_rtb *rtb, const ngtcp2_rtb_entry *ent, + uint64_t loss_delay, ngtcp2_tstamp lost_send_time) { + if (ent->ts <= lost_send_time || + rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + NGTCP2_PKT_THRESHOLD) { + return 1; + } + + if (rtb->loss_time == UINT64_MAX) { + rtb->loss_time = ent->ts + loss_delay; + } else { + rtb->loss_time = ngtcp2_min(rtb->loss_time, ent->ts + loss_delay); + } + + return 0; +} + +/* + * rtb_compute_pkt_loss_delay computes delay until packet is + * considered lost in NGTCP2_MICROSECONDS resolution. + */ +static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_rcvry_stat *rcs) { + /* 9/8 is kTimeThreshold */ + ngtcp2_duration loss_delay = + ngtcp2_max(rcs->latest_rtt, rcs->smoothed_rtt) * 9 / 8; + return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY); +} + +void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rcvry_stat *rcs, 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_ksl_key key; + + rtb->loss_time = UINT64_MAX; + loss_delay = compute_pkt_loss_delay(rcs); + lost_send_time = ts - loss_delay; + + it = ngtcp2_ksl_lower_bound( + &rtb->ents, ngtcp2_ksl_key_ptr(&key, &rtb->largest_acked_tx_pkt_num)); + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + if (rtb_pkt_lost(rtb, ent, loss_delay, lost_send_time)) { + /* All entries from ent are considered to be lost. */ + latest_ts = oldest_ts = ent->ts; + last_lost_pkt_num = ent->hd.pkt_num; + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + + if (last_lost_pkt_num == ent->hd.pkt_num + 1) { + last_lost_pkt_num = ent->hd.pkt_num; + } else { + last_lost_pkt_num = -1; + } + + oldest_ts = ent->ts; + rtb_on_remove(rtb, ent); + rtb_on_pkt_lost(rtb, pfrc, ent); + } + + ngtcp2_default_cc_congestion_event(rtb->cc, latest_ts, ts); + + if (last_lost_pkt_num != -1) { + ngtcp2_default_cc_handle_persistent_congestion( + rtb->cc, latest_ts - oldest_ts, pto); + } + + return; + } + } +} + +void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + rtb_on_remove(rtb, ent); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + + rtb_on_pkt_lost(rtb, pfrc, ent); + } +} + +int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_frame_chain *nfrc; + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it gapit; + ngtcp2_range gap, range; + ngtcp2_crypto *fr; + int all_acked; + int rv; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + if ((ent->flags & NGTCP2_RTB_FLAG_PROBE) || + !(ent->flags & NGTCP2_RTB_FLAG_CRYPTO_PKT)) { + ngtcp2_ksl_it_next(&it); + continue; + } + + all_acked = 1; + + for (frc = ent->frc; frc; frc = frc->next) { + assert(frc->fr.type == NGTCP2_FRAME_CRYPTO); + + fr = &frc->fr.crypto; + + /* Don't resend CRYPTO frame if the whole region it contains has + been acknowledged */ + gapit = ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset, + fr->offset); + gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr; + + range.begin = fr->offset; + range.end = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); + range = ngtcp2_range_intersect(&range, &gap); + if (ngtcp2_range_len(&range) == 0) { + continue; + } + + all_acked = 0; + + if (!(ent->flags & NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED)) { + rv = ngtcp2_frame_chain_crypto_datacnt_new( + &nfrc, frc->fr.crypto.datacnt, rtb->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr = frc->fr; + ngtcp2_vec_copy(nfrc->fr.crypto.data, frc->fr.crypto.data, + frc->fr.crypto.datacnt); + + frame_chain_insert(pfrc, nfrc); + } + } + + if (all_acked) { + /* If the frames that ent contains have been acknowledged, + remove it from rtb. Otherwise crypto timer keeps firing. */ + rtb_on_remove(rtb, ent); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + ngtcp2_rtb_entry_del(ent, rtb->mem); + continue; + } + + ent->flags |= NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED; + + ngtcp2_ksl_it_next(&it); + } + + return 0; +} + +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { + return ngtcp2_ksl_len(&rtb->ents) == 0; +} + +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb) { + uint64_t bytes_in_flight = 0; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + for (it = ngtcp2_ksl_begin(&rtb->ents); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + bytes_in_flight += ent->pktlen; + } + } + + return bytes_in_flight; +} + +size_t ngtcp2_rtb_num_ack_eliciting(ngtcp2_rtb *rtb) { + return rtb->num_ack_eliciting; +} diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/lib/ngtcp2_rtb.h new file mode 100644 index 00000000000000..0e11cea5d4f0f0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rtb.h @@ -0,0 +1,329 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" + +struct ngtcp2_conn; +typedef struct ngtcp2_conn ngtcp2_conn; + +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_default_cc; +typedef struct ngtcp2_default_cc ngtcp2_default_cc; + +struct ngtcp2_strm; +typedef struct ngtcp2_strm ngtcp2_strm; + +struct ngtcp2_rst; +typedef struct ngtcp2_rst ngtcp2_rst; + +/* + * ngtcp2_frame_chain chains frames in a single packet. + */ +struct ngtcp2_frame_chain { + ngtcp2_frame_chain *next; + 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); + +/* + * 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_CRYPTO_PKT indicates that the entry includes + handshake CRYPTO frame. */ + NGTCP2_RTB_FLAG_CRYPTO_PKT = 0x02, + /* NGTCP2_RTB_FLAG_ACK_ELICITING indicates that the entry elicits + acknowledgement. */ + NGTCP2_RTB_FLAG_ACK_ELICITING = 0x04, + /* NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED indicates that the + CRYPTO frames have been retransmitted. */ + NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED = 0x08, +} 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; + /* 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_default_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; + size_t num_ack_eliciting; + ngtcp2_tstamp loss_time; + /* probe_pkt_left is the number of probe packet to send */ + size_t probe_pkt_left; + /* crypto_level is encryption level which |crypto| belongs to. */ + ngtcp2_crypto_level crypto_level; + /* cc_pkt_num is the smallest packet number that is contributed to + bytes_in_flight. */ + int64_t cc_pkt_num; +} ngtcp2_rtb; + +/* + * ngtcp2_rtb_init initializes |rtb|. + */ +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, ngtcp2_rst *rst, + ngtcp2_default_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_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 *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. + */ +void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * 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. + */ +void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc); + +/* + * ngtcp2_rtb_on_crypto_timeout copies all unacknowledged CRYPTO + * frames and links them to |*pfrc|. The affected ngtcp2_rtb_entry + * will have NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED set. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc); + +/* + * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. + */ +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_get_bytes_in_flight returns the sum of bytes in flight + * for the stored entries. + */ +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_num_ack_eliciting returns the number of ACK eliciting + * entries. + */ +size_t ngtcp2_rtb_num_ack_eliciting(ngtcp2_rtb *rtb); + +#endif /* NGTCP2_RTB_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/lib/ngtcp2_str.c new file mode 100644 index 00000000000000..b7502e3bd0a5af --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_str.c @@ -0,0 +1,98 @@ +/* + * 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 + +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 new file mode 100644 index 00000000000000..104b1f1a03873b --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_str.h @@ -0,0 +1,85 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..b6b315489dc798 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_strm.c @@ -0,0 +1,325 @@ +/* + * 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 +#include + +#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 *lhs->i < *rhs->i; +} + +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) { + int rv; + + strm->cycle = 0; + strm->tx.offset = 0; + strm->tx.max_offset = max_tx_offset; + 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; + + rv = ngtcp2_gaptr_init(&strm->tx.acked_offset, mem); + if (rv != 0) { + goto fail_gaptr_init; + } + + rv = ngtcp2_rob_init(&strm->rx.rob, 8 * 1024, mem); + if (rv != 0) { + goto fail_rob_init; + } + + rv = ngtcp2_ksl_init(&strm->tx.streamfrq, offset_less, sizeof(uint64_t), mem); + if (rv != 0) { + goto fail_tx_streamfrq_init; + } + + return 0; + +fail_tx_streamfrq_init: + ngtcp2_rob_free(&strm->rx.rob); +fail_rob_init: + ngtcp2_gaptr_free(&strm->tx.acked_offset); +fail_gaptr_init: + return rv; +} + +void ngtcp2_strm_free(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + if (strm == NULL) { + return; + } + + 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_rob_free(&strm->rx.rob); + ngtcp2_gaptr_free(&strm->tx.acked_offset); +} + +uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) { + 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) { + return ngtcp2_rob_push(&strm->rx.rob, offset, data, datalen); +} + +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { + strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR; +} + +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { + ngtcp2_ksl_key key; + + assert(frc->fr.type == NGTCP2_FRAME_STREAM); + assert(frc->next == NULL); + + return ngtcp2_ksl_insert(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &frc->fr.stream.offset), + frc); +} + +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; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key, old_key; + uint64_t old_offset; + + if (ngtcp2_ksl_len(&strm->tx.streamfrq) == 0) { + *pfrc = NULL; + return 0; + } + + it = ngtcp2_ksl_begin(&strm->tx.streamfrq); + frc = ngtcp2_ksl_it_get(&it); + 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) { + *pfrc = NULL; + return 0; + } + } + + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &fr->offset)); + + 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, + ngtcp2_ksl_key_ptr(&key, &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);) { + it = ngtcp2_ksl_begin(&strm->tx.streamfrq); + nfrc = ngtcp2_ksl_it_get(&it); + nfr = &nfrc->fr.stream; + + if (nfr->offset != fr->offset + datalen) { + assert(fr->offset + datalen < nfr->offset); + break; + } + + if (nfr->fin && nfr->datacnt == 0) { + fr->fin = 1; + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + 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) { + break; + } + + datalen += nmerged; + left -= nmerged; + + if (nfr->datacnt == 0) { + fr->fin = nfr->fin; + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + continue; + } + + old_offset = nfr->offset; + nfr->offset += nmerged; + + ngtcp2_ksl_update_key(&strm->tx.streamfrq, + ngtcp2_ksl_key_ptr(&old_key, &old_offset), + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + + 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; +} + +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + 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 ngtcp2_ksl_len(&strm->tx.streamfrq) == 0; +} + +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + 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) { + return ngtcp2_gaptr_first_gap_offset(&strm->tx.acked_offset) == + strm->tx.offset; +} diff --git a/deps/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/lib/ngtcp2_strm.h new file mode 100644 index 00000000000000..67d49e519e0eda --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_strm.h @@ -0,0 +1,219 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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_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; + /* 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; + /* 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_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_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); + +#endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/lib/ngtcp2_vec.c new file mode 100644 index 00000000000000..7a6f8afa051f20 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_vec.c @@ -0,0 +1,232 @@ +/* + * 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 +#include + +#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 new file mode 100644 index 00000000000000..077820a9efed23 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_vec.h @@ -0,0 +1,114 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +#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 new file mode 100644 index 00000000000000..40f3ae3f9eade4 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_version.c @@ -0,0 +1,39 @@ +/* + * 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 +#endif /* HAVE_CONFIG_H */ + +#include + +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 new file mode 100644 index 00000000000000..1da2cba93dc19d --- /dev/null +++ b/deps/ngtcp2/ngtcp2.gyp @@ -0,0 +1,116 @@ +{ + 'target_defaults': { + 'defines': [ + '_U_=' + ] + }, + 'targets': [ + { + 'target_name': 'ngtcp2', + 'type': 'static_library', + 'include_dirs': [ + 'lib/includes', + 'crypto/includes', + 'lib', + ], + 'defines': [ + 'BUILDING_NGTCP2', + 'NGTCP2_STATICLIB', + ], + 'dependencies': [ + '../openssl/openssl.gyp:openssl' + ], + 'conditions': [ + ['OS=="win"', { + 'defines': [ + 'WIN32', + '_WINDOWS', + 'HAVE_CONFIG_H', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'CompileAs': '1' + }, + }, + }], + ], + '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_net.h', + 'lib/ngtcp2_path.c', + 'lib/ngtcp2_path.h', + 'lib/ngtcp2_pipeack.c', + 'lib/ngtcp2_pipeack.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_psl.c', + 'lib/ngtcp2_psl.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/config/archs/BSD-x86/asm/configdata.pm b/deps/openssl/config/archs/BSD-x86/asm/configdata.pm index 60838a7aa0d12e..7ecca4ed5792cd 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9170,6 +9173,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9235,6 +9243,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14013,6 +14026,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14026,6 +14040,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14133,6 +14148,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14185,6 +14204,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -16086,3 +16109,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s index db384026d9d2d6..3e834e9b7b05a4 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s @@ -11,7 +11,7 @@ L_BF_encrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -19,7 +19,7 @@ L_BF_encrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 0 + # Round 0 movl 4(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -39,7 +39,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 8(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -59,7 +59,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 12(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -79,7 +79,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 16(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -99,7 +99,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 20(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -119,7 +119,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 24(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -139,7 +139,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 28(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -159,7 +159,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 32(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -179,7 +179,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 36(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -199,7 +199,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 40(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -219,7 +219,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 44(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -239,7 +239,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 48(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -259,7 +259,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 52(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -279,7 +279,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 56(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -299,7 +299,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 60(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -319,7 +319,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 64(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -336,7 +336,7 @@ L_BF_encrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (16) enc=1 + # Load parameter 0 (16) enc=1 movl 20(%esp),%eax xorl %ebx,%edi movl 68(%ebp),%edx @@ -360,7 +360,7 @@ L_BF_decrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -368,7 +368,7 @@ L_BF_decrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 16 + # Round 16 movl 64(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -388,7 +388,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 60(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -408,7 +408,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 56(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -428,7 +428,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 52(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -448,7 +448,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 48(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -468,7 +468,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 44(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -488,7 +488,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 40(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -508,7 +508,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 36(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -528,7 +528,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 32(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -548,7 +548,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 28(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -568,7 +568,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 24(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -588,7 +588,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 20(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -608,7 +608,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 16(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -628,7 +628,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 12(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -648,7 +648,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 8(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -668,7 +668,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 4(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -685,7 +685,7 @@ L_BF_decrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (1) enc=0 + # Load parameter 0 (1) enc=0 movl 20(%esp),%eax xorl %ebx,%edi movl (%ebp),%edx @@ -708,7 +708,7 @@ L_BF_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -719,9 +719,9 @@ L_BF_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 3 + # get and push parameter 3 movl 48(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s index 80c8db4d292bc9..c7c0a81c38b98a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s @@ -116,7 +116,7 @@ L001maw_non_sse2: jz L006maw_finish .align 4,0x90 L007maw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -125,7 +125,7 @@ L007maw_loop: adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -134,7 +134,7 @@ L007maw_loop: adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -143,7 +143,7 @@ L007maw_loop: adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -152,7 +152,7 @@ L007maw_loop: adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -161,7 +161,7 @@ L007maw_loop: adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -170,7 +170,7 @@ L007maw_loop: adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -179,7 +179,7 @@ L007maw_loop: adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ebp addl %esi,%eax @@ -199,7 +199,7 @@ L006maw_finish: jnz L008maw_finish2 jmp L009maw_end L008maw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -210,7 +210,7 @@ L008maw_finish2: movl %eax,(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -221,7 +221,7 @@ L008maw_finish2: movl %eax,4(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -232,7 +232,7 @@ L008maw_finish2: movl %eax,8(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -243,7 +243,7 @@ L008maw_finish2: movl %eax,12(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -254,7 +254,7 @@ L008maw_finish2: movl %eax,16(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -265,7 +265,7 @@ L008maw_finish2: movl %eax,20(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -328,56 +328,56 @@ L011mw_non_sse2: andl $4294967288,%ebp jz L013mw_finish L014mw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ecx addl %esi,%eax @@ -396,7 +396,7 @@ L013mw_finish: jnz L015mw_finish2 jmp L016mw_end L015mw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax @@ -405,7 +405,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ecx addl %esi,%eax @@ -414,7 +414,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ecx addl %esi,%eax @@ -423,7 +423,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ecx addl %esi,%eax @@ -432,7 +432,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ecx addl %esi,%eax @@ -441,7 +441,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ecx addl %esi,%eax @@ -450,7 +450,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ecx addl %esi,%eax @@ -503,42 +503,42 @@ L018sqr_non_sse2: andl $4294967288,%ebx jz L020sw_finish L021sw_loop: - # Round 0 + # Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) movl %edx,4(%esi) - # Round 4 + # Round 4 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) movl %edx,12(%esi) - # Round 8 + # Round 8 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) movl %edx,20(%esi) - # Round 12 + # Round 12 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) movl %edx,28(%esi) - # Round 16 + # Round 16 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) movl %edx,36(%esi) - # Round 20 + # Round 20 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) movl %edx,44(%esi) - # Round 24 + # Round 24 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) movl %edx,52(%esi) - # Round 28 + # Round 28 movl 28(%edi),%eax mull %eax movl %eax,56(%esi) @@ -552,49 +552,49 @@ L020sw_finish: movl 28(%esp),%ebx andl $7,%ebx jz L022sw_end - # Tail Round 0 + # Tail Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) decl %ebx movl %edx,4(%esi) jz L022sw_end - # Tail Round 1 + # Tail Round 1 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) decl %ebx movl %edx,12(%esi) jz L022sw_end - # Tail Round 2 + # Tail Round 2 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) decl %ebx movl %edx,20(%esi) jz L022sw_end - # Tail Round 3 + # Tail Round 3 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) decl %ebx movl %edx,28(%esi) jz L022sw_end - # Tail Round 4 + # Tail Round 4 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) decl %ebx movl %edx,36(%esi) jz L022sw_end - # Tail Round 5 + # Tail Round 5 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) decl %ebx movl %edx,44(%esi) jz L022sw_end - # Tail Round 6 + # Tail Round 6 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) @@ -633,7 +633,7 @@ L_bn_add_words_begin: andl $4294967288,%ebp jz L023aw_finish L024aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -642,7 +642,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -651,7 +651,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -660,7 +660,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -669,7 +669,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -678,7 +678,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -687,7 +687,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -696,7 +696,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx addl %eax,%ecx @@ -715,7 +715,7 @@ L023aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L025aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -726,7 +726,7 @@ L023aw_finish: decl %ebp movl %ecx,(%ebx) jz L025aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -737,7 +737,7 @@ L023aw_finish: decl %ebp movl %ecx,4(%ebx) jz L025aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -748,7 +748,7 @@ L023aw_finish: decl %ebp movl %ecx,8(%ebx) jz L025aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -759,7 +759,7 @@ L023aw_finish: decl %ebp movl %ecx,12(%ebx) jz L025aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -770,7 +770,7 @@ L023aw_finish: decl %ebp movl %ecx,16(%ebx) jz L025aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -781,7 +781,7 @@ L023aw_finish: decl %ebp movl %ecx,20(%ebx) jz L025aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -814,7 +814,7 @@ L_bn_sub_words_begin: andl $4294967288,%ebp jz L026aw_finish L027aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -823,7 +823,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -832,7 +832,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -841,7 +841,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -850,7 +850,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -859,7 +859,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -868,7 +868,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -877,7 +877,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -896,7 +896,7 @@ L026aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L028aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -907,7 +907,7 @@ L026aw_finish: decl %ebp movl %ecx,(%ebx) jz L028aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -918,7 +918,7 @@ L026aw_finish: decl %ebp movl %ecx,4(%ebx) jz L028aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -929,7 +929,7 @@ L026aw_finish: decl %ebp movl %ecx,8(%ebx) jz L028aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -940,7 +940,7 @@ L026aw_finish: decl %ebp movl %ecx,12(%ebx) jz L028aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -951,7 +951,7 @@ L026aw_finish: decl %ebp movl %ecx,16(%ebx) jz L028aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -962,7 +962,7 @@ L026aw_finish: decl %ebp movl %ecx,20(%ebx) jz L028aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -995,7 +995,7 @@ L_bn_sub_part_words_begin: andl $4294967288,%ebp jz L029aw_finish L030aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1004,7 +1004,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1013,7 +1013,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1022,7 +1022,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1031,7 +1031,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1040,7 +1040,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1049,7 +1049,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1058,7 +1058,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1077,7 +1077,7 @@ L029aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L031aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1091,7 +1091,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 1 + # Tail Round 1 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1105,7 +1105,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 2 + # Tail Round 2 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1119,7 +1119,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 3 + # Tail Round 3 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1133,7 +1133,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 4 + # Tail Round 4 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1147,7 +1147,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 5 + # Tail Round 5 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1161,7 +1161,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 6 + # Tail Round 6 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1180,14 +1180,14 @@ L031aw_end: cmpl $0,%ebp je L032pw_end jge L033pw_pos - # pw_neg + # pw_neg movl $0,%edx subl %ebp,%edx movl %edx,%ebp andl $4294967288,%ebp jz L034pw_neg_finish L035pw_neg_loop: - # dl<0 Round 0 + # dl<0 Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1196,7 +1196,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # dl<0 Round 1 + # dl<0 Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1205,7 +1205,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # dl<0 Round 2 + # dl<0 Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1214,7 +1214,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # dl<0 Round 3 + # dl<0 Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1223,7 +1223,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # dl<0 Round 4 + # dl<0 Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1232,7 +1232,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # dl<0 Round 5 + # dl<0 Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1241,7 +1241,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # dl<0 Round 6 + # dl<0 Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1250,7 +1250,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # dl<0 Round 7 + # dl<0 Round 7 movl $0,%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1270,7 +1270,7 @@ L034pw_neg_finish: subl %edx,%ebp andl $7,%ebp jz L032pw_end - # dl<0 Tail Round 0 + # dl<0 Tail Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1281,7 +1281,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,(%ebx) jz L032pw_end - # dl<0 Tail Round 1 + # dl<0 Tail Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1292,7 +1292,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,4(%ebx) jz L032pw_end - # dl<0 Tail Round 2 + # dl<0 Tail Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1303,7 +1303,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,8(%ebx) jz L032pw_end - # dl<0 Tail Round 3 + # dl<0 Tail Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1314,7 +1314,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,12(%ebx) jz L032pw_end - # dl<0 Tail Round 4 + # dl<0 Tail Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1325,7 +1325,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,16(%ebx) jz L032pw_end - # dl<0 Tail Round 5 + # dl<0 Tail Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1336,7 +1336,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,20(%ebx) jz L032pw_end - # dl<0 Tail Round 6 + # dl<0 Tail Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1350,42 +1350,42 @@ L033pw_pos: andl $4294967288,%ebp jz L036pw_pos_finish L037pw_pos_loop: - # dl>0 Round 0 + # dl>0 Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L038pw_nc0 - # dl>0 Round 1 + # dl>0 Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L039pw_nc1 - # dl>0 Round 2 + # dl>0 Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L040pw_nc2 - # dl>0 Round 3 + # dl>0 Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L041pw_nc3 - # dl>0 Round 4 + # dl>0 Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L042pw_nc4 - # dl>0 Round 5 + # dl>0 Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L043pw_nc5 - # dl>0 Round 6 + # dl>0 Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) jnc L044pw_nc6 - # dl>0 Round 7 + # dl>0 Round 7 movl 28(%esi),%ecx subl %eax,%ecx movl %ecx,28(%ebx) @@ -1399,49 +1399,49 @@ L036pw_pos_finish: movl 36(%esp),%ebp andl $7,%ebp jz L032pw_end - # dl>0 Tail Round 0 + # dl>0 Tail Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L046pw_tail_nc0 decl %ebp jz L032pw_end - # dl>0 Tail Round 1 + # dl>0 Tail Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L047pw_tail_nc1 decl %ebp jz L032pw_end - # dl>0 Tail Round 2 + # dl>0 Tail Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L048pw_tail_nc2 decl %ebp jz L032pw_end - # dl>0 Tail Round 3 + # dl>0 Tail Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L049pw_tail_nc3 decl %ebp jz L032pw_end - # dl>0 Tail Round 4 + # dl>0 Tail Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L050pw_tail_nc4 decl %ebp jz L032pw_end - # dl>0 Tail Round 5 + # dl>0 Tail Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L051pw_tail_nc5 decl %ebp jz L032pw_end - # dl>0 Tail Round 6 + # dl>0 Tail Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s index 0196f42b789164..d2608b28564f5a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s @@ -14,9 +14,9 @@ L_bn_mul_comba8_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -25,17 +25,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -44,24 +44,24 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -70,31 +70,31 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -103,38 +103,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 16(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[4]*b[0] + # mul a[4]*b[0] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[0]*b[4] + # mul a[0]*b[4] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -143,45 +143,45 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 20(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[5]*b[0] + # mul a[5]*b[0] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[4]*b[1] + # mul a[4]*b[1] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[1]*b[4] + # mul a[1]*b[4] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[0]*b[5] + # mul a[0]*b[5] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -190,52 +190,52 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 24(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[6]*b[0] + # mul a[6]*b[0] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[5]*b[1] + # mul a[5]*b[1] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[4]*b[2] + # mul a[4]*b[2] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[2]*b[4] + # mul a[2]*b[4] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[1]*b[5] + # mul a[1]*b[5] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[0]*b[6] + # mul a[0]*b[6] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -244,59 +244,59 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,24(%eax) movl 28(%esi),%eax - # saved r[6] - # ################## Calculate word 7 + # saved r[6] + # ################## Calculate word 7 xorl %ebx,%ebx - # mul a[7]*b[0] + # mul a[7]*b[0] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[6]*b[1] + # mul a[6]*b[1] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[5]*b[2] + # mul a[5]*b[2] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[4]*b[3] + # mul a[4]*b[3] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[3]*b[4] + # mul a[3]*b[4] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[2]*b[5] + # mul a[2]*b[5] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[1]*b[6] + # mul a[1]*b[6] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[0]*b[7] + # mul a[0]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -305,52 +305,52 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,28(%eax) movl 28(%esi),%eax - # saved r[7] - # ################## Calculate word 8 + # saved r[7] + # ################## Calculate word 8 xorl %ecx,%ecx - # mul a[7]*b[1] + # mul a[7]*b[1] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[6]*b[2] + # mul a[6]*b[2] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[5]*b[3] + # mul a[5]*b[3] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[4]*b[4] + # mul a[4]*b[4] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[3]*b[5] + # mul a[3]*b[5] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[2]*b[6] + # mul a[2]*b[6] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[1]*b[7] + # mul a[1]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -359,45 +359,45 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,32(%eax) movl 28(%esi),%eax - # saved r[8] - # ################## Calculate word 9 + # saved r[8] + # ################## Calculate word 9 xorl %ebp,%ebp - # mul a[7]*b[2] + # mul a[7]*b[2] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[6]*b[3] + # mul a[6]*b[3] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[5]*b[4] + # mul a[5]*b[4] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[4]*b[5] + # mul a[4]*b[5] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[3]*b[6] + # mul a[3]*b[6] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[2]*b[7] + # mul a[2]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -406,38 +406,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,36(%eax) movl 28(%esi),%eax - # saved r[9] - # ################## Calculate word 10 + # saved r[9] + # ################## Calculate word 10 xorl %ebx,%ebx - # mul a[7]*b[3] + # mul a[7]*b[3] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[6]*b[4] + # mul a[6]*b[4] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[5]*b[5] + # mul a[5]*b[5] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[4]*b[6] + # mul a[4]*b[6] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[3]*b[7] + # mul a[3]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -446,31 +446,31 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,40(%eax) movl 28(%esi),%eax - # saved r[10] - # ################## Calculate word 11 + # saved r[10] + # ################## Calculate word 11 xorl %ecx,%ecx - # mul a[7]*b[4] + # mul a[7]*b[4] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[6]*b[5] + # mul a[6]*b[5] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[5]*b[6] + # mul a[5]*b[6] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[4]*b[7] + # mul a[4]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -479,24 +479,24 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,44(%eax) movl 28(%esi),%eax - # saved r[11] - # ################## Calculate word 12 + # saved r[11] + # ################## Calculate word 12 xorl %ebp,%ebp - # mul a[7]*b[5] + # mul a[7]*b[5] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[6]*b[6] + # mul a[6]*b[6] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[5]*b[7] + # mul a[5]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -505,17 +505,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,48(%eax) movl 28(%esi),%eax - # saved r[12] - # ################## Calculate word 13 + # saved r[12] + # ################## Calculate word 13 xorl %ebx,%ebx - # mul a[7]*b[6] + # mul a[7]*b[6] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[6]*b[7] + # mul a[6]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -524,18 +524,18 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,52(%eax) movl 28(%esi),%eax - # saved r[13] - # ################## Calculate word 14 + # saved r[13] + # ################## Calculate word 14 xorl %ecx,%ecx - # mul a[7]*b[7] + # mul a[7]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%eax) - # saved r[14] - # save r[15] + # saved r[14] + # save r[15] movl %ebx,60(%eax) popl %ebx popl %ebp @@ -557,9 +557,9 @@ L_bn_mul_comba4_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -568,17 +568,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -587,24 +587,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -613,31 +613,31 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -646,24 +646,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 12(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -672,17 +672,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 12(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -691,18 +691,18 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 12(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%eax) - # saved r[6] - # save r[7] + # saved r[6] + # save r[7] movl %ecx,28(%eax) popl %ebx popl %ebp @@ -723,9 +723,9 @@ L_bn_sqr_comba8_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -733,10 +733,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -747,10 +747,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -759,7 +759,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -767,10 +767,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -780,7 +780,7 @@ L_bn_sqr_comba8_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -791,10 +791,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,12(%edi) movl (%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[4]*a[0] + # sqr a[4]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -804,7 +804,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -813,7 +813,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -821,10 +821,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 20(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[5]*a[0] + # sqr a[5]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -834,7 +834,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ecx movl 4(%esi),%edx - # sqr a[4]*a[1] + # sqr a[4]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -844,7 +844,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -855,10 +855,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,20(%edi) movl (%esi),%edx - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[6]*a[0] + # sqr a[6]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -868,7 +868,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[5]*a[1] + # sqr a[5]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -878,7 +878,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebp movl 8(%esi),%edx - # sqr a[4]*a[2] + # sqr a[4]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -887,7 +887,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 12(%esi),%eax adcl $0,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -895,10 +895,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,24(%edi) movl 28(%esi),%eax - # saved r[6] - # ############### Calculate word 7 + # saved r[6] + # ############### Calculate word 7 xorl %ebx,%ebx - # sqr a[7]*a[0] + # sqr a[7]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -908,7 +908,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[6]*a[1] + # sqr a[6]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -918,7 +918,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebx movl 8(%esi),%edx - # sqr a[5]*a[2] + # sqr a[5]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -928,7 +928,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebx movl 12(%esi),%edx - # sqr a[4]*a[3] + # sqr a[4]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -939,10 +939,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,28(%edi) movl 4(%esi),%edx - # saved r[7] - # ############### Calculate word 8 + # saved r[7] + # ############### Calculate word 8 xorl %ecx,%ecx - # sqr a[7]*a[1] + # sqr a[7]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -952,7 +952,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[6]*a[2] + # sqr a[6]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -962,7 +962,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ecx movl 12(%esi),%edx - # sqr a[5]*a[3] + # sqr a[5]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -971,7 +971,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 16(%esi),%eax adcl $0,%ecx - # sqr a[4]*a[4] + # sqr a[4]*a[4] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -979,10 +979,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,32(%edi) movl 28(%esi),%eax - # saved r[8] - # ############### Calculate word 9 + # saved r[8] + # ############### Calculate word 9 xorl %ebp,%ebp - # sqr a[7]*a[2] + # sqr a[7]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -992,7 +992,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebp movl 12(%esi),%edx - # sqr a[6]*a[3] + # sqr a[6]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1002,7 +1002,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 16(%esi),%edx - # sqr a[5]*a[4] + # sqr a[5]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1013,10 +1013,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,36(%edi) movl 12(%esi),%edx - # saved r[9] - # ############### Calculate word 10 + # saved r[9] + # ############### Calculate word 10 xorl %ebx,%ebx - # sqr a[7]*a[3] + # sqr a[7]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1026,7 +1026,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 16(%esi),%edx - # sqr a[6]*a[4] + # sqr a[6]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1035,7 +1035,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 20(%esi),%eax adcl $0,%ebx - # sqr a[5]*a[5] + # sqr a[5]*a[5] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1043,10 +1043,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,40(%edi) movl 28(%esi),%eax - # saved r[10] - # ############### Calculate word 11 + # saved r[10] + # ############### Calculate word 11 xorl %ecx,%ecx - # sqr a[7]*a[4] + # sqr a[7]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1056,7 +1056,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 20(%esi),%edx - # sqr a[6]*a[5] + # sqr a[6]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1067,10 +1067,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,44(%edi) movl 20(%esi),%edx - # saved r[11] - # ############### Calculate word 12 + # saved r[11] + # ############### Calculate word 12 xorl %ebp,%ebp - # sqr a[7]*a[5] + # sqr a[7]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1079,7 +1079,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 24(%esi),%eax adcl $0,%ebp - # sqr a[6]*a[6] + # sqr a[6]*a[6] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1087,10 +1087,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,48(%edi) movl 28(%esi),%eax - # saved r[12] - # ############### Calculate word 13 + # saved r[12] + # ############### Calculate word 13 xorl %ebx,%ebx - # sqr a[7]*a[6] + # sqr a[7]*a[6] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1100,16 +1100,16 @@ L_bn_sqr_comba8_begin: movl 28(%esi),%eax adcl $0,%ebx movl %ecx,52(%edi) - # saved r[13] - # ############### Calculate word 14 + # saved r[13] + # ############### Calculate word 14 xorl %ecx,%ecx - # sqr a[7]*a[7] + # sqr a[7]*a[7] mull %eax addl %eax,%ebp adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%edi) - # saved r[14] + # saved r[14] movl %ebx,60(%edi) popl %ebx popl %ebp @@ -1130,9 +1130,9 @@ L_bn_sqr_comba4_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1140,10 +1140,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1154,10 +1154,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1166,7 +1166,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -1174,10 +1174,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1187,7 +1187,7 @@ L_bn_sqr_comba4_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1198,10 +1198,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,12(%edi) movl 4(%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1210,7 +1210,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1218,10 +1218,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 12(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1231,16 +1231,16 @@ L_bn_sqr_comba4_begin: movl 12(%esi),%eax adcl $0,%ecx movl %ebp,20(%edi) - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%edi) - # saved r[6] + # saved r[6] movl %ecx,28(%edi) popl %ebx popl %ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h index feabbb00ba4bc3..500cd68ba0669e 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:16 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:31 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s index e4f05f09e3e588..d2c370231dfa7f 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s @@ -9,7 +9,7 @@ L_fcrypt_body_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words xorl %edi,%edi xorl %esi,%esi call L000PIC_me_up @@ -22,7 +22,7 @@ L000PIC_me_up: pushl $25 L001start: - # Round 0 + # Round 0 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -72,7 +72,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 1 + # Round 1 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -122,7 +122,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 2 + # Round 2 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -172,7 +172,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 3 + # Round 3 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -222,7 +222,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 4 + # Round 4 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -272,7 +272,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 5 + # Round 5 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -322,7 +322,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 6 + # Round 6 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -372,7 +372,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 7 + # Round 7 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -422,7 +422,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 8 + # Round 8 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -472,7 +472,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 9 + # Round 9 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -522,7 +522,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 10 + # Round 10 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -572,7 +572,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 11 + # Round 11 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -622,7 +622,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 12 + # Round 12 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -672,7 +672,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 13 + # Round 13 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -722,7 +722,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 14 + # Round 14 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -772,7 +772,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 15 + # Round 15 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -829,7 +829,7 @@ L001start: movl %ebx,(%esp) jnz L001start - # FP + # FP movl 28(%esp),%edx rorl $1,%edi movl %esi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s index 14d61fda5f9598..5ddd0ed7311ec1 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s @@ -4,7 +4,7 @@ .align 4 __x86_DES_encrypt: pushl %ecx - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -33,7 +33,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -62,7 +62,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -91,7 +91,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -120,7 +120,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -149,7 +149,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -178,7 +178,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -207,7 +207,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -236,7 +236,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -265,7 +265,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -294,7 +294,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -323,7 +323,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -352,7 +352,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -381,7 +381,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -410,7 +410,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -439,7 +439,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -474,7 +474,7 @@ __x86_DES_encrypt: .align 4 __x86_DES_decrypt: pushl %ecx - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -503,7 +503,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -532,7 +532,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -561,7 +561,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -590,7 +590,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -619,7 +619,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -648,7 +648,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -677,7 +677,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -706,7 +706,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -735,7 +735,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -764,7 +764,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -793,7 +793,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -822,7 +822,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -851,7 +851,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -880,7 +880,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -909,7 +909,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -948,7 +948,7 @@ L_DES_encrypt1_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%esi xorl %ecx,%ecx pushl %ebx @@ -957,7 +957,7 @@ L_DES_encrypt1_begin: movl 28(%esp),%ebx movl 4(%esi),%edi - # IP + # IP roll $4,%eax movl %eax,%esi xorl %edi,%eax @@ -1007,7 +1007,7 @@ L001decrypt: call __x86_DES_decrypt L002done: - # FP + # FP movl 20(%esp),%edx rorl $1,%esi movl %edi,%eax @@ -1060,7 +1060,7 @@ L_DES_encrypt2_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%eax xorl %ecx,%ecx pushl %ebx @@ -1083,7 +1083,7 @@ L004decrypt: call __x86_DES_decrypt L005done: - # Fixup + # Fixup rorl $3,%edi movl 20(%esp),%eax rorl $3,%esi @@ -1105,12 +1105,12 @@ L_DES_encrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1169,7 +1169,7 @@ L_DES_encrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1225,12 +1225,12 @@ L_DES_decrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1289,7 +1289,7 @@ L_DES_decrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1345,7 +1345,7 @@ L_DES_ncbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1356,11 +1356,11 @@ L_DES_ncbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 pushl %ecx - # get and push parameter 3 + # get and push parameter 3 movl 52(%esp),%eax pushl %eax pushl %ebx @@ -1524,7 +1524,7 @@ L_DES_ede3_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 6 + # getting iv ptr from parameter 6 movl 44(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1535,15 +1535,15 @@ L_DES_ede3_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 7 + # getting encrypt flag from parameter 7 movl 64(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 movl 56(%esp),%eax pushl %eax - # get and push parameter 4 + # get and push parameter 4 movl 56(%esp),%eax pushl %eax - # get and push parameter 3 + # get and push parameter 3 movl 56(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s index 91e941d1b41b3a..2f4efe555caebb 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s @@ -21,10 +21,10 @@ L_md5_block_asm_data_order_begin: movl 12(%edi),%edx L000start: - # R0 section + # R0 section movl %ecx,%edi movl (%esi),%ebp - # R0 0 + # R0 0 xorl %edx,%edi andl %ebx,%edi leal 3614090360(%eax,%ebp,1),%eax @@ -34,7 +34,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 1 + # R0 1 xorl %ecx,%edi andl %eax,%edi leal 3905402710(%edx,%ebp,1),%edx @@ -44,7 +44,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 2 + # R0 2 xorl %ebx,%edi andl %edx,%edi leal 606105819(%ecx,%ebp,1),%ecx @@ -54,7 +54,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 3 + # R0 3 xorl %eax,%edi andl %ecx,%edi leal 3250441966(%ebx,%ebp,1),%ebx @@ -64,7 +64,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 4 + # R0 4 xorl %edx,%edi andl %ebx,%edi leal 4118548399(%eax,%ebp,1),%eax @@ -74,7 +74,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 5 + # R0 5 xorl %ecx,%edi andl %eax,%edi leal 1200080426(%edx,%ebp,1),%edx @@ -84,7 +84,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 6 + # R0 6 xorl %ebx,%edi andl %edx,%edi leal 2821735955(%ecx,%ebp,1),%ecx @@ -94,7 +94,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 7 + # R0 7 xorl %eax,%edi andl %ecx,%edi leal 4249261313(%ebx,%ebp,1),%ebx @@ -104,7 +104,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 8 + # R0 8 xorl %edx,%edi andl %ebx,%edi leal 1770035416(%eax,%ebp,1),%eax @@ -114,7 +114,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 9 + # R0 9 xorl %ecx,%edi andl %eax,%edi leal 2336552879(%edx,%ebp,1),%edx @@ -124,7 +124,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 10 + # R0 10 xorl %ebx,%edi andl %edx,%edi leal 4294925233(%ecx,%ebp,1),%ecx @@ -134,7 +134,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 11 + # R0 11 xorl %eax,%edi andl %ecx,%edi leal 2304563134(%ebx,%ebp,1),%ebx @@ -144,7 +144,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 12 + # R0 12 xorl %edx,%edi andl %ebx,%edi leal 1804603682(%eax,%ebp,1),%eax @@ -154,7 +154,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 13 + # R0 13 xorl %ecx,%edi andl %eax,%edi leal 4254626195(%edx,%ebp,1),%edx @@ -164,7 +164,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 14 + # R0 14 xorl %ebx,%edi andl %edx,%edi leal 2792965006(%ecx,%ebp,1),%ecx @@ -174,7 +174,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 15 + # R0 15 xorl %eax,%edi andl %ecx,%edi leal 1236535329(%ebx,%ebp,1),%ebx @@ -185,8 +185,8 @@ L000start: movl %ecx,%edi addl %ecx,%ebx - # R1 section - # R1 16 + # R1 section + # R1 16 xorl %ebx,%edi andl %edx,%edi leal 4129170786(%eax,%ebp,1),%eax @@ -196,7 +196,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 17 + # R1 17 xorl %eax,%edi andl %ecx,%edi leal 3225465664(%edx,%ebp,1),%edx @@ -206,7 +206,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 18 + # R1 18 xorl %edx,%edi andl %ebx,%edi leal 643717713(%ecx,%ebp,1),%ecx @@ -216,7 +216,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 19 + # R1 19 xorl %ecx,%edi andl %eax,%edi leal 3921069994(%ebx,%ebp,1),%ebx @@ -226,7 +226,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 20 + # R1 20 xorl %ebx,%edi andl %edx,%edi leal 3593408605(%eax,%ebp,1),%eax @@ -236,7 +236,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 21 + # R1 21 xorl %eax,%edi andl %ecx,%edi leal 38016083(%edx,%ebp,1),%edx @@ -246,7 +246,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 22 + # R1 22 xorl %edx,%edi andl %ebx,%edi leal 3634488961(%ecx,%ebp,1),%ecx @@ -256,7 +256,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 23 + # R1 23 xorl %ecx,%edi andl %eax,%edi leal 3889429448(%ebx,%ebp,1),%ebx @@ -266,7 +266,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 24 + # R1 24 xorl %ebx,%edi andl %edx,%edi leal 568446438(%eax,%ebp,1),%eax @@ -276,7 +276,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 25 + # R1 25 xorl %eax,%edi andl %ecx,%edi leal 3275163606(%edx,%ebp,1),%edx @@ -286,7 +286,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 26 + # R1 26 xorl %edx,%edi andl %ebx,%edi leal 4107603335(%ecx,%ebp,1),%ecx @@ -296,7 +296,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 27 + # R1 27 xorl %ecx,%edi andl %eax,%edi leal 1163531501(%ebx,%ebp,1),%ebx @@ -306,7 +306,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 28 + # R1 28 xorl %ebx,%edi andl %edx,%edi leal 2850285829(%eax,%ebp,1),%eax @@ -316,7 +316,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 29 + # R1 29 xorl %eax,%edi andl %ecx,%edi leal 4243563512(%edx,%ebp,1),%edx @@ -326,7 +326,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 30 + # R1 30 xorl %edx,%edi andl %ebx,%edi leal 1735328473(%ecx,%ebp,1),%ecx @@ -336,7 +336,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 31 + # R1 31 xorl %ecx,%edi andl %eax,%edi leal 2368359562(%ebx,%ebp,1),%ebx @@ -347,8 +347,8 @@ L000start: roll $20,%ebx addl %ecx,%ebx - # R2 section - # R2 32 + # R2 section + # R2 32 xorl %edx,%edi xorl %ebx,%edi leal 4294588738(%eax,%ebp,1),%eax @@ -356,7 +356,7 @@ L000start: movl 32(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 33 + # R2 33 addl %ebx,%eax xorl %ecx,%edi leal 2272392833(%edx,%ebp,1),%edx @@ -366,7 +366,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 34 + # R2 34 xorl %ebx,%edi xorl %edx,%edi leal 1839030562(%ecx,%ebp,1),%ecx @@ -374,7 +374,7 @@ L000start: movl 56(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 35 + # R2 35 addl %edx,%ecx xorl %eax,%edi leal 4259657740(%ebx,%ebp,1),%ebx @@ -384,7 +384,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 36 + # R2 36 xorl %edx,%edi xorl %ebx,%edi leal 2763975236(%eax,%ebp,1),%eax @@ -392,7 +392,7 @@ L000start: movl 16(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 37 + # R2 37 addl %ebx,%eax xorl %ecx,%edi leal 1272893353(%edx,%ebp,1),%edx @@ -402,7 +402,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 38 + # R2 38 xorl %ebx,%edi xorl %edx,%edi leal 4139469664(%ecx,%ebp,1),%ecx @@ -410,7 +410,7 @@ L000start: movl 40(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 39 + # R2 39 addl %edx,%ecx xorl %eax,%edi leal 3200236656(%ebx,%ebp,1),%ebx @@ -420,7 +420,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 40 + # R2 40 xorl %edx,%edi xorl %ebx,%edi leal 681279174(%eax,%ebp,1),%eax @@ -428,7 +428,7 @@ L000start: movl (%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 41 + # R2 41 addl %ebx,%eax xorl %ecx,%edi leal 3936430074(%edx,%ebp,1),%edx @@ -438,7 +438,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 42 + # R2 42 xorl %ebx,%edi xorl %edx,%edi leal 3572445317(%ecx,%ebp,1),%ecx @@ -446,7 +446,7 @@ L000start: movl 24(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 43 + # R2 43 addl %edx,%ecx xorl %eax,%edi leal 76029189(%ebx,%ebp,1),%ebx @@ -456,7 +456,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 44 + # R2 44 xorl %edx,%edi xorl %ebx,%edi leal 3654602809(%eax,%ebp,1),%eax @@ -464,7 +464,7 @@ L000start: movl 48(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 45 + # R2 45 addl %ebx,%eax xorl %ecx,%edi leal 3873151461(%edx,%ebp,1),%edx @@ -474,7 +474,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 46 + # R2 46 xorl %ebx,%edi xorl %edx,%edi leal 530742520(%ecx,%ebp,1),%ecx @@ -482,7 +482,7 @@ L000start: movl 8(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 47 + # R2 47 addl %edx,%ecx xorl %eax,%edi leal 3299628645(%ebx,%ebp,1),%ebx @@ -493,8 +493,8 @@ L000start: roll $23,%ebx addl %ecx,%ebx - # R3 section - # R3 48 + # R3 section + # R3 48 xorl %edx,%edi orl %ebx,%edi leal 4096336452(%eax,%ebp,1),%eax @@ -505,7 +505,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 49 + # R3 49 orl %eax,%edi leal 1126891415(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -515,7 +515,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 50 + # R3 50 orl %edx,%edi leal 2878612391(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -525,7 +525,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 51 + # R3 51 orl %ecx,%edi leal 4237533241(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -535,7 +535,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 52 + # R3 52 orl %ebx,%edi leal 1700485571(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -545,7 +545,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 53 + # R3 53 orl %eax,%edi leal 2399980690(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -555,7 +555,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 54 + # R3 54 orl %edx,%edi leal 4293915773(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -565,7 +565,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 55 + # R3 55 orl %ecx,%edi leal 2240044497(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -575,7 +575,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 56 + # R3 56 orl %ebx,%edi leal 1873313359(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -585,7 +585,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 57 + # R3 57 orl %eax,%edi leal 4264355552(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -595,7 +595,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 58 + # R3 58 orl %edx,%edi leal 2734768916(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -605,7 +605,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 59 + # R3 59 orl %ecx,%edi leal 1309151649(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -615,7 +615,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 60 + # R3 60 orl %ebx,%edi leal 4149444226(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -625,7 +625,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 61 + # R3 61 orl %eax,%edi leal 3174756917(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -635,7 +635,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 62 + # R3 62 orl %edx,%edi leal 718787259(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -645,7 +645,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 63 + # R3 63 orl %ecx,%edi leal 3951481745(%ebx,%ebp,1),%ebx xorl %edx,%edi diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s index 9484963b97fdc7..17603e38536262 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s @@ -51,7 +51,7 @@ L000start: movl %edi,%eax movl 12(%edx),%ebx movl 16(%edx),%ebp - # 0 + # 0 xorl %ebx,%eax movl (%esp),%edx xorl %esi,%eax @@ -61,7 +61,7 @@ L000start: movl %esi,%eax roll $11,%ecx addl %ebp,%ecx - # 1 + # 1 xorl %edi,%eax movl 4(%esp),%edx xorl %ecx,%eax @@ -72,7 +72,7 @@ L000start: xorl %esi,%eax roll $14,%ebp addl %ebx,%ebp - # 2 + # 2 movl 8(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -81,7 +81,7 @@ L000start: movl %ebp,%eax roll $15,%ebx addl %edi,%ebx - # 3 + # 3 xorl %ecx,%eax movl 12(%esp),%edx xorl %ebx,%eax @@ -92,7 +92,7 @@ L000start: xorl %ebp,%eax roll $12,%edi addl %esi,%edi - # 4 + # 4 movl 16(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -101,7 +101,7 @@ L000start: movl %edi,%eax roll $5,%esi addl %ecx,%esi - # 5 + # 5 xorl %ebx,%eax movl 20(%esp),%edx xorl %esi,%eax @@ -112,7 +112,7 @@ L000start: xorl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 6 + # 6 movl 24(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -121,7 +121,7 @@ L000start: movl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 7 + # 7 xorl %esi,%eax movl 28(%esp),%edx xorl %ebp,%eax @@ -132,7 +132,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 8 + # 8 movl 32(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -141,7 +141,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 9 + # 9 xorl %ebp,%eax movl 36(%esp),%edx xorl %edi,%eax @@ -152,7 +152,7 @@ L000start: xorl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 10 + # 10 movl 40(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -161,7 +161,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 11 + # 11 xorl %edi,%eax movl 44(%esp),%edx xorl %ecx,%eax @@ -172,7 +172,7 @@ L000start: xorl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 12 + # 12 movl 48(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -181,7 +181,7 @@ L000start: movl %ebp,%eax roll $6,%ebx addl %edi,%ebx - # 13 + # 13 xorl %ecx,%eax movl 52(%esp),%edx xorl %ebx,%eax @@ -192,7 +192,7 @@ L000start: xorl %ebp,%eax roll $7,%edi addl %esi,%edi - # 14 + # 14 movl 56(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -201,7 +201,7 @@ L000start: movl %edi,%eax roll $9,%esi addl %ecx,%esi - # 15 + # 15 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -212,7 +212,7 @@ L000start: movl 28(%esp),%edx roll $8,%ecx addl %ebp,%ecx - # 16 + # 16 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -225,7 +225,7 @@ L000start: movl $-1,%edx roll $7,%ebp addl %ebx,%ebp - # 17 + # 17 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -238,7 +238,7 @@ L000start: movl $-1,%eax roll $6,%ebx addl %edi,%ebx - # 18 + # 18 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -251,7 +251,7 @@ L000start: movl $-1,%edx roll $8,%edi addl %esi,%edi - # 19 + # 19 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -264,7 +264,7 @@ L000start: movl $-1,%eax roll $13,%esi addl %ecx,%esi - # 20 + # 20 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -277,7 +277,7 @@ L000start: movl $-1,%edx roll $11,%ecx addl %ebp,%ecx - # 21 + # 21 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -290,7 +290,7 @@ L000start: movl $-1,%eax roll $9,%ebp addl %ebx,%ebp - # 22 + # 22 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -303,7 +303,7 @@ L000start: movl $-1,%edx roll $7,%ebx addl %edi,%ebx - # 23 + # 23 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -316,7 +316,7 @@ L000start: movl $-1,%eax roll $15,%edi addl %esi,%edi - # 24 + # 24 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -329,7 +329,7 @@ L000start: movl $-1,%edx roll $7,%esi addl %ecx,%esi - # 25 + # 25 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -342,7 +342,7 @@ L000start: movl $-1,%eax roll $12,%ecx addl %ebp,%ecx - # 26 + # 26 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -355,7 +355,7 @@ L000start: movl $-1,%edx roll $15,%ebp addl %ebx,%ebp - # 27 + # 27 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -368,7 +368,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 28 + # 28 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -381,7 +381,7 @@ L000start: movl $-1,%edx roll $11,%edi addl %esi,%edi - # 29 + # 29 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -394,7 +394,7 @@ L000start: movl $-1,%eax roll $7,%esi addl %ecx,%esi - # 30 + # 30 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -407,7 +407,7 @@ L000start: movl $-1,%edx roll $13,%ecx addl %ebp,%ecx - # 31 + # 31 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -420,7 +420,7 @@ L000start: subl %ecx,%edx roll $12,%ebp addl %ebx,%ebp - # 32 + # 32 movl 12(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -431,7 +431,7 @@ L000start: subl %ebp,%eax roll $11,%ebx addl %edi,%ebx - # 33 + # 33 movl 40(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -442,7 +442,7 @@ L000start: subl %ebx,%edx roll $13,%edi addl %esi,%edi - # 34 + # 34 movl 56(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -453,7 +453,7 @@ L000start: subl %edi,%eax roll $6,%esi addl %ecx,%esi - # 35 + # 35 movl 16(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -464,7 +464,7 @@ L000start: subl %esi,%edx roll $7,%ecx addl %ebp,%ecx - # 36 + # 36 movl 36(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -475,7 +475,7 @@ L000start: subl %ecx,%eax roll $14,%ebp addl %ebx,%ebp - # 37 + # 37 movl 60(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -486,7 +486,7 @@ L000start: subl %ebp,%edx roll $9,%ebx addl %edi,%ebx - # 38 + # 38 movl 32(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -497,7 +497,7 @@ L000start: subl %ebx,%eax roll $13,%edi addl %esi,%edi - # 39 + # 39 movl 4(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -508,7 +508,7 @@ L000start: subl %edi,%edx roll $15,%esi addl %ecx,%esi - # 40 + # 40 movl 8(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -519,7 +519,7 @@ L000start: subl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 41 + # 41 movl 28(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -530,7 +530,7 @@ L000start: subl %ecx,%edx roll $8,%ebp addl %ebx,%ebp - # 42 + # 42 movl (%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -541,7 +541,7 @@ L000start: subl %ebp,%eax roll $13,%ebx addl %edi,%ebx - # 43 + # 43 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -552,7 +552,7 @@ L000start: subl %ebx,%edx roll $6,%edi addl %esi,%edi - # 44 + # 44 movl 52(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -563,7 +563,7 @@ L000start: subl %edi,%eax roll $5,%esi addl %ecx,%esi - # 45 + # 45 movl 44(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -574,7 +574,7 @@ L000start: subl %esi,%edx roll $12,%ecx addl %ebp,%ecx - # 46 + # 46 movl 20(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -585,7 +585,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 47 + # 47 movl 48(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -596,7 +596,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 48 + # 48 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -609,7 +609,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 49 + # 49 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -622,7 +622,7 @@ L000start: movl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 50 + # 50 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -635,7 +635,7 @@ L000start: movl %edi,%eax roll $14,%ecx addl %ebp,%ecx - # 51 + # 51 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -648,7 +648,7 @@ L000start: movl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 52 + # 52 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -661,7 +661,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 53 + # 53 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -674,7 +674,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 54 + # 54 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -687,7 +687,7 @@ L000start: movl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 55 + # 55 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -700,7 +700,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 56 + # 56 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -713,7 +713,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 57 + # 57 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -726,7 +726,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 58 + # 58 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -739,7 +739,7 @@ L000start: movl %ebp,%eax roll $5,%edi addl %esi,%edi - # 59 + # 59 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -752,7 +752,7 @@ L000start: movl %ebx,%eax roll $6,%esi addl %ecx,%esi - # 60 + # 60 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -765,7 +765,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 61 + # 61 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -778,7 +778,7 @@ L000start: movl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 62 + # 62 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -791,7 +791,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 63 + # 63 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -804,7 +804,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 64 + # 64 movl 16(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -815,7 +815,7 @@ L000start: subl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 65 + # 65 movl (%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -826,7 +826,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 66 + # 66 movl 20(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -837,7 +837,7 @@ L000start: subl %esi,%eax roll $5,%ebp addl %ebx,%ebp - # 67 + # 67 movl 36(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -848,7 +848,7 @@ L000start: subl %ecx,%edx roll $11,%ebx addl %edi,%ebx - # 68 + # 68 movl 28(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -859,7 +859,7 @@ L000start: subl %ebp,%eax roll $6,%edi addl %esi,%edi - # 69 + # 69 movl 48(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -870,7 +870,7 @@ L000start: subl %ebx,%edx roll $8,%esi addl %ecx,%esi - # 70 + # 70 movl 8(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -881,7 +881,7 @@ L000start: subl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 71 + # 71 movl 40(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -892,7 +892,7 @@ L000start: subl %esi,%edx roll $12,%ebp addl %ebx,%ebp - # 72 + # 72 movl 56(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -903,7 +903,7 @@ L000start: subl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 73 + # 73 movl 4(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -914,7 +914,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 74 + # 74 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -925,7 +925,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 75 + # 75 movl 32(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -936,7 +936,7 @@ L000start: subl %edi,%edx roll $14,%ecx addl %ebp,%ecx - # 76 + # 76 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -947,7 +947,7 @@ L000start: subl %esi,%eax roll $11,%ebp addl %ebx,%ebp - # 77 + # 77 movl 24(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -958,7 +958,7 @@ L000start: subl %ecx,%edx roll $8,%ebx addl %edi,%ebx - # 78 + # 78 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -969,7 +969,7 @@ L000start: subl %ebp,%eax roll $5,%edi addl %esi,%edi - # 79 + # 79 movl 52(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -989,7 +989,7 @@ L000start: movl %ebp,80(%esp) movl 12(%edx),%ebx movl 16(%edx),%ebp - # 80 + # 80 movl $-1,%edx subl %ebx,%edx movl 20(%esp),%eax @@ -1002,7 +1002,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 81 + # 81 movl 56(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1013,7 +1013,7 @@ L000start: subl %esi,%edx roll $9,%ebp addl %ebx,%ebp - # 82 + # 82 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1024,7 +1024,7 @@ L000start: subl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 83 + # 83 movl (%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1035,7 +1035,7 @@ L000start: subl %ebp,%edx roll $11,%edi addl %esi,%edi - # 84 + # 84 movl 36(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1046,7 +1046,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 85 + # 85 movl 8(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1057,7 +1057,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 86 + # 86 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -1068,7 +1068,7 @@ L000start: subl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 87 + # 87 movl 16(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -1079,7 +1079,7 @@ L000start: subl %ecx,%edx roll $5,%ebx addl %edi,%ebx - # 88 + # 88 movl 52(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -1090,7 +1090,7 @@ L000start: subl %ebp,%eax roll $7,%edi addl %esi,%edi - # 89 + # 89 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -1101,7 +1101,7 @@ L000start: subl %ebx,%edx roll $7,%esi addl %ecx,%esi - # 90 + # 90 movl 60(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -1112,7 +1112,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 91 + # 91 movl 32(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1123,7 +1123,7 @@ L000start: subl %esi,%edx roll $11,%ebp addl %ebx,%ebp - # 92 + # 92 movl 4(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1134,7 +1134,7 @@ L000start: subl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 93 + # 93 movl 40(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1145,7 +1145,7 @@ L000start: subl %ebp,%edx roll $14,%edi addl %esi,%edi - # 94 + # 94 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1156,7 +1156,7 @@ L000start: subl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 95 + # 95 movl 48(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1167,7 +1167,7 @@ L000start: movl %edi,%eax roll $6,%ecx addl %ebp,%ecx - # 96 + # 96 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1180,7 +1180,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 97 + # 97 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1193,7 +1193,7 @@ L000start: movl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 98 + # 98 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1206,7 +1206,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 99 + # 99 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1219,7 +1219,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 100 + # 100 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1232,7 +1232,7 @@ L000start: movl %edi,%eax roll $12,%ecx addl %ebp,%ecx - # 101 + # 101 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1245,7 +1245,7 @@ L000start: movl %esi,%eax roll $8,%ebp addl %ebx,%ebp - # 102 + # 102 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1258,7 +1258,7 @@ L000start: movl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 103 + # 103 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1271,7 +1271,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 104 + # 104 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1284,7 +1284,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 105 + # 105 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1297,7 +1297,7 @@ L000start: movl %edi,%eax roll $7,%ecx addl %ebp,%ecx - # 106 + # 106 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1310,7 +1310,7 @@ L000start: movl %esi,%eax roll $12,%ebp addl %ebx,%ebp - # 107 + # 107 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1323,7 +1323,7 @@ L000start: movl %ecx,%eax roll $7,%ebx addl %edi,%ebx - # 108 + # 108 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1336,7 +1336,7 @@ L000start: movl %ebp,%eax roll $6,%edi addl %esi,%edi - # 109 + # 109 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1349,7 +1349,7 @@ L000start: movl %ebx,%eax roll $15,%esi addl %ecx,%esi - # 110 + # 110 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1362,7 +1362,7 @@ L000start: movl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 111 + # 111 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1375,7 +1375,7 @@ L000start: subl %ecx,%edx roll $11,%ebp addl %ebx,%ebp - # 112 + # 112 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1386,7 +1386,7 @@ L000start: subl %ebp,%eax roll $9,%ebx addl %edi,%ebx - # 113 + # 113 movl 20(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1397,7 +1397,7 @@ L000start: subl %ebx,%edx roll $7,%edi addl %esi,%edi - # 114 + # 114 movl 4(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1408,7 +1408,7 @@ L000start: subl %edi,%eax roll $15,%esi addl %ecx,%esi - # 115 + # 115 movl 12(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1419,7 +1419,7 @@ L000start: subl %esi,%edx roll $11,%ecx addl %ebp,%ecx - # 116 + # 116 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1430,7 +1430,7 @@ L000start: subl %ecx,%eax roll $8,%ebp addl %ebx,%ebp - # 117 + # 117 movl 56(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1441,7 +1441,7 @@ L000start: subl %ebp,%edx roll $6,%ebx addl %edi,%ebx - # 118 + # 118 movl 24(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -1452,7 +1452,7 @@ L000start: subl %ebx,%eax roll $6,%edi addl %esi,%edi - # 119 + # 119 movl 36(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -1463,7 +1463,7 @@ L000start: subl %edi,%edx roll $14,%esi addl %ecx,%esi - # 120 + # 120 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -1474,7 +1474,7 @@ L000start: subl %esi,%eax roll $12,%ecx addl %ebp,%ecx - # 121 + # 121 movl 32(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -1485,7 +1485,7 @@ L000start: subl %ecx,%edx roll $13,%ebp addl %ebx,%ebp - # 122 + # 122 movl 48(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1496,7 +1496,7 @@ L000start: subl %ebp,%eax roll $5,%ebx addl %edi,%ebx - # 123 + # 123 movl 8(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1507,7 +1507,7 @@ L000start: subl %ebx,%edx roll $14,%edi addl %esi,%edi - # 124 + # 124 movl 40(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1518,7 +1518,7 @@ L000start: subl %edi,%eax roll $13,%esi addl %ecx,%esi - # 125 + # 125 movl (%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1529,7 +1529,7 @@ L000start: subl %esi,%edx roll $13,%ecx addl %ebp,%ecx - # 126 + # 126 movl 16(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1540,7 +1540,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 127 + # 127 movl 52(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1551,7 +1551,7 @@ L000start: movl $-1,%eax roll $5,%ebx addl %edi,%ebx - # 128 + # 128 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1564,7 +1564,7 @@ L000start: movl $-1,%edx roll $15,%edi addl %esi,%edi - # 129 + # 129 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1577,7 +1577,7 @@ L000start: movl $-1,%eax roll $5,%esi addl %ecx,%esi - # 130 + # 130 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1590,7 +1590,7 @@ L000start: movl $-1,%edx roll $8,%ecx addl %ebp,%ecx - # 131 + # 131 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1603,7 +1603,7 @@ L000start: movl $-1,%eax roll $11,%ebp addl %ebx,%ebp - # 132 + # 132 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1616,7 +1616,7 @@ L000start: movl $-1,%edx roll $14,%ebx addl %edi,%ebx - # 133 + # 133 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1629,7 +1629,7 @@ L000start: movl $-1,%eax roll $14,%edi addl %esi,%edi - # 134 + # 134 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -1642,7 +1642,7 @@ L000start: movl $-1,%edx roll $6,%esi addl %ecx,%esi - # 135 + # 135 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -1655,7 +1655,7 @@ L000start: movl $-1,%eax roll $14,%ecx addl %ebp,%ecx - # 136 + # 136 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -1668,7 +1668,7 @@ L000start: movl $-1,%edx roll $6,%ebp addl %ebx,%ebp - # 137 + # 137 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -1681,7 +1681,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 138 + # 138 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1694,7 +1694,7 @@ L000start: movl $-1,%edx roll $12,%edi addl %esi,%edi - # 139 + # 139 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1707,7 +1707,7 @@ L000start: movl $-1,%eax roll $9,%esi addl %ecx,%esi - # 140 + # 140 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1720,7 +1720,7 @@ L000start: movl $-1,%edx roll $12,%ecx addl %ebp,%ecx - # 141 + # 141 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1733,7 +1733,7 @@ L000start: movl $-1,%eax roll $5,%ebp addl %ebx,%ebp - # 142 + # 142 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1746,7 +1746,7 @@ L000start: movl $-1,%edx roll $15,%ebx addl %edi,%ebx - # 143 + # 143 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1759,7 +1759,7 @@ L000start: xorl %ebp,%eax roll $8,%edi addl %esi,%edi - # 144 + # 144 movl 48(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1768,7 +1768,7 @@ L000start: movl %edi,%eax roll $8,%esi addl %ecx,%esi - # 145 + # 145 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -1779,7 +1779,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 146 + # 146 movl 40(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1788,7 +1788,7 @@ L000start: movl %ecx,%eax roll $12,%ebp addl %ebx,%ebp - # 147 + # 147 xorl %esi,%eax movl 16(%esp),%edx xorl %ebp,%eax @@ -1799,7 +1799,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 148 + # 148 movl 4(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1808,7 +1808,7 @@ L000start: movl %ebx,%eax roll $12,%edi addl %esi,%edi - # 149 + # 149 xorl %ebp,%eax movl 20(%esp),%edx xorl %edi,%eax @@ -1819,7 +1819,7 @@ L000start: xorl %ebx,%eax roll $5,%esi addl %ecx,%esi - # 150 + # 150 movl 32(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -1828,7 +1828,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 151 + # 151 xorl %edi,%eax movl 28(%esp),%edx xorl %ecx,%eax @@ -1839,7 +1839,7 @@ L000start: xorl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 152 + # 152 movl 24(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -1848,7 +1848,7 @@ L000start: movl %ebp,%eax roll $8,%ebx addl %edi,%ebx - # 153 + # 153 xorl %ecx,%eax movl 8(%esp),%edx xorl %ebx,%eax @@ -1859,7 +1859,7 @@ L000start: xorl %ebp,%eax roll $13,%edi addl %esi,%edi - # 154 + # 154 movl 52(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1868,7 +1868,7 @@ L000start: movl %edi,%eax roll $6,%esi addl %ecx,%esi - # 155 + # 155 xorl %ebx,%eax movl 56(%esp),%edx xorl %esi,%eax @@ -1879,7 +1879,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 156 + # 156 movl (%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1888,7 +1888,7 @@ L000start: movl %ecx,%eax roll $15,%ebp addl %ebx,%ebp - # 157 + # 157 xorl %esi,%eax movl 12(%esp),%edx xorl %ebp,%eax @@ -1899,7 +1899,7 @@ L000start: xorl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 158 + # 158 movl 36(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1908,7 +1908,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 159 + # 159 xorl %ebp,%eax movl 44(%esp),%edx xorl %edi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s index 48860a65b70a61..42b2788199f6dc 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s @@ -95,7 +95,7 @@ L002loop: movl 4(%ebp),%ebx movl 8(%ebp),%ecx movl 12(%ebp),%edx - # 00_15 0 + # 00_15 0 movl %ecx,%esi movl %eax,%ebp roll $5,%ebp @@ -107,7 +107,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 1 + # 00_15 1 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -119,7 +119,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 2 + # 00_15 2 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -131,7 +131,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 3 + # 00_15 3 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -143,7 +143,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 4 + # 00_15 4 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -155,7 +155,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 5 + # 00_15 5 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -167,7 +167,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 6 + # 00_15 6 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -179,7 +179,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 7 + # 00_15 7 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -191,7 +191,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 8 + # 00_15 8 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -203,7 +203,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 9 + # 00_15 9 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -215,7 +215,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 10 + # 00_15 10 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -227,7 +227,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 11 + # 00_15 11 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -239,7 +239,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 12 + # 00_15 12 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -251,7 +251,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 13 + # 00_15 13 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -263,7 +263,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 14 + # 00_15 14 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -275,7 +275,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 15 + # 00_15 15 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -288,7 +288,7 @@ L002loop: leal 1518500249(%ebp,%ebx,1),%ebp movl (%esp),%ebx addl %ebp,%ecx - # 16_19 16 + # 16_19 16 movl %edi,%ebp xorl 8(%esp),%ebx xorl %esi,%ebp @@ -305,7 +305,7 @@ L002loop: leal 1518500249(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 16_19 17 + # 16_19 17 movl %edx,%ebp xorl 12(%esp),%eax xorl %edi,%ebp @@ -322,7 +322,7 @@ L002loop: leal 1518500249(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 16_19 18 + # 16_19 18 movl %ecx,%ebp xorl 16(%esp),%esi xorl %edx,%ebp @@ -339,7 +339,7 @@ L002loop: leal 1518500249(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 16_19 19 + # 16_19 19 movl %ebx,%ebp xorl 20(%esp),%edi xorl %ecx,%ebp @@ -356,7 +356,7 @@ L002loop: leal 1518500249(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 20 + # 20_39 20 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -372,7 +372,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 21 + # 20_39 21 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -388,7 +388,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 22 + # 20_39 22 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -404,7 +404,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 23 + # 20_39 23 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -420,7 +420,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 24 + # 20_39 24 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -436,7 +436,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 25 + # 20_39 25 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -452,7 +452,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 26 + # 20_39 26 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -468,7 +468,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 27 + # 20_39 27 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -484,7 +484,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 28 + # 20_39 28 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -500,7 +500,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 29 + # 20_39 29 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -516,7 +516,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 30 + # 20_39 30 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -532,7 +532,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 31 + # 20_39 31 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp @@ -548,7 +548,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl (%esp),%edx addl %ebp,%edi - # 20_39 32 + # 20_39 32 movl %esi,%ebp xorl 8(%esp),%edx xorl %eax,%ebp @@ -564,7 +564,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 4(%esp),%ecx addl %ebp,%edx - # 20_39 33 + # 20_39 33 movl %edi,%ebp xorl 12(%esp),%ecx xorl %esi,%ebp @@ -580,7 +580,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 8(%esp),%ebx addl %ebp,%ecx - # 20_39 34 + # 20_39 34 movl %edx,%ebp xorl 16(%esp),%ebx xorl %edi,%ebp @@ -596,7 +596,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 12(%esp),%eax addl %ebp,%ebx - # 20_39 35 + # 20_39 35 movl %ecx,%ebp xorl 20(%esp),%eax xorl %edx,%ebp @@ -612,7 +612,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 16(%esp),%esi addl %ebp,%eax - # 20_39 36 + # 20_39 36 movl %ebx,%ebp xorl 24(%esp),%esi xorl %ecx,%ebp @@ -628,7 +628,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 20(%esp),%edi addl %ebp,%esi - # 20_39 37 + # 20_39 37 movl %eax,%ebp xorl 28(%esp),%edi xorl %ebx,%ebp @@ -644,7 +644,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 24(%esp),%edx addl %ebp,%edi - # 20_39 38 + # 20_39 38 movl %esi,%ebp xorl 32(%esp),%edx xorl %eax,%ebp @@ -660,7 +660,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 28(%esp),%ecx addl %ebp,%edx - # 20_39 39 + # 20_39 39 movl %edi,%ebp xorl 36(%esp),%ecx xorl %esi,%ebp @@ -676,7 +676,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 32(%esp),%ebx addl %ebp,%ecx - # 40_59 40 + # 40_59 40 movl %edi,%ebp xorl 40(%esp),%ebx xorl %esi,%ebp @@ -695,7 +695,7 @@ L002loop: andl %esi,%ebp movl 36(%esp),%eax addl %ebp,%ebx - # 40_59 41 + # 40_59 41 movl %edx,%ebp xorl 44(%esp),%eax xorl %edi,%ebp @@ -714,7 +714,7 @@ L002loop: andl %edi,%ebp movl 40(%esp),%esi addl %ebp,%eax - # 40_59 42 + # 40_59 42 movl %ecx,%ebp xorl 48(%esp),%esi xorl %edx,%ebp @@ -733,7 +733,7 @@ L002loop: andl %edx,%ebp movl 44(%esp),%edi addl %ebp,%esi - # 40_59 43 + # 40_59 43 movl %ebx,%ebp xorl 52(%esp),%edi xorl %ecx,%ebp @@ -752,7 +752,7 @@ L002loop: andl %ecx,%ebp movl 48(%esp),%edx addl %ebp,%edi - # 40_59 44 + # 40_59 44 movl %eax,%ebp xorl 56(%esp),%edx xorl %ebx,%ebp @@ -771,7 +771,7 @@ L002loop: andl %ebx,%ebp movl 52(%esp),%ecx addl %ebp,%edx - # 40_59 45 + # 40_59 45 movl %esi,%ebp xorl 60(%esp),%ecx xorl %eax,%ebp @@ -790,7 +790,7 @@ L002loop: andl %eax,%ebp movl 56(%esp),%ebx addl %ebp,%ecx - # 40_59 46 + # 40_59 46 movl %edi,%ebp xorl (%esp),%ebx xorl %esi,%ebp @@ -809,7 +809,7 @@ L002loop: andl %esi,%ebp movl 60(%esp),%eax addl %ebp,%ebx - # 40_59 47 + # 40_59 47 movl %edx,%ebp xorl 4(%esp),%eax xorl %edi,%ebp @@ -828,7 +828,7 @@ L002loop: andl %edi,%ebp movl (%esp),%esi addl %ebp,%eax - # 40_59 48 + # 40_59 48 movl %ecx,%ebp xorl 8(%esp),%esi xorl %edx,%ebp @@ -847,7 +847,7 @@ L002loop: andl %edx,%ebp movl 4(%esp),%edi addl %ebp,%esi - # 40_59 49 + # 40_59 49 movl %ebx,%ebp xorl 12(%esp),%edi xorl %ecx,%ebp @@ -866,7 +866,7 @@ L002loop: andl %ecx,%ebp movl 8(%esp),%edx addl %ebp,%edi - # 40_59 50 + # 40_59 50 movl %eax,%ebp xorl 16(%esp),%edx xorl %ebx,%ebp @@ -885,7 +885,7 @@ L002loop: andl %ebx,%ebp movl 12(%esp),%ecx addl %ebp,%edx - # 40_59 51 + # 40_59 51 movl %esi,%ebp xorl 20(%esp),%ecx xorl %eax,%ebp @@ -904,7 +904,7 @@ L002loop: andl %eax,%ebp movl 16(%esp),%ebx addl %ebp,%ecx - # 40_59 52 + # 40_59 52 movl %edi,%ebp xorl 24(%esp),%ebx xorl %esi,%ebp @@ -923,7 +923,7 @@ L002loop: andl %esi,%ebp movl 20(%esp),%eax addl %ebp,%ebx - # 40_59 53 + # 40_59 53 movl %edx,%ebp xorl 28(%esp),%eax xorl %edi,%ebp @@ -942,7 +942,7 @@ L002loop: andl %edi,%ebp movl 24(%esp),%esi addl %ebp,%eax - # 40_59 54 + # 40_59 54 movl %ecx,%ebp xorl 32(%esp),%esi xorl %edx,%ebp @@ -961,7 +961,7 @@ L002loop: andl %edx,%ebp movl 28(%esp),%edi addl %ebp,%esi - # 40_59 55 + # 40_59 55 movl %ebx,%ebp xorl 36(%esp),%edi xorl %ecx,%ebp @@ -980,7 +980,7 @@ L002loop: andl %ecx,%ebp movl 32(%esp),%edx addl %ebp,%edi - # 40_59 56 + # 40_59 56 movl %eax,%ebp xorl 40(%esp),%edx xorl %ebx,%ebp @@ -999,7 +999,7 @@ L002loop: andl %ebx,%ebp movl 36(%esp),%ecx addl %ebp,%edx - # 40_59 57 + # 40_59 57 movl %esi,%ebp xorl 44(%esp),%ecx xorl %eax,%ebp @@ -1018,7 +1018,7 @@ L002loop: andl %eax,%ebp movl 40(%esp),%ebx addl %ebp,%ecx - # 40_59 58 + # 40_59 58 movl %edi,%ebp xorl 48(%esp),%ebx xorl %esi,%ebp @@ -1037,7 +1037,7 @@ L002loop: andl %esi,%ebp movl 44(%esp),%eax addl %ebp,%ebx - # 40_59 59 + # 40_59 59 movl %edx,%ebp xorl 52(%esp),%eax xorl %edi,%ebp @@ -1056,7 +1056,7 @@ L002loop: andl %edi,%ebp movl 48(%esp),%esi addl %ebp,%eax - # 20_39 60 + # 20_39 60 movl %ebx,%ebp xorl 56(%esp),%esi xorl %ecx,%ebp @@ -1072,7 +1072,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 52(%esp),%edi addl %ebp,%esi - # 20_39 61 + # 20_39 61 movl %eax,%ebp xorl 60(%esp),%edi xorl %ebx,%ebp @@ -1088,7 +1088,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 56(%esp),%edx addl %ebp,%edi - # 20_39 62 + # 20_39 62 movl %esi,%ebp xorl (%esp),%edx xorl %eax,%ebp @@ -1104,7 +1104,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 60(%esp),%ecx addl %ebp,%edx - # 20_39 63 + # 20_39 63 movl %edi,%ebp xorl 4(%esp),%ecx xorl %esi,%ebp @@ -1120,7 +1120,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl (%esp),%ebx addl %ebp,%ecx - # 20_39 64 + # 20_39 64 movl %edx,%ebp xorl 8(%esp),%ebx xorl %edi,%ebp @@ -1136,7 +1136,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 20_39 65 + # 20_39 65 movl %ecx,%ebp xorl 12(%esp),%eax xorl %edx,%ebp @@ -1152,7 +1152,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 20_39 66 + # 20_39 66 movl %ebx,%ebp xorl 16(%esp),%esi xorl %ecx,%ebp @@ -1168,7 +1168,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 20_39 67 + # 20_39 67 movl %eax,%ebp xorl 20(%esp),%edi xorl %ebx,%ebp @@ -1184,7 +1184,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 68 + # 20_39 68 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -1200,7 +1200,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 69 + # 20_39 69 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -1216,7 +1216,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 70 + # 20_39 70 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -1232,7 +1232,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 71 + # 20_39 71 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -1248,7 +1248,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 72 + # 20_39 72 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -1264,7 +1264,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 73 + # 20_39 73 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -1280,7 +1280,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 74 + # 20_39 74 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -1296,7 +1296,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 75 + # 20_39 75 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -1312,7 +1312,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 76 + # 20_39 76 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -1328,7 +1328,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 77 + # 20_39 77 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -1343,7 +1343,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 78 + # 20_39 78 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -1358,7 +1358,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 79 + # 20_39 79 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm/include/progs.h b/deps/openssl/config/archs/BSD-x86/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi index 5e6af396479e3a..d637dd5190a9af 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm b/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm index 15ac34550bee59..9466b5181be011 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9170,6 +9173,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9235,6 +9243,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14013,6 +14026,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14026,6 +14040,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14133,6 +14148,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14185,6 +14204,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -16086,3 +16109,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s index db384026d9d2d6..3e834e9b7b05a4 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s @@ -11,7 +11,7 @@ L_BF_encrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -19,7 +19,7 @@ L_BF_encrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 0 + # Round 0 movl 4(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -39,7 +39,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 8(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -59,7 +59,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 12(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -79,7 +79,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 16(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -99,7 +99,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 20(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -119,7 +119,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 24(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -139,7 +139,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 28(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -159,7 +159,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 32(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -179,7 +179,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 36(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -199,7 +199,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 40(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -219,7 +219,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 44(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -239,7 +239,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 48(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -259,7 +259,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 52(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -279,7 +279,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 56(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -299,7 +299,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 60(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -319,7 +319,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 64(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -336,7 +336,7 @@ L_BF_encrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (16) enc=1 + # Load parameter 0 (16) enc=1 movl 20(%esp),%eax xorl %ebx,%edi movl 68(%ebp),%edx @@ -360,7 +360,7 @@ L_BF_decrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -368,7 +368,7 @@ L_BF_decrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 16 + # Round 16 movl 64(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -388,7 +388,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 60(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -408,7 +408,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 56(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -428,7 +428,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 52(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -448,7 +448,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 48(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -468,7 +468,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 44(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -488,7 +488,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 40(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -508,7 +508,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 36(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -528,7 +528,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 32(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -548,7 +548,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 28(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -568,7 +568,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 24(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -588,7 +588,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 20(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -608,7 +608,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 16(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -628,7 +628,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 12(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -648,7 +648,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 8(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -668,7 +668,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 4(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -685,7 +685,7 @@ L_BF_decrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (1) enc=0 + # Load parameter 0 (1) enc=0 movl 20(%esp),%eax xorl %ebx,%edi movl (%ebp),%edx @@ -708,7 +708,7 @@ L_BF_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -719,9 +719,9 @@ L_BF_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 3 + # get and push parameter 3 movl 48(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s index 80c8db4d292bc9..c7c0a81c38b98a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s @@ -116,7 +116,7 @@ L001maw_non_sse2: jz L006maw_finish .align 4,0x90 L007maw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -125,7 +125,7 @@ L007maw_loop: adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -134,7 +134,7 @@ L007maw_loop: adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -143,7 +143,7 @@ L007maw_loop: adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -152,7 +152,7 @@ L007maw_loop: adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -161,7 +161,7 @@ L007maw_loop: adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -170,7 +170,7 @@ L007maw_loop: adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -179,7 +179,7 @@ L007maw_loop: adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ebp addl %esi,%eax @@ -199,7 +199,7 @@ L006maw_finish: jnz L008maw_finish2 jmp L009maw_end L008maw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -210,7 +210,7 @@ L008maw_finish2: movl %eax,(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -221,7 +221,7 @@ L008maw_finish2: movl %eax,4(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -232,7 +232,7 @@ L008maw_finish2: movl %eax,8(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -243,7 +243,7 @@ L008maw_finish2: movl %eax,12(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -254,7 +254,7 @@ L008maw_finish2: movl %eax,16(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -265,7 +265,7 @@ L008maw_finish2: movl %eax,20(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -328,56 +328,56 @@ L011mw_non_sse2: andl $4294967288,%ebp jz L013mw_finish L014mw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ecx addl %esi,%eax @@ -396,7 +396,7 @@ L013mw_finish: jnz L015mw_finish2 jmp L016mw_end L015mw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax @@ -405,7 +405,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ecx addl %esi,%eax @@ -414,7 +414,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ecx addl %esi,%eax @@ -423,7 +423,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ecx addl %esi,%eax @@ -432,7 +432,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ecx addl %esi,%eax @@ -441,7 +441,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ecx addl %esi,%eax @@ -450,7 +450,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ecx addl %esi,%eax @@ -503,42 +503,42 @@ L018sqr_non_sse2: andl $4294967288,%ebx jz L020sw_finish L021sw_loop: - # Round 0 + # Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) movl %edx,4(%esi) - # Round 4 + # Round 4 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) movl %edx,12(%esi) - # Round 8 + # Round 8 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) movl %edx,20(%esi) - # Round 12 + # Round 12 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) movl %edx,28(%esi) - # Round 16 + # Round 16 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) movl %edx,36(%esi) - # Round 20 + # Round 20 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) movl %edx,44(%esi) - # Round 24 + # Round 24 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) movl %edx,52(%esi) - # Round 28 + # Round 28 movl 28(%edi),%eax mull %eax movl %eax,56(%esi) @@ -552,49 +552,49 @@ L020sw_finish: movl 28(%esp),%ebx andl $7,%ebx jz L022sw_end - # Tail Round 0 + # Tail Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) decl %ebx movl %edx,4(%esi) jz L022sw_end - # Tail Round 1 + # Tail Round 1 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) decl %ebx movl %edx,12(%esi) jz L022sw_end - # Tail Round 2 + # Tail Round 2 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) decl %ebx movl %edx,20(%esi) jz L022sw_end - # Tail Round 3 + # Tail Round 3 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) decl %ebx movl %edx,28(%esi) jz L022sw_end - # Tail Round 4 + # Tail Round 4 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) decl %ebx movl %edx,36(%esi) jz L022sw_end - # Tail Round 5 + # Tail Round 5 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) decl %ebx movl %edx,44(%esi) jz L022sw_end - # Tail Round 6 + # Tail Round 6 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) @@ -633,7 +633,7 @@ L_bn_add_words_begin: andl $4294967288,%ebp jz L023aw_finish L024aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -642,7 +642,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -651,7 +651,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -660,7 +660,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -669,7 +669,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -678,7 +678,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -687,7 +687,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -696,7 +696,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx addl %eax,%ecx @@ -715,7 +715,7 @@ L023aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L025aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -726,7 +726,7 @@ L023aw_finish: decl %ebp movl %ecx,(%ebx) jz L025aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -737,7 +737,7 @@ L023aw_finish: decl %ebp movl %ecx,4(%ebx) jz L025aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -748,7 +748,7 @@ L023aw_finish: decl %ebp movl %ecx,8(%ebx) jz L025aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -759,7 +759,7 @@ L023aw_finish: decl %ebp movl %ecx,12(%ebx) jz L025aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -770,7 +770,7 @@ L023aw_finish: decl %ebp movl %ecx,16(%ebx) jz L025aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -781,7 +781,7 @@ L023aw_finish: decl %ebp movl %ecx,20(%ebx) jz L025aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -814,7 +814,7 @@ L_bn_sub_words_begin: andl $4294967288,%ebp jz L026aw_finish L027aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -823,7 +823,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -832,7 +832,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -841,7 +841,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -850,7 +850,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -859,7 +859,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -868,7 +868,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -877,7 +877,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -896,7 +896,7 @@ L026aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L028aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -907,7 +907,7 @@ L026aw_finish: decl %ebp movl %ecx,(%ebx) jz L028aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -918,7 +918,7 @@ L026aw_finish: decl %ebp movl %ecx,4(%ebx) jz L028aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -929,7 +929,7 @@ L026aw_finish: decl %ebp movl %ecx,8(%ebx) jz L028aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -940,7 +940,7 @@ L026aw_finish: decl %ebp movl %ecx,12(%ebx) jz L028aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -951,7 +951,7 @@ L026aw_finish: decl %ebp movl %ecx,16(%ebx) jz L028aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -962,7 +962,7 @@ L026aw_finish: decl %ebp movl %ecx,20(%ebx) jz L028aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -995,7 +995,7 @@ L_bn_sub_part_words_begin: andl $4294967288,%ebp jz L029aw_finish L030aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1004,7 +1004,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1013,7 +1013,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1022,7 +1022,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1031,7 +1031,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1040,7 +1040,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1049,7 +1049,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1058,7 +1058,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1077,7 +1077,7 @@ L029aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L031aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1091,7 +1091,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 1 + # Tail Round 1 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1105,7 +1105,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 2 + # Tail Round 2 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1119,7 +1119,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 3 + # Tail Round 3 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1133,7 +1133,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 4 + # Tail Round 4 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1147,7 +1147,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 5 + # Tail Round 5 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1161,7 +1161,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 6 + # Tail Round 6 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1180,14 +1180,14 @@ L031aw_end: cmpl $0,%ebp je L032pw_end jge L033pw_pos - # pw_neg + # pw_neg movl $0,%edx subl %ebp,%edx movl %edx,%ebp andl $4294967288,%ebp jz L034pw_neg_finish L035pw_neg_loop: - # dl<0 Round 0 + # dl<0 Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1196,7 +1196,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # dl<0 Round 1 + # dl<0 Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1205,7 +1205,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # dl<0 Round 2 + # dl<0 Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1214,7 +1214,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # dl<0 Round 3 + # dl<0 Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1223,7 +1223,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # dl<0 Round 4 + # dl<0 Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1232,7 +1232,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # dl<0 Round 5 + # dl<0 Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1241,7 +1241,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # dl<0 Round 6 + # dl<0 Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1250,7 +1250,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # dl<0 Round 7 + # dl<0 Round 7 movl $0,%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1270,7 +1270,7 @@ L034pw_neg_finish: subl %edx,%ebp andl $7,%ebp jz L032pw_end - # dl<0 Tail Round 0 + # dl<0 Tail Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1281,7 +1281,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,(%ebx) jz L032pw_end - # dl<0 Tail Round 1 + # dl<0 Tail Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1292,7 +1292,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,4(%ebx) jz L032pw_end - # dl<0 Tail Round 2 + # dl<0 Tail Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1303,7 +1303,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,8(%ebx) jz L032pw_end - # dl<0 Tail Round 3 + # dl<0 Tail Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1314,7 +1314,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,12(%ebx) jz L032pw_end - # dl<0 Tail Round 4 + # dl<0 Tail Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1325,7 +1325,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,16(%ebx) jz L032pw_end - # dl<0 Tail Round 5 + # dl<0 Tail Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1336,7 +1336,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,20(%ebx) jz L032pw_end - # dl<0 Tail Round 6 + # dl<0 Tail Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1350,42 +1350,42 @@ L033pw_pos: andl $4294967288,%ebp jz L036pw_pos_finish L037pw_pos_loop: - # dl>0 Round 0 + # dl>0 Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L038pw_nc0 - # dl>0 Round 1 + # dl>0 Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L039pw_nc1 - # dl>0 Round 2 + # dl>0 Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L040pw_nc2 - # dl>0 Round 3 + # dl>0 Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L041pw_nc3 - # dl>0 Round 4 + # dl>0 Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L042pw_nc4 - # dl>0 Round 5 + # dl>0 Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L043pw_nc5 - # dl>0 Round 6 + # dl>0 Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) jnc L044pw_nc6 - # dl>0 Round 7 + # dl>0 Round 7 movl 28(%esi),%ecx subl %eax,%ecx movl %ecx,28(%ebx) @@ -1399,49 +1399,49 @@ L036pw_pos_finish: movl 36(%esp),%ebp andl $7,%ebp jz L032pw_end - # dl>0 Tail Round 0 + # dl>0 Tail Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L046pw_tail_nc0 decl %ebp jz L032pw_end - # dl>0 Tail Round 1 + # dl>0 Tail Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L047pw_tail_nc1 decl %ebp jz L032pw_end - # dl>0 Tail Round 2 + # dl>0 Tail Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L048pw_tail_nc2 decl %ebp jz L032pw_end - # dl>0 Tail Round 3 + # dl>0 Tail Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L049pw_tail_nc3 decl %ebp jz L032pw_end - # dl>0 Tail Round 4 + # dl>0 Tail Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L050pw_tail_nc4 decl %ebp jz L032pw_end - # dl>0 Tail Round 5 + # dl>0 Tail Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L051pw_tail_nc5 decl %ebp jz L032pw_end - # dl>0 Tail Round 6 + # dl>0 Tail Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s index 0196f42b789164..d2608b28564f5a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s @@ -14,9 +14,9 @@ L_bn_mul_comba8_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -25,17 +25,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -44,24 +44,24 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -70,31 +70,31 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -103,38 +103,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 16(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[4]*b[0] + # mul a[4]*b[0] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[0]*b[4] + # mul a[0]*b[4] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -143,45 +143,45 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 20(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[5]*b[0] + # mul a[5]*b[0] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[4]*b[1] + # mul a[4]*b[1] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[1]*b[4] + # mul a[1]*b[4] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[0]*b[5] + # mul a[0]*b[5] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -190,52 +190,52 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 24(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[6]*b[0] + # mul a[6]*b[0] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[5]*b[1] + # mul a[5]*b[1] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[4]*b[2] + # mul a[4]*b[2] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[2]*b[4] + # mul a[2]*b[4] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[1]*b[5] + # mul a[1]*b[5] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[0]*b[6] + # mul a[0]*b[6] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -244,59 +244,59 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,24(%eax) movl 28(%esi),%eax - # saved r[6] - # ################## Calculate word 7 + # saved r[6] + # ################## Calculate word 7 xorl %ebx,%ebx - # mul a[7]*b[0] + # mul a[7]*b[0] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[6]*b[1] + # mul a[6]*b[1] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[5]*b[2] + # mul a[5]*b[2] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[4]*b[3] + # mul a[4]*b[3] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[3]*b[4] + # mul a[3]*b[4] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[2]*b[5] + # mul a[2]*b[5] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[1]*b[6] + # mul a[1]*b[6] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[0]*b[7] + # mul a[0]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -305,52 +305,52 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,28(%eax) movl 28(%esi),%eax - # saved r[7] - # ################## Calculate word 8 + # saved r[7] + # ################## Calculate word 8 xorl %ecx,%ecx - # mul a[7]*b[1] + # mul a[7]*b[1] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[6]*b[2] + # mul a[6]*b[2] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[5]*b[3] + # mul a[5]*b[3] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[4]*b[4] + # mul a[4]*b[4] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[3]*b[5] + # mul a[3]*b[5] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[2]*b[6] + # mul a[2]*b[6] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[1]*b[7] + # mul a[1]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -359,45 +359,45 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,32(%eax) movl 28(%esi),%eax - # saved r[8] - # ################## Calculate word 9 + # saved r[8] + # ################## Calculate word 9 xorl %ebp,%ebp - # mul a[7]*b[2] + # mul a[7]*b[2] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[6]*b[3] + # mul a[6]*b[3] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[5]*b[4] + # mul a[5]*b[4] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[4]*b[5] + # mul a[4]*b[5] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[3]*b[6] + # mul a[3]*b[6] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[2]*b[7] + # mul a[2]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -406,38 +406,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,36(%eax) movl 28(%esi),%eax - # saved r[9] - # ################## Calculate word 10 + # saved r[9] + # ################## Calculate word 10 xorl %ebx,%ebx - # mul a[7]*b[3] + # mul a[7]*b[3] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[6]*b[4] + # mul a[6]*b[4] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[5]*b[5] + # mul a[5]*b[5] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[4]*b[6] + # mul a[4]*b[6] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[3]*b[7] + # mul a[3]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -446,31 +446,31 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,40(%eax) movl 28(%esi),%eax - # saved r[10] - # ################## Calculate word 11 + # saved r[10] + # ################## Calculate word 11 xorl %ecx,%ecx - # mul a[7]*b[4] + # mul a[7]*b[4] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[6]*b[5] + # mul a[6]*b[5] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[5]*b[6] + # mul a[5]*b[6] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[4]*b[7] + # mul a[4]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -479,24 +479,24 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,44(%eax) movl 28(%esi),%eax - # saved r[11] - # ################## Calculate word 12 + # saved r[11] + # ################## Calculate word 12 xorl %ebp,%ebp - # mul a[7]*b[5] + # mul a[7]*b[5] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[6]*b[6] + # mul a[6]*b[6] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[5]*b[7] + # mul a[5]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -505,17 +505,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,48(%eax) movl 28(%esi),%eax - # saved r[12] - # ################## Calculate word 13 + # saved r[12] + # ################## Calculate word 13 xorl %ebx,%ebx - # mul a[7]*b[6] + # mul a[7]*b[6] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[6]*b[7] + # mul a[6]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -524,18 +524,18 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,52(%eax) movl 28(%esi),%eax - # saved r[13] - # ################## Calculate word 14 + # saved r[13] + # ################## Calculate word 14 xorl %ecx,%ecx - # mul a[7]*b[7] + # mul a[7]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%eax) - # saved r[14] - # save r[15] + # saved r[14] + # save r[15] movl %ebx,60(%eax) popl %ebx popl %ebp @@ -557,9 +557,9 @@ L_bn_mul_comba4_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -568,17 +568,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -587,24 +587,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -613,31 +613,31 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -646,24 +646,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 12(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -672,17 +672,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 12(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -691,18 +691,18 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 12(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%eax) - # saved r[6] - # save r[7] + # saved r[6] + # save r[7] movl %ecx,28(%eax) popl %ebx popl %ebp @@ -723,9 +723,9 @@ L_bn_sqr_comba8_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -733,10 +733,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -747,10 +747,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -759,7 +759,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -767,10 +767,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -780,7 +780,7 @@ L_bn_sqr_comba8_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -791,10 +791,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,12(%edi) movl (%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[4]*a[0] + # sqr a[4]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -804,7 +804,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -813,7 +813,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -821,10 +821,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 20(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[5]*a[0] + # sqr a[5]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -834,7 +834,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ecx movl 4(%esi),%edx - # sqr a[4]*a[1] + # sqr a[4]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -844,7 +844,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -855,10 +855,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,20(%edi) movl (%esi),%edx - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[6]*a[0] + # sqr a[6]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -868,7 +868,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[5]*a[1] + # sqr a[5]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -878,7 +878,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebp movl 8(%esi),%edx - # sqr a[4]*a[2] + # sqr a[4]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -887,7 +887,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 12(%esi),%eax adcl $0,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -895,10 +895,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,24(%edi) movl 28(%esi),%eax - # saved r[6] - # ############### Calculate word 7 + # saved r[6] + # ############### Calculate word 7 xorl %ebx,%ebx - # sqr a[7]*a[0] + # sqr a[7]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -908,7 +908,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[6]*a[1] + # sqr a[6]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -918,7 +918,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebx movl 8(%esi),%edx - # sqr a[5]*a[2] + # sqr a[5]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -928,7 +928,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebx movl 12(%esi),%edx - # sqr a[4]*a[3] + # sqr a[4]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -939,10 +939,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,28(%edi) movl 4(%esi),%edx - # saved r[7] - # ############### Calculate word 8 + # saved r[7] + # ############### Calculate word 8 xorl %ecx,%ecx - # sqr a[7]*a[1] + # sqr a[7]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -952,7 +952,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[6]*a[2] + # sqr a[6]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -962,7 +962,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ecx movl 12(%esi),%edx - # sqr a[5]*a[3] + # sqr a[5]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -971,7 +971,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 16(%esi),%eax adcl $0,%ecx - # sqr a[4]*a[4] + # sqr a[4]*a[4] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -979,10 +979,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,32(%edi) movl 28(%esi),%eax - # saved r[8] - # ############### Calculate word 9 + # saved r[8] + # ############### Calculate word 9 xorl %ebp,%ebp - # sqr a[7]*a[2] + # sqr a[7]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -992,7 +992,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebp movl 12(%esi),%edx - # sqr a[6]*a[3] + # sqr a[6]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1002,7 +1002,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 16(%esi),%edx - # sqr a[5]*a[4] + # sqr a[5]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1013,10 +1013,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,36(%edi) movl 12(%esi),%edx - # saved r[9] - # ############### Calculate word 10 + # saved r[9] + # ############### Calculate word 10 xorl %ebx,%ebx - # sqr a[7]*a[3] + # sqr a[7]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1026,7 +1026,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 16(%esi),%edx - # sqr a[6]*a[4] + # sqr a[6]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1035,7 +1035,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 20(%esi),%eax adcl $0,%ebx - # sqr a[5]*a[5] + # sqr a[5]*a[5] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1043,10 +1043,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,40(%edi) movl 28(%esi),%eax - # saved r[10] - # ############### Calculate word 11 + # saved r[10] + # ############### Calculate word 11 xorl %ecx,%ecx - # sqr a[7]*a[4] + # sqr a[7]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1056,7 +1056,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 20(%esi),%edx - # sqr a[6]*a[5] + # sqr a[6]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1067,10 +1067,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,44(%edi) movl 20(%esi),%edx - # saved r[11] - # ############### Calculate word 12 + # saved r[11] + # ############### Calculate word 12 xorl %ebp,%ebp - # sqr a[7]*a[5] + # sqr a[7]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1079,7 +1079,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 24(%esi),%eax adcl $0,%ebp - # sqr a[6]*a[6] + # sqr a[6]*a[6] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1087,10 +1087,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,48(%edi) movl 28(%esi),%eax - # saved r[12] - # ############### Calculate word 13 + # saved r[12] + # ############### Calculate word 13 xorl %ebx,%ebx - # sqr a[7]*a[6] + # sqr a[7]*a[6] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1100,16 +1100,16 @@ L_bn_sqr_comba8_begin: movl 28(%esi),%eax adcl $0,%ebx movl %ecx,52(%edi) - # saved r[13] - # ############### Calculate word 14 + # saved r[13] + # ############### Calculate word 14 xorl %ecx,%ecx - # sqr a[7]*a[7] + # sqr a[7]*a[7] mull %eax addl %eax,%ebp adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%edi) - # saved r[14] + # saved r[14] movl %ebx,60(%edi) popl %ebx popl %ebp @@ -1130,9 +1130,9 @@ L_bn_sqr_comba4_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1140,10 +1140,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1154,10 +1154,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1166,7 +1166,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -1174,10 +1174,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1187,7 +1187,7 @@ L_bn_sqr_comba4_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1198,10 +1198,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,12(%edi) movl 4(%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1210,7 +1210,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1218,10 +1218,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 12(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1231,16 +1231,16 @@ L_bn_sqr_comba4_begin: movl 12(%esi),%eax adcl $0,%ecx movl %ebp,20(%edi) - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%edi) - # saved r[6] + # saved r[6] movl %ecx,28(%edi) popl %ebx popl %ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h index e3bc6a9e02edd8..c5d780344ac85c 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:35 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s index e4f05f09e3e588..d2c370231dfa7f 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s @@ -9,7 +9,7 @@ L_fcrypt_body_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words xorl %edi,%edi xorl %esi,%esi call L000PIC_me_up @@ -22,7 +22,7 @@ L000PIC_me_up: pushl $25 L001start: - # Round 0 + # Round 0 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -72,7 +72,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 1 + # Round 1 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -122,7 +122,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 2 + # Round 2 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -172,7 +172,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 3 + # Round 3 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -222,7 +222,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 4 + # Round 4 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -272,7 +272,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 5 + # Round 5 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -322,7 +322,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 6 + # Round 6 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -372,7 +372,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 7 + # Round 7 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -422,7 +422,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 8 + # Round 8 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -472,7 +472,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 9 + # Round 9 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -522,7 +522,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 10 + # Round 10 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -572,7 +572,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 11 + # Round 11 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -622,7 +622,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 12 + # Round 12 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -672,7 +672,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 13 + # Round 13 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -722,7 +722,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 14 + # Round 14 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -772,7 +772,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 15 + # Round 15 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -829,7 +829,7 @@ L001start: movl %ebx,(%esp) jnz L001start - # FP + # FP movl 28(%esp),%edx rorl $1,%edi movl %esi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s index 14d61fda5f9598..5ddd0ed7311ec1 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s @@ -4,7 +4,7 @@ .align 4 __x86_DES_encrypt: pushl %ecx - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -33,7 +33,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -62,7 +62,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -91,7 +91,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -120,7 +120,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -149,7 +149,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -178,7 +178,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -207,7 +207,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -236,7 +236,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -265,7 +265,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -294,7 +294,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -323,7 +323,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -352,7 +352,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -381,7 +381,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -410,7 +410,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -439,7 +439,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -474,7 +474,7 @@ __x86_DES_encrypt: .align 4 __x86_DES_decrypt: pushl %ecx - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -503,7 +503,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -532,7 +532,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -561,7 +561,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -590,7 +590,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -619,7 +619,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -648,7 +648,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -677,7 +677,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -706,7 +706,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -735,7 +735,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -764,7 +764,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -793,7 +793,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -822,7 +822,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -851,7 +851,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -880,7 +880,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -909,7 +909,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -948,7 +948,7 @@ L_DES_encrypt1_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%esi xorl %ecx,%ecx pushl %ebx @@ -957,7 +957,7 @@ L_DES_encrypt1_begin: movl 28(%esp),%ebx movl 4(%esi),%edi - # IP + # IP roll $4,%eax movl %eax,%esi xorl %edi,%eax @@ -1007,7 +1007,7 @@ L001decrypt: call __x86_DES_decrypt L002done: - # FP + # FP movl 20(%esp),%edx rorl $1,%esi movl %edi,%eax @@ -1060,7 +1060,7 @@ L_DES_encrypt2_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%eax xorl %ecx,%ecx pushl %ebx @@ -1083,7 +1083,7 @@ L004decrypt: call __x86_DES_decrypt L005done: - # Fixup + # Fixup rorl $3,%edi movl 20(%esp),%eax rorl $3,%esi @@ -1105,12 +1105,12 @@ L_DES_encrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1169,7 +1169,7 @@ L_DES_encrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1225,12 +1225,12 @@ L_DES_decrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1289,7 +1289,7 @@ L_DES_decrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1345,7 +1345,7 @@ L_DES_ncbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1356,11 +1356,11 @@ L_DES_ncbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 pushl %ecx - # get and push parameter 3 + # get and push parameter 3 movl 52(%esp),%eax pushl %eax pushl %ebx @@ -1524,7 +1524,7 @@ L_DES_ede3_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 6 + # getting iv ptr from parameter 6 movl 44(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1535,15 +1535,15 @@ L_DES_ede3_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 7 + # getting encrypt flag from parameter 7 movl 64(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 movl 56(%esp),%eax pushl %eax - # get and push parameter 4 + # get and push parameter 4 movl 56(%esp),%eax pushl %eax - # get and push parameter 3 + # get and push parameter 3 movl 56(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s index 91e941d1b41b3a..2f4efe555caebb 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s @@ -21,10 +21,10 @@ L_md5_block_asm_data_order_begin: movl 12(%edi),%edx L000start: - # R0 section + # R0 section movl %ecx,%edi movl (%esi),%ebp - # R0 0 + # R0 0 xorl %edx,%edi andl %ebx,%edi leal 3614090360(%eax,%ebp,1),%eax @@ -34,7 +34,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 1 + # R0 1 xorl %ecx,%edi andl %eax,%edi leal 3905402710(%edx,%ebp,1),%edx @@ -44,7 +44,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 2 + # R0 2 xorl %ebx,%edi andl %edx,%edi leal 606105819(%ecx,%ebp,1),%ecx @@ -54,7 +54,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 3 + # R0 3 xorl %eax,%edi andl %ecx,%edi leal 3250441966(%ebx,%ebp,1),%ebx @@ -64,7 +64,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 4 + # R0 4 xorl %edx,%edi andl %ebx,%edi leal 4118548399(%eax,%ebp,1),%eax @@ -74,7 +74,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 5 + # R0 5 xorl %ecx,%edi andl %eax,%edi leal 1200080426(%edx,%ebp,1),%edx @@ -84,7 +84,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 6 + # R0 6 xorl %ebx,%edi andl %edx,%edi leal 2821735955(%ecx,%ebp,1),%ecx @@ -94,7 +94,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 7 + # R0 7 xorl %eax,%edi andl %ecx,%edi leal 4249261313(%ebx,%ebp,1),%ebx @@ -104,7 +104,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 8 + # R0 8 xorl %edx,%edi andl %ebx,%edi leal 1770035416(%eax,%ebp,1),%eax @@ -114,7 +114,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 9 + # R0 9 xorl %ecx,%edi andl %eax,%edi leal 2336552879(%edx,%ebp,1),%edx @@ -124,7 +124,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 10 + # R0 10 xorl %ebx,%edi andl %edx,%edi leal 4294925233(%ecx,%ebp,1),%ecx @@ -134,7 +134,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 11 + # R0 11 xorl %eax,%edi andl %ecx,%edi leal 2304563134(%ebx,%ebp,1),%ebx @@ -144,7 +144,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 12 + # R0 12 xorl %edx,%edi andl %ebx,%edi leal 1804603682(%eax,%ebp,1),%eax @@ -154,7 +154,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 13 + # R0 13 xorl %ecx,%edi andl %eax,%edi leal 4254626195(%edx,%ebp,1),%edx @@ -164,7 +164,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 14 + # R0 14 xorl %ebx,%edi andl %edx,%edi leal 2792965006(%ecx,%ebp,1),%ecx @@ -174,7 +174,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 15 + # R0 15 xorl %eax,%edi andl %ecx,%edi leal 1236535329(%ebx,%ebp,1),%ebx @@ -185,8 +185,8 @@ L000start: movl %ecx,%edi addl %ecx,%ebx - # R1 section - # R1 16 + # R1 section + # R1 16 xorl %ebx,%edi andl %edx,%edi leal 4129170786(%eax,%ebp,1),%eax @@ -196,7 +196,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 17 + # R1 17 xorl %eax,%edi andl %ecx,%edi leal 3225465664(%edx,%ebp,1),%edx @@ -206,7 +206,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 18 + # R1 18 xorl %edx,%edi andl %ebx,%edi leal 643717713(%ecx,%ebp,1),%ecx @@ -216,7 +216,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 19 + # R1 19 xorl %ecx,%edi andl %eax,%edi leal 3921069994(%ebx,%ebp,1),%ebx @@ -226,7 +226,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 20 + # R1 20 xorl %ebx,%edi andl %edx,%edi leal 3593408605(%eax,%ebp,1),%eax @@ -236,7 +236,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 21 + # R1 21 xorl %eax,%edi andl %ecx,%edi leal 38016083(%edx,%ebp,1),%edx @@ -246,7 +246,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 22 + # R1 22 xorl %edx,%edi andl %ebx,%edi leal 3634488961(%ecx,%ebp,1),%ecx @@ -256,7 +256,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 23 + # R1 23 xorl %ecx,%edi andl %eax,%edi leal 3889429448(%ebx,%ebp,1),%ebx @@ -266,7 +266,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 24 + # R1 24 xorl %ebx,%edi andl %edx,%edi leal 568446438(%eax,%ebp,1),%eax @@ -276,7 +276,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 25 + # R1 25 xorl %eax,%edi andl %ecx,%edi leal 3275163606(%edx,%ebp,1),%edx @@ -286,7 +286,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 26 + # R1 26 xorl %edx,%edi andl %ebx,%edi leal 4107603335(%ecx,%ebp,1),%ecx @@ -296,7 +296,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 27 + # R1 27 xorl %ecx,%edi andl %eax,%edi leal 1163531501(%ebx,%ebp,1),%ebx @@ -306,7 +306,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 28 + # R1 28 xorl %ebx,%edi andl %edx,%edi leal 2850285829(%eax,%ebp,1),%eax @@ -316,7 +316,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 29 + # R1 29 xorl %eax,%edi andl %ecx,%edi leal 4243563512(%edx,%ebp,1),%edx @@ -326,7 +326,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 30 + # R1 30 xorl %edx,%edi andl %ebx,%edi leal 1735328473(%ecx,%ebp,1),%ecx @@ -336,7 +336,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 31 + # R1 31 xorl %ecx,%edi andl %eax,%edi leal 2368359562(%ebx,%ebp,1),%ebx @@ -347,8 +347,8 @@ L000start: roll $20,%ebx addl %ecx,%ebx - # R2 section - # R2 32 + # R2 section + # R2 32 xorl %edx,%edi xorl %ebx,%edi leal 4294588738(%eax,%ebp,1),%eax @@ -356,7 +356,7 @@ L000start: movl 32(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 33 + # R2 33 addl %ebx,%eax xorl %ecx,%edi leal 2272392833(%edx,%ebp,1),%edx @@ -366,7 +366,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 34 + # R2 34 xorl %ebx,%edi xorl %edx,%edi leal 1839030562(%ecx,%ebp,1),%ecx @@ -374,7 +374,7 @@ L000start: movl 56(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 35 + # R2 35 addl %edx,%ecx xorl %eax,%edi leal 4259657740(%ebx,%ebp,1),%ebx @@ -384,7 +384,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 36 + # R2 36 xorl %edx,%edi xorl %ebx,%edi leal 2763975236(%eax,%ebp,1),%eax @@ -392,7 +392,7 @@ L000start: movl 16(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 37 + # R2 37 addl %ebx,%eax xorl %ecx,%edi leal 1272893353(%edx,%ebp,1),%edx @@ -402,7 +402,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 38 + # R2 38 xorl %ebx,%edi xorl %edx,%edi leal 4139469664(%ecx,%ebp,1),%ecx @@ -410,7 +410,7 @@ L000start: movl 40(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 39 + # R2 39 addl %edx,%ecx xorl %eax,%edi leal 3200236656(%ebx,%ebp,1),%ebx @@ -420,7 +420,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 40 + # R2 40 xorl %edx,%edi xorl %ebx,%edi leal 681279174(%eax,%ebp,1),%eax @@ -428,7 +428,7 @@ L000start: movl (%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 41 + # R2 41 addl %ebx,%eax xorl %ecx,%edi leal 3936430074(%edx,%ebp,1),%edx @@ -438,7 +438,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 42 + # R2 42 xorl %ebx,%edi xorl %edx,%edi leal 3572445317(%ecx,%ebp,1),%ecx @@ -446,7 +446,7 @@ L000start: movl 24(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 43 + # R2 43 addl %edx,%ecx xorl %eax,%edi leal 76029189(%ebx,%ebp,1),%ebx @@ -456,7 +456,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 44 + # R2 44 xorl %edx,%edi xorl %ebx,%edi leal 3654602809(%eax,%ebp,1),%eax @@ -464,7 +464,7 @@ L000start: movl 48(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 45 + # R2 45 addl %ebx,%eax xorl %ecx,%edi leal 3873151461(%edx,%ebp,1),%edx @@ -474,7 +474,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 46 + # R2 46 xorl %ebx,%edi xorl %edx,%edi leal 530742520(%ecx,%ebp,1),%ecx @@ -482,7 +482,7 @@ L000start: movl 8(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 47 + # R2 47 addl %edx,%ecx xorl %eax,%edi leal 3299628645(%ebx,%ebp,1),%ebx @@ -493,8 +493,8 @@ L000start: roll $23,%ebx addl %ecx,%ebx - # R3 section - # R3 48 + # R3 section + # R3 48 xorl %edx,%edi orl %ebx,%edi leal 4096336452(%eax,%ebp,1),%eax @@ -505,7 +505,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 49 + # R3 49 orl %eax,%edi leal 1126891415(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -515,7 +515,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 50 + # R3 50 orl %edx,%edi leal 2878612391(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -525,7 +525,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 51 + # R3 51 orl %ecx,%edi leal 4237533241(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -535,7 +535,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 52 + # R3 52 orl %ebx,%edi leal 1700485571(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -545,7 +545,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 53 + # R3 53 orl %eax,%edi leal 2399980690(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -555,7 +555,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 54 + # R3 54 orl %edx,%edi leal 4293915773(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -565,7 +565,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 55 + # R3 55 orl %ecx,%edi leal 2240044497(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -575,7 +575,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 56 + # R3 56 orl %ebx,%edi leal 1873313359(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -585,7 +585,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 57 + # R3 57 orl %eax,%edi leal 4264355552(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -595,7 +595,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 58 + # R3 58 orl %edx,%edi leal 2734768916(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -605,7 +605,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 59 + # R3 59 orl %ecx,%edi leal 1309151649(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -615,7 +615,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 60 + # R3 60 orl %ebx,%edi leal 4149444226(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -625,7 +625,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 61 + # R3 61 orl %eax,%edi leal 3174756917(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -635,7 +635,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 62 + # R3 62 orl %edx,%edi leal 718787259(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -645,7 +645,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 63 + # R3 63 orl %ecx,%edi leal 3951481745(%ebx,%ebp,1),%ebx xorl %edx,%edi diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s index 9484963b97fdc7..17603e38536262 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s @@ -51,7 +51,7 @@ L000start: movl %edi,%eax movl 12(%edx),%ebx movl 16(%edx),%ebp - # 0 + # 0 xorl %ebx,%eax movl (%esp),%edx xorl %esi,%eax @@ -61,7 +61,7 @@ L000start: movl %esi,%eax roll $11,%ecx addl %ebp,%ecx - # 1 + # 1 xorl %edi,%eax movl 4(%esp),%edx xorl %ecx,%eax @@ -72,7 +72,7 @@ L000start: xorl %esi,%eax roll $14,%ebp addl %ebx,%ebp - # 2 + # 2 movl 8(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -81,7 +81,7 @@ L000start: movl %ebp,%eax roll $15,%ebx addl %edi,%ebx - # 3 + # 3 xorl %ecx,%eax movl 12(%esp),%edx xorl %ebx,%eax @@ -92,7 +92,7 @@ L000start: xorl %ebp,%eax roll $12,%edi addl %esi,%edi - # 4 + # 4 movl 16(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -101,7 +101,7 @@ L000start: movl %edi,%eax roll $5,%esi addl %ecx,%esi - # 5 + # 5 xorl %ebx,%eax movl 20(%esp),%edx xorl %esi,%eax @@ -112,7 +112,7 @@ L000start: xorl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 6 + # 6 movl 24(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -121,7 +121,7 @@ L000start: movl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 7 + # 7 xorl %esi,%eax movl 28(%esp),%edx xorl %ebp,%eax @@ -132,7 +132,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 8 + # 8 movl 32(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -141,7 +141,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 9 + # 9 xorl %ebp,%eax movl 36(%esp),%edx xorl %edi,%eax @@ -152,7 +152,7 @@ L000start: xorl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 10 + # 10 movl 40(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -161,7 +161,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 11 + # 11 xorl %edi,%eax movl 44(%esp),%edx xorl %ecx,%eax @@ -172,7 +172,7 @@ L000start: xorl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 12 + # 12 movl 48(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -181,7 +181,7 @@ L000start: movl %ebp,%eax roll $6,%ebx addl %edi,%ebx - # 13 + # 13 xorl %ecx,%eax movl 52(%esp),%edx xorl %ebx,%eax @@ -192,7 +192,7 @@ L000start: xorl %ebp,%eax roll $7,%edi addl %esi,%edi - # 14 + # 14 movl 56(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -201,7 +201,7 @@ L000start: movl %edi,%eax roll $9,%esi addl %ecx,%esi - # 15 + # 15 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -212,7 +212,7 @@ L000start: movl 28(%esp),%edx roll $8,%ecx addl %ebp,%ecx - # 16 + # 16 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -225,7 +225,7 @@ L000start: movl $-1,%edx roll $7,%ebp addl %ebx,%ebp - # 17 + # 17 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -238,7 +238,7 @@ L000start: movl $-1,%eax roll $6,%ebx addl %edi,%ebx - # 18 + # 18 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -251,7 +251,7 @@ L000start: movl $-1,%edx roll $8,%edi addl %esi,%edi - # 19 + # 19 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -264,7 +264,7 @@ L000start: movl $-1,%eax roll $13,%esi addl %ecx,%esi - # 20 + # 20 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -277,7 +277,7 @@ L000start: movl $-1,%edx roll $11,%ecx addl %ebp,%ecx - # 21 + # 21 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -290,7 +290,7 @@ L000start: movl $-1,%eax roll $9,%ebp addl %ebx,%ebp - # 22 + # 22 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -303,7 +303,7 @@ L000start: movl $-1,%edx roll $7,%ebx addl %edi,%ebx - # 23 + # 23 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -316,7 +316,7 @@ L000start: movl $-1,%eax roll $15,%edi addl %esi,%edi - # 24 + # 24 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -329,7 +329,7 @@ L000start: movl $-1,%edx roll $7,%esi addl %ecx,%esi - # 25 + # 25 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -342,7 +342,7 @@ L000start: movl $-1,%eax roll $12,%ecx addl %ebp,%ecx - # 26 + # 26 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -355,7 +355,7 @@ L000start: movl $-1,%edx roll $15,%ebp addl %ebx,%ebp - # 27 + # 27 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -368,7 +368,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 28 + # 28 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -381,7 +381,7 @@ L000start: movl $-1,%edx roll $11,%edi addl %esi,%edi - # 29 + # 29 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -394,7 +394,7 @@ L000start: movl $-1,%eax roll $7,%esi addl %ecx,%esi - # 30 + # 30 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -407,7 +407,7 @@ L000start: movl $-1,%edx roll $13,%ecx addl %ebp,%ecx - # 31 + # 31 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -420,7 +420,7 @@ L000start: subl %ecx,%edx roll $12,%ebp addl %ebx,%ebp - # 32 + # 32 movl 12(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -431,7 +431,7 @@ L000start: subl %ebp,%eax roll $11,%ebx addl %edi,%ebx - # 33 + # 33 movl 40(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -442,7 +442,7 @@ L000start: subl %ebx,%edx roll $13,%edi addl %esi,%edi - # 34 + # 34 movl 56(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -453,7 +453,7 @@ L000start: subl %edi,%eax roll $6,%esi addl %ecx,%esi - # 35 + # 35 movl 16(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -464,7 +464,7 @@ L000start: subl %esi,%edx roll $7,%ecx addl %ebp,%ecx - # 36 + # 36 movl 36(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -475,7 +475,7 @@ L000start: subl %ecx,%eax roll $14,%ebp addl %ebx,%ebp - # 37 + # 37 movl 60(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -486,7 +486,7 @@ L000start: subl %ebp,%edx roll $9,%ebx addl %edi,%ebx - # 38 + # 38 movl 32(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -497,7 +497,7 @@ L000start: subl %ebx,%eax roll $13,%edi addl %esi,%edi - # 39 + # 39 movl 4(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -508,7 +508,7 @@ L000start: subl %edi,%edx roll $15,%esi addl %ecx,%esi - # 40 + # 40 movl 8(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -519,7 +519,7 @@ L000start: subl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 41 + # 41 movl 28(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -530,7 +530,7 @@ L000start: subl %ecx,%edx roll $8,%ebp addl %ebx,%ebp - # 42 + # 42 movl (%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -541,7 +541,7 @@ L000start: subl %ebp,%eax roll $13,%ebx addl %edi,%ebx - # 43 + # 43 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -552,7 +552,7 @@ L000start: subl %ebx,%edx roll $6,%edi addl %esi,%edi - # 44 + # 44 movl 52(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -563,7 +563,7 @@ L000start: subl %edi,%eax roll $5,%esi addl %ecx,%esi - # 45 + # 45 movl 44(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -574,7 +574,7 @@ L000start: subl %esi,%edx roll $12,%ecx addl %ebp,%ecx - # 46 + # 46 movl 20(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -585,7 +585,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 47 + # 47 movl 48(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -596,7 +596,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 48 + # 48 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -609,7 +609,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 49 + # 49 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -622,7 +622,7 @@ L000start: movl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 50 + # 50 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -635,7 +635,7 @@ L000start: movl %edi,%eax roll $14,%ecx addl %ebp,%ecx - # 51 + # 51 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -648,7 +648,7 @@ L000start: movl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 52 + # 52 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -661,7 +661,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 53 + # 53 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -674,7 +674,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 54 + # 54 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -687,7 +687,7 @@ L000start: movl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 55 + # 55 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -700,7 +700,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 56 + # 56 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -713,7 +713,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 57 + # 57 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -726,7 +726,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 58 + # 58 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -739,7 +739,7 @@ L000start: movl %ebp,%eax roll $5,%edi addl %esi,%edi - # 59 + # 59 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -752,7 +752,7 @@ L000start: movl %ebx,%eax roll $6,%esi addl %ecx,%esi - # 60 + # 60 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -765,7 +765,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 61 + # 61 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -778,7 +778,7 @@ L000start: movl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 62 + # 62 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -791,7 +791,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 63 + # 63 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -804,7 +804,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 64 + # 64 movl 16(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -815,7 +815,7 @@ L000start: subl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 65 + # 65 movl (%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -826,7 +826,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 66 + # 66 movl 20(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -837,7 +837,7 @@ L000start: subl %esi,%eax roll $5,%ebp addl %ebx,%ebp - # 67 + # 67 movl 36(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -848,7 +848,7 @@ L000start: subl %ecx,%edx roll $11,%ebx addl %edi,%ebx - # 68 + # 68 movl 28(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -859,7 +859,7 @@ L000start: subl %ebp,%eax roll $6,%edi addl %esi,%edi - # 69 + # 69 movl 48(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -870,7 +870,7 @@ L000start: subl %ebx,%edx roll $8,%esi addl %ecx,%esi - # 70 + # 70 movl 8(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -881,7 +881,7 @@ L000start: subl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 71 + # 71 movl 40(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -892,7 +892,7 @@ L000start: subl %esi,%edx roll $12,%ebp addl %ebx,%ebp - # 72 + # 72 movl 56(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -903,7 +903,7 @@ L000start: subl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 73 + # 73 movl 4(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -914,7 +914,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 74 + # 74 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -925,7 +925,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 75 + # 75 movl 32(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -936,7 +936,7 @@ L000start: subl %edi,%edx roll $14,%ecx addl %ebp,%ecx - # 76 + # 76 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -947,7 +947,7 @@ L000start: subl %esi,%eax roll $11,%ebp addl %ebx,%ebp - # 77 + # 77 movl 24(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -958,7 +958,7 @@ L000start: subl %ecx,%edx roll $8,%ebx addl %edi,%ebx - # 78 + # 78 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -969,7 +969,7 @@ L000start: subl %ebp,%eax roll $5,%edi addl %esi,%edi - # 79 + # 79 movl 52(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -989,7 +989,7 @@ L000start: movl %ebp,80(%esp) movl 12(%edx),%ebx movl 16(%edx),%ebp - # 80 + # 80 movl $-1,%edx subl %ebx,%edx movl 20(%esp),%eax @@ -1002,7 +1002,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 81 + # 81 movl 56(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1013,7 +1013,7 @@ L000start: subl %esi,%edx roll $9,%ebp addl %ebx,%ebp - # 82 + # 82 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1024,7 +1024,7 @@ L000start: subl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 83 + # 83 movl (%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1035,7 +1035,7 @@ L000start: subl %ebp,%edx roll $11,%edi addl %esi,%edi - # 84 + # 84 movl 36(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1046,7 +1046,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 85 + # 85 movl 8(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1057,7 +1057,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 86 + # 86 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -1068,7 +1068,7 @@ L000start: subl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 87 + # 87 movl 16(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -1079,7 +1079,7 @@ L000start: subl %ecx,%edx roll $5,%ebx addl %edi,%ebx - # 88 + # 88 movl 52(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -1090,7 +1090,7 @@ L000start: subl %ebp,%eax roll $7,%edi addl %esi,%edi - # 89 + # 89 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -1101,7 +1101,7 @@ L000start: subl %ebx,%edx roll $7,%esi addl %ecx,%esi - # 90 + # 90 movl 60(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -1112,7 +1112,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 91 + # 91 movl 32(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1123,7 +1123,7 @@ L000start: subl %esi,%edx roll $11,%ebp addl %ebx,%ebp - # 92 + # 92 movl 4(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1134,7 +1134,7 @@ L000start: subl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 93 + # 93 movl 40(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1145,7 +1145,7 @@ L000start: subl %ebp,%edx roll $14,%edi addl %esi,%edi - # 94 + # 94 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1156,7 +1156,7 @@ L000start: subl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 95 + # 95 movl 48(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1167,7 +1167,7 @@ L000start: movl %edi,%eax roll $6,%ecx addl %ebp,%ecx - # 96 + # 96 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1180,7 +1180,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 97 + # 97 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1193,7 +1193,7 @@ L000start: movl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 98 + # 98 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1206,7 +1206,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 99 + # 99 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1219,7 +1219,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 100 + # 100 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1232,7 +1232,7 @@ L000start: movl %edi,%eax roll $12,%ecx addl %ebp,%ecx - # 101 + # 101 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1245,7 +1245,7 @@ L000start: movl %esi,%eax roll $8,%ebp addl %ebx,%ebp - # 102 + # 102 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1258,7 +1258,7 @@ L000start: movl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 103 + # 103 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1271,7 +1271,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 104 + # 104 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1284,7 +1284,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 105 + # 105 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1297,7 +1297,7 @@ L000start: movl %edi,%eax roll $7,%ecx addl %ebp,%ecx - # 106 + # 106 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1310,7 +1310,7 @@ L000start: movl %esi,%eax roll $12,%ebp addl %ebx,%ebp - # 107 + # 107 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1323,7 +1323,7 @@ L000start: movl %ecx,%eax roll $7,%ebx addl %edi,%ebx - # 108 + # 108 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1336,7 +1336,7 @@ L000start: movl %ebp,%eax roll $6,%edi addl %esi,%edi - # 109 + # 109 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1349,7 +1349,7 @@ L000start: movl %ebx,%eax roll $15,%esi addl %ecx,%esi - # 110 + # 110 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1362,7 +1362,7 @@ L000start: movl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 111 + # 111 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1375,7 +1375,7 @@ L000start: subl %ecx,%edx roll $11,%ebp addl %ebx,%ebp - # 112 + # 112 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1386,7 +1386,7 @@ L000start: subl %ebp,%eax roll $9,%ebx addl %edi,%ebx - # 113 + # 113 movl 20(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1397,7 +1397,7 @@ L000start: subl %ebx,%edx roll $7,%edi addl %esi,%edi - # 114 + # 114 movl 4(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1408,7 +1408,7 @@ L000start: subl %edi,%eax roll $15,%esi addl %ecx,%esi - # 115 + # 115 movl 12(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1419,7 +1419,7 @@ L000start: subl %esi,%edx roll $11,%ecx addl %ebp,%ecx - # 116 + # 116 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1430,7 +1430,7 @@ L000start: subl %ecx,%eax roll $8,%ebp addl %ebx,%ebp - # 117 + # 117 movl 56(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1441,7 +1441,7 @@ L000start: subl %ebp,%edx roll $6,%ebx addl %edi,%ebx - # 118 + # 118 movl 24(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -1452,7 +1452,7 @@ L000start: subl %ebx,%eax roll $6,%edi addl %esi,%edi - # 119 + # 119 movl 36(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -1463,7 +1463,7 @@ L000start: subl %edi,%edx roll $14,%esi addl %ecx,%esi - # 120 + # 120 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -1474,7 +1474,7 @@ L000start: subl %esi,%eax roll $12,%ecx addl %ebp,%ecx - # 121 + # 121 movl 32(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -1485,7 +1485,7 @@ L000start: subl %ecx,%edx roll $13,%ebp addl %ebx,%ebp - # 122 + # 122 movl 48(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1496,7 +1496,7 @@ L000start: subl %ebp,%eax roll $5,%ebx addl %edi,%ebx - # 123 + # 123 movl 8(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1507,7 +1507,7 @@ L000start: subl %ebx,%edx roll $14,%edi addl %esi,%edi - # 124 + # 124 movl 40(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1518,7 +1518,7 @@ L000start: subl %edi,%eax roll $13,%esi addl %ecx,%esi - # 125 + # 125 movl (%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1529,7 +1529,7 @@ L000start: subl %esi,%edx roll $13,%ecx addl %ebp,%ecx - # 126 + # 126 movl 16(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1540,7 +1540,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 127 + # 127 movl 52(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1551,7 +1551,7 @@ L000start: movl $-1,%eax roll $5,%ebx addl %edi,%ebx - # 128 + # 128 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1564,7 +1564,7 @@ L000start: movl $-1,%edx roll $15,%edi addl %esi,%edi - # 129 + # 129 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1577,7 +1577,7 @@ L000start: movl $-1,%eax roll $5,%esi addl %ecx,%esi - # 130 + # 130 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1590,7 +1590,7 @@ L000start: movl $-1,%edx roll $8,%ecx addl %ebp,%ecx - # 131 + # 131 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1603,7 +1603,7 @@ L000start: movl $-1,%eax roll $11,%ebp addl %ebx,%ebp - # 132 + # 132 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1616,7 +1616,7 @@ L000start: movl $-1,%edx roll $14,%ebx addl %edi,%ebx - # 133 + # 133 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1629,7 +1629,7 @@ L000start: movl $-1,%eax roll $14,%edi addl %esi,%edi - # 134 + # 134 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -1642,7 +1642,7 @@ L000start: movl $-1,%edx roll $6,%esi addl %ecx,%esi - # 135 + # 135 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -1655,7 +1655,7 @@ L000start: movl $-1,%eax roll $14,%ecx addl %ebp,%ecx - # 136 + # 136 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -1668,7 +1668,7 @@ L000start: movl $-1,%edx roll $6,%ebp addl %ebx,%ebp - # 137 + # 137 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -1681,7 +1681,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 138 + # 138 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1694,7 +1694,7 @@ L000start: movl $-1,%edx roll $12,%edi addl %esi,%edi - # 139 + # 139 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1707,7 +1707,7 @@ L000start: movl $-1,%eax roll $9,%esi addl %ecx,%esi - # 140 + # 140 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1720,7 +1720,7 @@ L000start: movl $-1,%edx roll $12,%ecx addl %ebp,%ecx - # 141 + # 141 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1733,7 +1733,7 @@ L000start: movl $-1,%eax roll $5,%ebp addl %ebx,%ebp - # 142 + # 142 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1746,7 +1746,7 @@ L000start: movl $-1,%edx roll $15,%ebx addl %edi,%ebx - # 143 + # 143 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1759,7 +1759,7 @@ L000start: xorl %ebp,%eax roll $8,%edi addl %esi,%edi - # 144 + # 144 movl 48(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1768,7 +1768,7 @@ L000start: movl %edi,%eax roll $8,%esi addl %ecx,%esi - # 145 + # 145 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -1779,7 +1779,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 146 + # 146 movl 40(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1788,7 +1788,7 @@ L000start: movl %ecx,%eax roll $12,%ebp addl %ebx,%ebp - # 147 + # 147 xorl %esi,%eax movl 16(%esp),%edx xorl %ebp,%eax @@ -1799,7 +1799,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 148 + # 148 movl 4(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1808,7 +1808,7 @@ L000start: movl %ebx,%eax roll $12,%edi addl %esi,%edi - # 149 + # 149 xorl %ebp,%eax movl 20(%esp),%edx xorl %edi,%eax @@ -1819,7 +1819,7 @@ L000start: xorl %ebx,%eax roll $5,%esi addl %ecx,%esi - # 150 + # 150 movl 32(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -1828,7 +1828,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 151 + # 151 xorl %edi,%eax movl 28(%esp),%edx xorl %ecx,%eax @@ -1839,7 +1839,7 @@ L000start: xorl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 152 + # 152 movl 24(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -1848,7 +1848,7 @@ L000start: movl %ebp,%eax roll $8,%ebx addl %edi,%ebx - # 153 + # 153 xorl %ecx,%eax movl 8(%esp),%edx xorl %ebx,%eax @@ -1859,7 +1859,7 @@ L000start: xorl %ebp,%eax roll $13,%edi addl %esi,%edi - # 154 + # 154 movl 52(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1868,7 +1868,7 @@ L000start: movl %edi,%eax roll $6,%esi addl %ecx,%esi - # 155 + # 155 xorl %ebx,%eax movl 56(%esp),%edx xorl %esi,%eax @@ -1879,7 +1879,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 156 + # 156 movl (%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1888,7 +1888,7 @@ L000start: movl %ecx,%eax roll $15,%ebp addl %ebx,%ebp - # 157 + # 157 xorl %esi,%eax movl 12(%esp),%edx xorl %ebp,%eax @@ -1899,7 +1899,7 @@ L000start: xorl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 158 + # 158 movl 36(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1908,7 +1908,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 159 + # 159 xorl %ebp,%eax movl 44(%esp),%edx xorl %edi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s index 48860a65b70a61..42b2788199f6dc 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s @@ -95,7 +95,7 @@ L002loop: movl 4(%ebp),%ebx movl 8(%ebp),%ecx movl 12(%ebp),%edx - # 00_15 0 + # 00_15 0 movl %ecx,%esi movl %eax,%ebp roll $5,%ebp @@ -107,7 +107,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 1 + # 00_15 1 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -119,7 +119,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 2 + # 00_15 2 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -131,7 +131,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 3 + # 00_15 3 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -143,7 +143,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 4 + # 00_15 4 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -155,7 +155,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 5 + # 00_15 5 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -167,7 +167,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 6 + # 00_15 6 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -179,7 +179,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 7 + # 00_15 7 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -191,7 +191,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 8 + # 00_15 8 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -203,7 +203,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 9 + # 00_15 9 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -215,7 +215,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 10 + # 00_15 10 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -227,7 +227,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 11 + # 00_15 11 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -239,7 +239,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 12 + # 00_15 12 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -251,7 +251,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 13 + # 00_15 13 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -263,7 +263,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 14 + # 00_15 14 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -275,7 +275,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 15 + # 00_15 15 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -288,7 +288,7 @@ L002loop: leal 1518500249(%ebp,%ebx,1),%ebp movl (%esp),%ebx addl %ebp,%ecx - # 16_19 16 + # 16_19 16 movl %edi,%ebp xorl 8(%esp),%ebx xorl %esi,%ebp @@ -305,7 +305,7 @@ L002loop: leal 1518500249(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 16_19 17 + # 16_19 17 movl %edx,%ebp xorl 12(%esp),%eax xorl %edi,%ebp @@ -322,7 +322,7 @@ L002loop: leal 1518500249(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 16_19 18 + # 16_19 18 movl %ecx,%ebp xorl 16(%esp),%esi xorl %edx,%ebp @@ -339,7 +339,7 @@ L002loop: leal 1518500249(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 16_19 19 + # 16_19 19 movl %ebx,%ebp xorl 20(%esp),%edi xorl %ecx,%ebp @@ -356,7 +356,7 @@ L002loop: leal 1518500249(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 20 + # 20_39 20 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -372,7 +372,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 21 + # 20_39 21 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -388,7 +388,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 22 + # 20_39 22 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -404,7 +404,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 23 + # 20_39 23 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -420,7 +420,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 24 + # 20_39 24 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -436,7 +436,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 25 + # 20_39 25 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -452,7 +452,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 26 + # 20_39 26 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -468,7 +468,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 27 + # 20_39 27 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -484,7 +484,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 28 + # 20_39 28 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -500,7 +500,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 29 + # 20_39 29 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -516,7 +516,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 30 + # 20_39 30 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -532,7 +532,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 31 + # 20_39 31 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp @@ -548,7 +548,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl (%esp),%edx addl %ebp,%edi - # 20_39 32 + # 20_39 32 movl %esi,%ebp xorl 8(%esp),%edx xorl %eax,%ebp @@ -564,7 +564,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 4(%esp),%ecx addl %ebp,%edx - # 20_39 33 + # 20_39 33 movl %edi,%ebp xorl 12(%esp),%ecx xorl %esi,%ebp @@ -580,7 +580,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 8(%esp),%ebx addl %ebp,%ecx - # 20_39 34 + # 20_39 34 movl %edx,%ebp xorl 16(%esp),%ebx xorl %edi,%ebp @@ -596,7 +596,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 12(%esp),%eax addl %ebp,%ebx - # 20_39 35 + # 20_39 35 movl %ecx,%ebp xorl 20(%esp),%eax xorl %edx,%ebp @@ -612,7 +612,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 16(%esp),%esi addl %ebp,%eax - # 20_39 36 + # 20_39 36 movl %ebx,%ebp xorl 24(%esp),%esi xorl %ecx,%ebp @@ -628,7 +628,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 20(%esp),%edi addl %ebp,%esi - # 20_39 37 + # 20_39 37 movl %eax,%ebp xorl 28(%esp),%edi xorl %ebx,%ebp @@ -644,7 +644,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 24(%esp),%edx addl %ebp,%edi - # 20_39 38 + # 20_39 38 movl %esi,%ebp xorl 32(%esp),%edx xorl %eax,%ebp @@ -660,7 +660,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 28(%esp),%ecx addl %ebp,%edx - # 20_39 39 + # 20_39 39 movl %edi,%ebp xorl 36(%esp),%ecx xorl %esi,%ebp @@ -676,7 +676,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 32(%esp),%ebx addl %ebp,%ecx - # 40_59 40 + # 40_59 40 movl %edi,%ebp xorl 40(%esp),%ebx xorl %esi,%ebp @@ -695,7 +695,7 @@ L002loop: andl %esi,%ebp movl 36(%esp),%eax addl %ebp,%ebx - # 40_59 41 + # 40_59 41 movl %edx,%ebp xorl 44(%esp),%eax xorl %edi,%ebp @@ -714,7 +714,7 @@ L002loop: andl %edi,%ebp movl 40(%esp),%esi addl %ebp,%eax - # 40_59 42 + # 40_59 42 movl %ecx,%ebp xorl 48(%esp),%esi xorl %edx,%ebp @@ -733,7 +733,7 @@ L002loop: andl %edx,%ebp movl 44(%esp),%edi addl %ebp,%esi - # 40_59 43 + # 40_59 43 movl %ebx,%ebp xorl 52(%esp),%edi xorl %ecx,%ebp @@ -752,7 +752,7 @@ L002loop: andl %ecx,%ebp movl 48(%esp),%edx addl %ebp,%edi - # 40_59 44 + # 40_59 44 movl %eax,%ebp xorl 56(%esp),%edx xorl %ebx,%ebp @@ -771,7 +771,7 @@ L002loop: andl %ebx,%ebp movl 52(%esp),%ecx addl %ebp,%edx - # 40_59 45 + # 40_59 45 movl %esi,%ebp xorl 60(%esp),%ecx xorl %eax,%ebp @@ -790,7 +790,7 @@ L002loop: andl %eax,%ebp movl 56(%esp),%ebx addl %ebp,%ecx - # 40_59 46 + # 40_59 46 movl %edi,%ebp xorl (%esp),%ebx xorl %esi,%ebp @@ -809,7 +809,7 @@ L002loop: andl %esi,%ebp movl 60(%esp),%eax addl %ebp,%ebx - # 40_59 47 + # 40_59 47 movl %edx,%ebp xorl 4(%esp),%eax xorl %edi,%ebp @@ -828,7 +828,7 @@ L002loop: andl %edi,%ebp movl (%esp),%esi addl %ebp,%eax - # 40_59 48 + # 40_59 48 movl %ecx,%ebp xorl 8(%esp),%esi xorl %edx,%ebp @@ -847,7 +847,7 @@ L002loop: andl %edx,%ebp movl 4(%esp),%edi addl %ebp,%esi - # 40_59 49 + # 40_59 49 movl %ebx,%ebp xorl 12(%esp),%edi xorl %ecx,%ebp @@ -866,7 +866,7 @@ L002loop: andl %ecx,%ebp movl 8(%esp),%edx addl %ebp,%edi - # 40_59 50 + # 40_59 50 movl %eax,%ebp xorl 16(%esp),%edx xorl %ebx,%ebp @@ -885,7 +885,7 @@ L002loop: andl %ebx,%ebp movl 12(%esp),%ecx addl %ebp,%edx - # 40_59 51 + # 40_59 51 movl %esi,%ebp xorl 20(%esp),%ecx xorl %eax,%ebp @@ -904,7 +904,7 @@ L002loop: andl %eax,%ebp movl 16(%esp),%ebx addl %ebp,%ecx - # 40_59 52 + # 40_59 52 movl %edi,%ebp xorl 24(%esp),%ebx xorl %esi,%ebp @@ -923,7 +923,7 @@ L002loop: andl %esi,%ebp movl 20(%esp),%eax addl %ebp,%ebx - # 40_59 53 + # 40_59 53 movl %edx,%ebp xorl 28(%esp),%eax xorl %edi,%ebp @@ -942,7 +942,7 @@ L002loop: andl %edi,%ebp movl 24(%esp),%esi addl %ebp,%eax - # 40_59 54 + # 40_59 54 movl %ecx,%ebp xorl 32(%esp),%esi xorl %edx,%ebp @@ -961,7 +961,7 @@ L002loop: andl %edx,%ebp movl 28(%esp),%edi addl %ebp,%esi - # 40_59 55 + # 40_59 55 movl %ebx,%ebp xorl 36(%esp),%edi xorl %ecx,%ebp @@ -980,7 +980,7 @@ L002loop: andl %ecx,%ebp movl 32(%esp),%edx addl %ebp,%edi - # 40_59 56 + # 40_59 56 movl %eax,%ebp xorl 40(%esp),%edx xorl %ebx,%ebp @@ -999,7 +999,7 @@ L002loop: andl %ebx,%ebp movl 36(%esp),%ecx addl %ebp,%edx - # 40_59 57 + # 40_59 57 movl %esi,%ebp xorl 44(%esp),%ecx xorl %eax,%ebp @@ -1018,7 +1018,7 @@ L002loop: andl %eax,%ebp movl 40(%esp),%ebx addl %ebp,%ecx - # 40_59 58 + # 40_59 58 movl %edi,%ebp xorl 48(%esp),%ebx xorl %esi,%ebp @@ -1037,7 +1037,7 @@ L002loop: andl %esi,%ebp movl 44(%esp),%eax addl %ebp,%ebx - # 40_59 59 + # 40_59 59 movl %edx,%ebp xorl 52(%esp),%eax xorl %edi,%ebp @@ -1056,7 +1056,7 @@ L002loop: andl %edi,%ebp movl 48(%esp),%esi addl %ebp,%eax - # 20_39 60 + # 20_39 60 movl %ebx,%ebp xorl 56(%esp),%esi xorl %ecx,%ebp @@ -1072,7 +1072,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 52(%esp),%edi addl %ebp,%esi - # 20_39 61 + # 20_39 61 movl %eax,%ebp xorl 60(%esp),%edi xorl %ebx,%ebp @@ -1088,7 +1088,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 56(%esp),%edx addl %ebp,%edi - # 20_39 62 + # 20_39 62 movl %esi,%ebp xorl (%esp),%edx xorl %eax,%ebp @@ -1104,7 +1104,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 60(%esp),%ecx addl %ebp,%edx - # 20_39 63 + # 20_39 63 movl %edi,%ebp xorl 4(%esp),%ecx xorl %esi,%ebp @@ -1120,7 +1120,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl (%esp),%ebx addl %ebp,%ecx - # 20_39 64 + # 20_39 64 movl %edx,%ebp xorl 8(%esp),%ebx xorl %edi,%ebp @@ -1136,7 +1136,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 20_39 65 + # 20_39 65 movl %ecx,%ebp xorl 12(%esp),%eax xorl %edx,%ebp @@ -1152,7 +1152,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 20_39 66 + # 20_39 66 movl %ebx,%ebp xorl 16(%esp),%esi xorl %ecx,%ebp @@ -1168,7 +1168,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 20_39 67 + # 20_39 67 movl %eax,%ebp xorl 20(%esp),%edi xorl %ebx,%ebp @@ -1184,7 +1184,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 68 + # 20_39 68 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -1200,7 +1200,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 69 + # 20_39 69 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -1216,7 +1216,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 70 + # 20_39 70 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -1232,7 +1232,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 71 + # 20_39 71 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -1248,7 +1248,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 72 + # 20_39 72 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -1264,7 +1264,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 73 + # 20_39 73 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -1280,7 +1280,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 74 + # 20_39 74 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -1296,7 +1296,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 75 + # 20_39 75 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -1312,7 +1312,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 76 + # 20_39 76 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -1328,7 +1328,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 77 + # 20_39 77 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -1343,7 +1343,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 78 + # 20_39 78 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -1358,7 +1358,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 79 + # 20_39 79 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h b/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi b/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi index 0645934ea717a8..0d74f2cfcc8f66 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm b/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm index c406c0cc9873b8..18ca9db55ad5ce 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "BSD-x86" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9077,6 +9080,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9142,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13855,6 +13868,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13868,6 +13882,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13975,6 +13990,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14027,6 +14046,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15931,3 +15954,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h index c1c537b3fb0604..caec1eb1bfdfd4 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:29 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:38 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h b/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi index 004701ad37a61d..a91d1a33e59103 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm index 958b30cc6a1612..e5c5c6cf79fd74 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3188,6 +3189,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3238,6 +3240,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9274,6 +9277,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9339,6 +9347,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14167,6 +14180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14180,6 +14194,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14287,6 +14302,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14339,6 +14358,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h index 3eb8d7ca6bad4a..691147c977d4ed 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:57:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:40 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi index 08b438900a5857..c1d3e86d5f0e55 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm index bf9459acade3c8..895915b7f1b7ee 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3188,6 +3189,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3238,6 +3240,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9274,6 +9277,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9339,6 +9347,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14167,6 +14180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14180,6 +14194,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14287,6 +14302,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14339,6 +14358,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h index c9ffd0c3c093bf..2c69f50e9139d3 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:57:48 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:49 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi index e9ccff96476c79..ff8709f91fc26e 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm index 84e3b187535590..afe466409f85df 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -265,6 +265,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3150,6 +3151,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3200,6 +3202,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9098,6 +9101,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9163,6 +9171,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13876,6 +13889,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13889,6 +13903,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13996,6 +14011,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14048,6 +14067,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h index 7702df5969293b..77b91b36b42dcb 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:58:03 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:57 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi index cbd749521e3dac..280cabe538963b 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm b/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm index ecd5ebea5fdb19..b217afb0b75bc3 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x56535777ac98)", + RANLIB => "CODE(0x55d79573e6c0)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86.s aesni-x86.s", @@ -289,6 +289,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3186,6 +3187,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3236,6 +3238,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9216,6 +9219,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9281,6 +9289,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14032,6 +14045,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14045,6 +14059,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14152,6 +14167,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14204,6 +14223,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h index b0dfdc7a561045..509dafd58ce656 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:03 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h b/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi index a718f985ec9427..0028ff60aef07b 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm b/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm index f0ae44934c40be..47fdbbbc65c9f4 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55a934dbe798)", + RANLIB => "CODE(0x5624002a8d50)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86.s aesni-x86.s", @@ -289,6 +289,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3186,6 +3187,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3236,6 +3238,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9216,6 +9219,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9281,6 +9289,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14032,6 +14045,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14045,6 +14059,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14152,6 +14167,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14204,6 +14223,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h index 6aeb27ba400834..e73ba40561498b 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:39 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:06 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h b/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi index a5bb5f58ee45b3..e275311fc86c18 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm index 16ff6d982004a3..492ca88e3f4e38 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm @@ -65,7 +65,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -131,7 +131,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55ddfff979c8)", + RANLIB => "CODE(0x563440a67888)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -286,6 +286,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9123,6 +9126,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9188,6 +9196,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13874,6 +13887,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13887,6 +13901,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13994,6 +14009,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14046,6 +14065,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h index 916cdacd502875..78537c713e9d9b 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:44 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:10 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi index 2c843d99020ec1..5645409c76b050 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm index 1d006b56d6bc9e..6eaceedda05f37 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm @@ -64,7 +64,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN64-ARM" ], perlenv => { "AR" => undef, @@ -128,7 +128,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55cd56acf3f8)", + RANLIB => "CODE(0x56190a16c608)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/50-win-onecore.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -280,6 +280,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9117,6 +9120,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9182,6 +9190,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13868,6 +13881,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13881,6 +13895,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13988,6 +14003,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14040,6 +14059,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h index 3e790eb5d64363..ac69eb5a2e51c5 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: VC-WIN64-ARM" -#define DATE "built on: Fri Sep 13 16:03:47 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:12 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi index 1b9795cdd47c79..b1bc8c1b97f277 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm index fd72727b52092d..6004a6783ed894 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm @@ -67,7 +67,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -133,7 +133,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x560295a39668)", + RANLIB => "CODE(0x559637c70c60)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86_64.s aesni-x86_64.s aesni-sha1-x86_64.s aesni-sha256-x86_64.s aesni-mb-x86_64.s", @@ -291,6 +291,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3198,6 +3199,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3248,6 +3250,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9288,6 +9291,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9353,6 +9361,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14154,6 +14167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14167,6 +14181,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14274,6 +14289,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14326,6 +14345,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h index eba30a9c2857ac..9069afb43160de 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:02:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:43 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi index 20ef5e5d4b3a08..f274761feb5543 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm index 1a22e6b6ed13c0..8f0002f44b84b4 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm @@ -67,7 +67,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -133,7 +133,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x562305dbe5d8)", + RANLIB => "CODE(0x563fbb64b740)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86_64.s aesni-x86_64.s aesni-sha1-x86_64.s aesni-sha256-x86_64.s aesni-mb-x86_64.s", @@ -291,6 +291,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3198,6 +3199,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3248,6 +3250,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9288,6 +9291,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9353,6 +9361,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14154,6 +14167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14167,6 +14181,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14274,6 +14289,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14326,6 +14345,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h index 8b7c50ce9a5416..88f52dfa3c7acb 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:52 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi index 53d15ad06c0ecb..9b32fcd8e765b8 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm index fdebb6f709d6b6..da5a29bdebc5e9 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x557a48128d28)", + RANLIB => "CODE(0x55ebe61c3c68)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -288,6 +288,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3173,6 +3174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3223,6 +3225,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9125,6 +9128,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9190,6 +9198,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13876,6 +13889,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13889,6 +13903,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13996,6 +14011,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14048,6 +14067,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h index fae2f1183e9277..2d32047ab1837d 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:30 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:01 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi index a5000c82601f3a..2cb69099ec5eb9 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/asm/configdata.pm b/deps/openssl/config/archs/aix-gcc/asm/configdata.pm index b79cebd14c344b..9b31fcfdef7826 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h index 23bf3c386089ea..d909ea67378c8e 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:47 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:14 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/asm/include/progs.h b/deps/openssl/config/archs/aix-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi b/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi index 272dfa47b0f03b..be4b22e052186c 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm index 52c1848ab07914..95c7b6b9a15087 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h index 553eaaf94c54aa..d9c3b3e2c97d49 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:17 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi index 724ae8a38f8407..1b04259393ddf8 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm index 48bc45df709b75..1dfbe27c3144d4 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "aix-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h index a3c2c5a66f7e4e..845ddc87259c1b 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi index 41095d9eb21597..e5dbb2911117a8 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm b/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm index a91a3c0878db81..94bb10b317502f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9206,6 +9209,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9271,6 +9279,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14059,6 +14072,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14072,6 +14086,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14179,6 +14194,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14231,6 +14250,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h index 4ec1ec8283c301..de2eea406e1019 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:01 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:22 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h b/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi index 321b86afd425ed..d9278603b7c88f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm index 56f615362d9a45..a004ff416477db 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9206,6 +9209,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9271,6 +9279,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14059,6 +14072,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14072,6 +14086,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14179,6 +14194,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14231,6 +14250,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h index fe20db38c0347d..8bf5cab038dc2f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:07 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi index f92a885321bffd..20c46d1e90fa45 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm index d37f8ad8b04179..ca9df45239ede0 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h index 533bf0b9e3e4a7..56272f3ef3bfbb 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:13 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:29 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi index 89752b18296f81..4e6b451e9f9811 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm index d9ceb02bb7bc17..6b88364842c928 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9164,6 +9167,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9229,6 +9237,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14002,6 +14015,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14015,6 +14029,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14122,6 +14137,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14174,6 +14193,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h index abd541341f17ee..f8c7fa6d519ce2 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:40 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:17 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi index 38d1fdcc00e68c..9295a344dd43af 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm index e6aed64bed36d2..d4fd7bf0609ec9 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9164,6 +9167,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9229,6 +9237,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14002,6 +14015,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14015,6 +14029,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14122,6 +14137,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14174,6 +14193,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h index a78ae310fc883b..9ccca4838d64d3 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:46 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:21 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi index c1f15b01a15b5a..ba6404162b2746 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm index a40cf9d26d3bd6..a90861f26fce5d 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9071,6 +9074,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9136,6 +9144,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13844,6 +13857,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13857,6 +13871,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13964,6 +13979,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14016,6 +14035,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h index 5a6d99c66de2c9..14e06dae01099b 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi index c62bcfabf4dd06..ba06c3d630cc8a 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm index 667d891890ef1c..c62730f9f208b0 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3179,6 +3180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3229,6 +3231,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9247,6 +9250,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9312,6 +9320,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14135,6 +14148,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14148,6 +14162,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14255,6 +14270,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14307,6 +14326,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h index a6d2dadb30280a..bfdf9e4a15528b 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:07 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:59 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi index f87225df694da8..638b3d84930690 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm index 32bcc2c46627da..05b69a40c14056 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3179,6 +3180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3229,6 +3231,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9247,6 +9250,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9312,6 +9320,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14135,6 +14148,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14148,6 +14162,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14255,6 +14270,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14307,6 +14326,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h index da2e3d51505ac4..52954dde5f06cd 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:21 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:07 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi index be4971efb1cbbf..ebb6c820367276 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm index 02290abc08f011..b3555c4582d96d 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9071,6 +9074,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9136,6 +9144,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13844,6 +13857,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13857,6 +13871,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13964,6 +13979,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14016,6 +14035,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h index a2a8835315fef3..c6c8939166740a 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:36 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:15 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi index ae6905b4dbb510..234a0c651e5bcb 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm b/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm index 832a223ee3ea9c..089f6dde9ddd45 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3166,6 +3167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3216,6 +3218,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9147,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9212,6 +9220,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13975,6 +13988,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13988,6 +14002,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14095,6 +14110,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14147,6 +14166,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h index 6046c7d5fd9777..c3c2471993b75c 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:58:56 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:27 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h b/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi index 01678f13026be3..f33588b637f9dd 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm index bfc02733dc2706..714f75b4b23a66 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3166,6 +3167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3216,6 +3218,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9147,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9212,6 +9220,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13975,6 +13988,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13988,6 +14002,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14095,6 +14110,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14147,6 +14166,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h index 566cfdfd842a9a..0fa826d96613c9 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:59:02 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:30 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi index b531611f3a51a9..b9a36817a55c5b 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm index 320833b90223fa..fdf67718092f36 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h index ed237c3102f71e..abbd1c49bc3352 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:59:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:33 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi index 05351ffa3de54d..ac3e33a41bbb5d 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/asm/configdata.pm b/deps/openssl/config/archs/linux-armv4/asm/configdata.pm index c62115205baa7e..18d57e707865b9 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-armv4" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9143,6 +9146,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9208,6 +9216,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13981,6 +13994,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13994,6 +14008,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14101,6 +14116,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14153,6 +14172,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h index ef86bdcf892a82..316d565ef68f49 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:11 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:35 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/asm/include/progs.h b/deps/openssl/config/archs/linux-armv4/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi b/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi index 8d6124fa1876bb..628bb5d5908f08 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm index 41e52a0bd5590d..5fdb51c9c7911c 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-armv4" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9143,6 +9146,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9208,6 +9216,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13981,6 +13994,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13994,6 +14008,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14101,6 +14116,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14153,6 +14172,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h index 3b9d482a1ab1ba..fe0e9746ab82a9 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:17 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:39 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi index 6358ee980cfa04..9898fba73aa21f 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm b/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm index 9603f342e70290..a163462fb34790 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-armv4" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h index 7cf872d12e9d2a..077f4eba380b20 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:42 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h b/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi index ad58dc4d8d07cd..070531fcea58e7 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/asm/configdata.pm b/deps/openssl/config/archs/linux-elf/asm/configdata.pm index 2e07e0f655cb5d..29723efdffbc5b 100644 --- a/deps/openssl/config/archs/linux-elf/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-elf" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3169,6 +3170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3219,6 +3221,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9189,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9254,6 +9262,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14027,6 +14040,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14040,6 +14054,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14147,6 +14162,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14199,6 +14218,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h index d35d80a968c59e..f9914abd41bafd 100644 --- a/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:27 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:44 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/asm/include/progs.h b/deps/openssl/config/archs/linux-elf/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-elf/asm/openssl.gypi b/deps/openssl/config/archs/linux-elf/asm/openssl.gypi index 19fe9eac77262f..4846cf63f83029 100644 --- a/deps/openssl/config/archs/linux-elf/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm index 9ddf36af0f5672..cd346875c753eb 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-elf" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3169,6 +3170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3219,6 +3221,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9189,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9254,6 +9262,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14027,6 +14040,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14040,6 +14054,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14147,6 +14162,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14199,6 +14218,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h index 153225532e4c1c..07be64138a042a 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:48 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi index 5cfdf4bbe688ae..d06fed49316efd 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm b/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm index 0c657bee8b914c..53c234555ca984 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-elf" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3153,6 +3154,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3203,6 +3205,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9095,6 +9098,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9160,6 +9168,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13868,6 +13881,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13881,6 +13895,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13988,6 +14003,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14040,6 +14059,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h index 39469e8298ae17..86532eadb4341e 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:39 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:51 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h b/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi index 90dd351ecee489..6e1be370e52b46 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc/asm/configdata.pm index 47234ab9177f5b..91a584ba4b6174 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9188,6 +9191,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9253,6 +9261,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14026,6 +14039,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14039,6 +14053,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14146,6 +14161,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14198,6 +14217,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h index af759c6bdab883..3fbe352e945aca 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:48 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:30 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi index 7e5c6bb10968b2..16bb142326aaab 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm index 65b50577c3673c..235caf55ada01f 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9188,6 +9191,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9253,6 +9261,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14026,6 +14039,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14039,6 +14053,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14146,6 +14161,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14198,6 +14217,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h index d91d124d0d95f7..dbe3ab2ea7a8be 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:33 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi index 439c2022b18ce7..12b0757d0a638d 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm index 41afbcc4445a44..f1ef21dc885cac 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h index feccd6e409e5d0..34b7e873739f45 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:59 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:36 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi index 7801ded0cf12d9..b171a2c5fbf116 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm index a8105b72abd10d..7588af12601d6a 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3172,6 +3173,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3222,6 +3224,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9210,6 +9213,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9275,6 +9283,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14063,6 +14076,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14076,6 +14090,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14183,6 +14198,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14235,6 +14254,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h index 2c9f24fdee2e25..cf2d57e41ad93b 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:02 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:38 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi index a050df1969e166..0801d695d269f2 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm index fa53549253d37e..14a47d4c434a7c 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3172,6 +3173,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3222,6 +3224,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9210,6 +9213,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9275,6 +9283,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14063,6 +14076,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14076,6 +14090,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14183,6 +14198,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14235,6 +14254,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h index 0e70ae12285ec6..47c478688719ed 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:42 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi index e0fea38173d967..abe76346265130 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm index 42b7f02251e94b..8acc6c533fa397 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h index 50c645ff13a868..bdcd72046f9b15 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:45 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi index 7580161fa0bfee..ac8494f3acb93c 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm index aff8df2a584ca8..1a39c845b002f3 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9209,6 +9212,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9274,6 +9282,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14062,6 +14075,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14075,6 +14089,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14182,6 +14197,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14234,6 +14253,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h index e7003954bb1a42..d1935581086970 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:17 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:47 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi index 7f228ce30357cc..947f5d69fed623 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm index a92bb250672e06..e33537179d65c4 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9209,6 +9212,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9274,6 +9282,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14062,6 +14075,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14075,6 +14089,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14182,6 +14197,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14234,6 +14253,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h index e8e073c0d22a9f..23884fe1e45088 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:50 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi index 548429f9f799f2..117de2efe50dc4 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm index 9d91dea1de5354..5601b0032a5c26 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h index aebc739df4d2c0..15b242772a88fd 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:29 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:54 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi index e6afc59dc7338a..a8b845c3413ed5 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/asm/configdata.pm b/deps/openssl/config/archs/linux-x32/asm/configdata.pm index 58a473be95a3e6..01666b76435fde 100644 --- a/deps/openssl/config/archs/linux-x32/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x32" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h index c9b1f198949916..a068758ad82591 100644 --- a/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 15:59:43 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:54 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/asm/include/progs.h b/deps/openssl/config/archs/linux-x32/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x32/asm/openssl.gypi b/deps/openssl/config/archs/linux-x32/asm/openssl.gypi index 5faef9c0e48f51..2dd7c27e8c80ae 100644 --- a/deps/openssl/config/archs/linux-x32/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm index c85073cdc5ea14..cd2ab1fa0760b9 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x32" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h index f567041169936f..ec5b5b8d0ef72d 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 15:59:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:02 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi index 7dd6ee5dda657f..0624421d8740e1 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm b/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm index b9a8bebaa20a64..b263c7ea4e229b 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-x32" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h index 369ee613964bf0..e40f0c6a77237a 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 16:00:12 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:10 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h b/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi index 96a69a8a9a0129..bd2bc30038a6bc 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm b/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm index 6eac1af414f413..9304830a31bfc7 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h index 17e89914852fb5..1cb5afe3fddb30 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:16 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:12 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h b/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi index 92491dbd9f1334..623e6153877f7b 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm index b2a66729ebcce6..e46f8af05da8ba 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h index 51e776d7cb1ade..5c643a2af84bab 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:30 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi index 05ee0df141731d..429dfc002fbec2 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm index 93da6e96905edd..56e61c0e2efd6e 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h index df3083b539c028..ff0106c7f2a1c0 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:45 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:28 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi index 11d3aa6e1486e3..d473731e5e4798 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm b/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm index 02768931a01019..1d5b687bd73293 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3162,6 +3163,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3212,6 +3214,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9119,6 +9122,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9184,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13922,6 +13935,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13935,6 +13949,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14042,6 +14057,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14094,6 +14113,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h index e5da8365c7a61d..7da494ac5b3540 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:56 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h b/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi index d70096ff56f805..40f777626d5b29 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm index 96ab247e83743d..dd8228346364d5 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3162,6 +3163,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3212,6 +3214,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9119,6 +9122,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9184,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13922,6 +13935,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13935,6 +13949,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14042,6 +14057,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14094,6 +14113,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h index 71628ed04c6c4f..bf2d4bac853c43 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:37 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:58 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi index 3f09a9306ca936..5fe0ff0ef418e4 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm b/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm index 1fcc3a092d63ba..ab3bf5783c4635 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9090,6 +9093,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9155,6 +9163,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13858,6 +13871,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13871,6 +13885,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13978,6 +13993,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14030,6 +14049,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h index e7256e3f12adc9..235b4ff88d8b86 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:41 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:00 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h b/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi index f1ab15205bd125..d306c74be0cbd7 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm b/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm index 6d77d25fe39da0..45332dee3b27f0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3161,6 +3162,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3211,6 +3213,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9112,6 +9115,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9177,6 +9185,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13910,6 +13923,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13923,6 +13937,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14030,6 +14045,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14082,6 +14101,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15986,3 +16009,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h index 865115208dc742..a07f7ebed69d68 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:01:56 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:09 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S index 0459f23f81b1ad..2906baaf6e155f 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S @@ -3034,3 +3034,4 @@ K256: .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 .asciiz "SHA256 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S index 538bd9e459332d..6318b5a40f9bd0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S @@ -3202,3 +3202,4 @@ K512: .dword 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 .asciiz "SHA512 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h b/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi index bc83b68da5f125..5fab88a675dda5 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm index 8324964ca7936e..01e8255c451294 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3161,6 +3162,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3211,6 +3213,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9112,6 +9115,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9177,6 +9185,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13910,6 +13923,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13923,6 +13937,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14030,6 +14045,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14082,6 +14101,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15986,3 +16009,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h index f6a9d770f1eb3e..af29142c9ef9cd 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:02:00 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:11 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S index 0459f23f81b1ad..2906baaf6e155f 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S @@ -3034,3 +3034,4 @@ K256: .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 .asciiz "SHA256 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S index 538bd9e459332d..6318b5a40f9bd0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S @@ -3202,3 +3202,4 @@ K512: .dword 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 .asciiz "SHA512 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi index 300c2862915e33..fc1d3c145a33b8 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm b/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm index f5b0bfea293e48..e9c443db4b2771 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15949,3 +15972,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h index 3c6ad34101ddd7..d5e5dbc0f64a76 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:02:04 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:14 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h b/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi index 8df2e5005f9e1a..28e13e5608601f 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm b/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm index e31688fe1aceae..2e6f66474e82dc 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3175,6 +3176,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3225,6 +3227,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9132,6 +9135,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9197,6 +9205,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13935,6 +13948,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13948,6 +13962,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14055,6 +14070,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14107,6 +14126,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h index 6d2d3ce3fd2bc9..d9527a9bd229a3 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:44 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:02 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h b/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi index ede7c74f3f3045..1e147d24c7269a 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm index 8aa79776f95c01..8f15e57d4ea244 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3175,6 +3176,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3225,6 +3227,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9132,6 +9135,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9197,6 +9205,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13935,6 +13948,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13948,6 +13962,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14055,6 +14070,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14107,6 +14126,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h index 34799d14b191e1..35610d05130a29 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:49 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:05 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi index cc32b3b5018b4f..a5656bc070313d 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm b/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm index 0a65365f26b154..fc14fbb5d7bf5e 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h index e0fd887860b698..e8844598bdcfeb 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:07 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h b/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi index 9e454f069376a2..6cfbbb721fd86b 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm index c14020e995aa16..e8983e7ba4bac6 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h index 15030d59077278..b270261855ab58 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:16 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi index 69bded9b4f426e..3a2d2232f77676 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm index 7663c71cf63596..623c419d997a97 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h index 78995b8d82a7e3..1a8bcb87e2e2a6 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi index 2e4804695fb92e..d7544b646388d8 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm index b0d7b13f2220d9..ef2a18e9c01c2d 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3149,6 +3150,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3199,6 +3201,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9091,6 +9094,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9156,6 +9164,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13864,6 +13877,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13877,6 +13891,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13984,6 +13999,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14036,6 +14055,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h index e606a3cd7599e4..37e8f820a1abf7 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:21 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:23 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi index 453f72681938fb..e3e22c6ee2959d 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm index d651ee1e5ffe7c..61630cbc58a185 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3189,6 +3190,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3239,6 +3241,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9269,6 +9272,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9334,6 +9342,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14157,6 +14170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14170,6 +14184,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14277,6 +14292,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14329,6 +14348,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h index cc89c88b1cefa1..12e7dc433f88cd 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:25 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi index 0a3feb2ad53f17..902a1a818dbc6c 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm index f10bb2324203ea..de4bcbf80c5043 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3189,6 +3190,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3239,6 +3241,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9269,6 +9272,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9334,6 +9342,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14157,6 +14170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14170,6 +14184,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14277,6 +14292,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14329,6 +14348,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h index c7f32e0bb8b008..7d4ed349c8d5f0 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:40 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:34 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi index a634c666572987..edf02f83cb517c 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm index 50438e79f77475..d3fc70db7e3e91 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h index ae3b2d62e6e569..5389ce09d4d71d 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:54 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:41 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 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 diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi index 1243b00e93eeec..84c85b8e9252c6 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 4609d83baabac1..3dfe0b8c8a01f7 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -16,6 +16,13 @@ '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/openssl/CHANGES b/deps/openssl/openssl/CHANGES index c6ca3439480e94..427ff4a4c7dabe 100644 --- a/deps/openssl/openssl/CHANGES +++ b/deps/openssl/openssl/CHANGES @@ -9,6 +9,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 5a699836f32a09..aa5f528ed3f318 100755 --- a/deps/openssl/openssl/Configure +++ b/deps/openssl/openssl/Configure @@ -378,6 +378,7 @@ my @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -494,6 +495,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 2119cbae9e597d..56fcf9c459ad6a 100644 --- a/deps/openssl/openssl/INSTALL +++ b/deps/openssl/openssl/INSTALL @@ -453,6 +453,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 a433b032407889..4622f3c3b6fe1c 100644 --- a/deps/openssl/openssl/crypto/err/openssl.txt +++ b/deps/openssl/openssl/crypto/err/openssl.txt @@ -1186,6 +1186,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:640:quic_get_message +SSL_F_QUIC_SET_ENCRYPTION_SECRETS:641: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 @@ -1196,7 +1199,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:642:ssl3_dispatch_alert SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC:292:ssl3_do_change_cipher_spec +SSL_F_SSL3_DO_WRITE:643: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 @@ -1304,6 +1309,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:644:SSL_process_quic_post_handshake +SSL_F_SSL_PROVIDE_QUIC_DATA:645: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 @@ -1353,6 +1360,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:646: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 @@ -1416,6 +1424,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:647:\ + 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 @@ -1457,6 +1467,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:648:\ + 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 @@ -1485,6 +1497,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:649:\ + 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 @@ -1503,6 +1517,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:650:\ + 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 @@ -2702,6 +2718,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:294: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 @@ -2877,6 +2894,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:295: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 ae46fad609ac99..722b6c036ff8b6 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 "internal/evp_int.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 26edae3d80be93..20437b76e84791 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 is returned. Examples (not comprehensive) NID_auth_ecdsa NID_auth_psk +SSL_CIPHER_get_prf_nid() retuns the pseudo-random function NID for B. If B is +a pre-TLS-1.2 cipher, it returns B 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 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 00000000000000..60bf704944b2eb --- /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 + + 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 +must remain valid for the lifetime of B or B. Calling this disables +the SSL_OP_ENABLE_MIDDLEBOX_COMPAT option, which is not required for QUIC. + +SSL_set_quic_transport_params() configures B to send B (of length +B) 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 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. 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. 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. It will call functions on +B to configure secrets and send data. If data is needed from +the peer, it will return B. 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 (B) 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 (B) 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 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, where secrets are +only available in the client to server direction. The other secret will be +NULL. The server acknowledges such data at B, +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 (B). + +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, L, L + +=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. + +=cut diff --git a/deps/openssl/openssl/include/openssl/evp.h b/deps/openssl/openssl/include/openssl/evp.h index 545654a98b1c08..073a957ad33504 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 7993ca28f3da5c..c374713bff2e82 100644 --- a/deps/openssl/openssl/include/openssl/ossl_typ.h +++ b/deps/openssl/openssl/include/openssl/ossl_typ.h @@ -175,6 +175,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 6724ccf2d25211..f21458cd5ecdf1 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 3d6850dea36e26..30605c06847be2 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 -# endif +# include # ifdef __cplusplus extern "C" @@ -95,6 +93,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 640 +# define SSL_F_QUIC_SET_ENCRYPTION_SECRETS 641 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SET_CLIENT_CIPHERSUITE 540 # define SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET 595 @@ -105,7 +106,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 642 # define SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC 292 +# define SSL_F_SSL3_DO_WRITE 643 # define SSL_F_SSL3_ENC 608 # define SSL_F_SSL3_FINAL_FINISH_MAC 285 # define SSL_F_SSL3_FINISH_MAC 587 @@ -210,6 +213,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 644 +# define SSL_F_SSL_PROVIDE_QUIC_DATA 645 # define SSL_F_SSL_READ 223 # define SSL_F_SSL_READ_EARLY_DATA 529 # define SSL_F_SSL_READ_EX 434 @@ -259,6 +264,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 646 # define SSL_F_STATE_MACHINE 353 # define SSL_F_TLS12_CHECK_PEER_SIGALG 333 # define SSL_F_TLS12_COPY_SIGALGS 533 @@ -318,6 +324,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 647 # 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 @@ -357,6 +364,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 648 # 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 @@ -382,6 +390,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 649 # 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 @@ -400,6 +409,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 650 # 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 @@ -564,6 +574,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 294 # define SSL_R_INVALID_ALERT 205 # define SSL_R_INVALID_CCS_MESSAGE 260 # define SSL_R_INVALID_CERTIFICATE_OR_ALG 238 @@ -761,6 +772,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 295 # 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 76d9fda46e207e..6e16c97316ddcb 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 bb2f1deb530066..eec0d14f2c5634 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 42382547fb2abb..a06054a0ead780 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 27a1b2ec68b3d0..8b15616feb69bb 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 4b12ed1485d985..f5d5e8eed845cd 100644 --- a/deps/openssl/openssl/ssl/ssl_err.c +++ b/deps/openssl/openssl/ssl/ssl_err.c @@ -122,6 +122,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 +144,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 +310,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 +390,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 +492,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 +565,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 +613,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 +647,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 +926,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 +1270,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 ac820cf9fe160c..c002c5ec539633 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; @@ -3511,6 +3545,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; @@ -3889,7 +3928,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_locl.h b/deps/openssl/openssl/ssl/ssl_locl.h index 25875c9f6d464f..5a7a8591190ed7 100644 --- a/deps/openssl/openssl/ssl/ssl_locl.h +++ b/deps/openssl/openssl/ssl/ssl_locl.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 00000000000000..1651894854aa84 --- /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_locl.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 24410991b29996..2368ba78182360 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 f0ae642fa09857..a5f86d08b06486 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 ab5453f63eccdb..a274e8600019f8 100644 --- a/deps/openssl/openssl/ssl/statem/extensions_srvr.c +++ b/deps/openssl/openssl/ssl/statem/extensions_srvr.c @@ -1298,6 +1298,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 */ @@ -1915,12 +1935,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); @@ -1961,3 +1989,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 e3c5ec003874bb..f1bc4b362f8d3f 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 6410414fb64a66..3e210867c8a6f7 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 22e9f0490e2d94..ccaa5353630f96 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) @@ -1144,6 +1158,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_locl.h b/deps/openssl/openssl/ssl/statem/statem_locl.h index e27c0c13a2bbd5..1551dac9527851 100644 --- a/deps/openssl/openssl/ssl/statem/statem_locl.h +++ b/deps/openssl/openssl/ssl/statem/statem_locl.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 00000000000000..771ccd4df42f56 --- /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_locl.h" +#include "statem_locl.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 8cf9c40d15c007..0b608695584321 100644 --- a/deps/openssl/openssl/ssl/statem/statem_srvr.c +++ b/deps/openssl/openssl/ssl/statem/statem_srvr.c @@ -57,7 +57,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 b5f57a02f747f2..1458e1cb79ae0c 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 7a142268fa764a..6c1214c6b06a78 100644 --- a/deps/openssl/openssl/test/sslapitest.c +++ b/deps/openssl/openssl/test/sslapitest.c @@ -6205,6 +6205,135 @@ static int test_ca_names(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)) @@ -6322,6 +6451,9 @@ int setup_tests(void) ADD_ALL_TESTS(test_cert_cb, 6); ADD_ALL_TESTS(test_client_cert_cb, 2); ADD_ALL_TESTS(test_ca_names, 3); +#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 456afdf4716e07..5f61e6339020bd 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/util/libssl.num b/deps/openssl/openssl/util/libssl.num index 297522c36391f3..15785fe10d0181 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 a6ef44e4a6eb3d..51fb81c0df2839 100644 --- a/deps/openssl/openssl/util/private.num +++ b/deps/openssl/openssl/util/private.num @@ -87,6 +87,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 diff --git a/doc/api/errors.md b/doc/api/errors.md index 03853cd18f543f..d3a4046772f912 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1674,6 +1674,132 @@ The `package.json` [exports][] field does not export the requested subpath. Because exports are encapsulated, private internal modules that are not exported cannot be imported through the package resolution, unless using an absolute URL. + +### `ERR_QUIC_CANNOT_SET_GROUPS` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUIC_ERROR` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUIC_TLS13_REQUIRED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUIC_UNAVAILABLE` + +> Stabililty: 1 - Experimental + +TBD + + +### `ERR_QUICCLIENTSESSION_FAILED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICCLIENTSESSION_FAILED_SETSOCKET` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_INVALID_DCID` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_UPDATEKEY` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_VERSION_NEGOTIATION` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_LISTENING` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_UNBOUND` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_INVALID_PUSH` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_OPEN_FAILED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_UNSUPPORTED_PUSH` + +> Stability: 1 - Experimental + +TBD + ### `ERR_REQUIRE_ESM` diff --git a/doc/api/index.md b/doc/api/index.md index 6a39e2102ce14c..6ff590912e17f9 100644 --- a/doc/api/index.md +++ b/doc/api/index.md @@ -43,6 +43,7 @@ * [Process](process.html) * [Punycode](punycode.html) * [Query Strings](querystring.html) +* [QUIC](quic.html) * [Readline](readline.html) * [REPL](repl.html) * [Report](report.html) diff --git a/doc/api/quic.md b/doc/api/quic.md new file mode 100644 index 00000000000000..48506272cc34f8 --- /dev/null +++ b/doc/api/quic.md @@ -0,0 +1,2273 @@ +# QUIC + + + +> Stability: 1 - Experimental + +The `quic` module provides an implementation of the QUIC protocol. To +access it: + +```js +const quic = require('quic'); +``` + +## Example + +```js +'use strict'; + +const key = getTLSKeySomehow(); +const cert = getTLSCertSomehow(); + +const { createSocket } = require('quic'); + +// Create the QUIC UDP IPv4 socket bound to local IP port 1234 +const socket = createSocket({ endpoint: { port: 1234 } }); + +// Tell the socket to operate as a server using the given +// key and certificate to secure new connections, using +// the fictional 'hello' application protocol. +socket.listen({ key, cert, alpn: 'hello' }); + +socket.on('session', (session) => { + // A new server side session has been created! + + session.on('secure', () => { + // Once the TLS handshake is completed, we can + // open streams... + const uni = session.openStream({ halfOpen: true }); + uni.write('hi '); + uni.end('from the server!'); + }); + + // 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')); + }); +}); + +socket.on('listening', () => { + // 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` - Entere 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 { createSocket } = require('quic'); + +// Create a QuicSocket associated with localhost and port 1234 +const socket = createSocket({ endpoint: { port: 1234 } }); + +const client = 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 key = getTLSKeySomehow(); +const cert = getTLSCertSomehow(); + +socket.listen({ + key, + cert, + alpn: 'foo' +}); + +socket.on('session', (session) => { + session.on('secure', () => { + // The QuicServerSession can now be used for application data + }); +}); +``` + +As with client `QuicSession` instances, the `QuicServerSession` cannot be +used to exhange 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 +const stream1 = session.openStream(); + +// Create a new unidirectional stream +const stream2 = 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 initated 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) make use of 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 + +### quic.createSocket(\[options\]) + + +* `options` {Object} + * `client` {Object} A default configuration for QUIC client sessions created + using `quicsocket.connect()`. + * `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} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6, + respectively. + * `ipv6Only` {boolean} + * `lookup` {Function} A custom DNS lookup function. Default `dns.lookup()`. + * `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 emit ['qlog'][] events 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. + * `validateAddress` {boolean} When `true`, the `QuicSocket` will use explicit + address validation using a QUIC `RETRY` frame when listening for new server + sessions. Default: `false`. + * `validateAddressLRU` {boolean} When `true`, validation will be skipped if + the address has been recently validated. Currently, only the 10 most + recently validated addresses are remembered. Setting `validateAddressLRU` + to `true`, will enable the `validateAddress` option as well. Default: + `false`. + +The `quic.createSocket()` function is used to create new `QuicSocket` instances +associated with a local UDP address. + +### Class: QuicEndpoint + + +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) + + +* `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 + + +* 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.bound + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is bound to the local UDP port. + +#### quicendpoint.closing + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is in the process of closing. + +#### quicendpoint.destroy(\[error\]) + + +* `error` {Object} An `Error` object. + +Closes and destroys the `QuicEndpoint` instance making it usuable. + +#### quicendpoint.destroyed + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` has been destroyed. + +#### quicendpoint.dropMembership(address, iface) + + +* `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 + + +* Type: {integer} + +The system file descriptor the `QuicEndpoint` is bound to. This property +is not set on Windows. + +#### quicendpoint.pending + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is in the process of binding to +the local UDP port. + +#### quicendpoint.ref() + + +#### quicendpoint.setBroadcast(\[on\]) + + +* `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) + + +* `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 + +On most systems, where scope format uses the interface name: + +```js +const socket = quic.createSocket({ endpoint: { type: 'udp6', port: 1234 } }); + +socket.on('ready', () => { + socket.endpoints[0].setMulticastInterface('::%eth1'); +}); +``` + +On Windows, where scope format uses an interface number: + +```js +const socket = quic.createSocket({ endpoint: { type: 'udp6', port: 1234 } }); + +socket.on('ready', () => { + socket.endpoints[0].setMulticastInterface('::%2'); +}); +``` + +##### Example: IPv4 Outgoing Multicast Interface + +All systems use an IP of the host on the desired physical interface: + +```js +const socket = quic.createSocket({ 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\]) + + +* `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) + + +* `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) + + +* `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() + + +### Class: QuicSession extends EventEmitter + +* 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'` + + +Emitted after the `QuicSession` has been destroyed and is no longer usable. + +The `'close'` event will not be emitted more than once. + +#### Event: `'error'` + + +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'` + + +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` 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'` + + +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: `'qlog'` + + +* `jsonChunk` {string} A JSON fragment. + +Emitted if the `qlog: true` option was passed to `quicsocket.connect()` or +`quic.createSocket()` functions. + +The argument is a JSON fragment according to the [qlog standard][]. + +The `'qlog'` event will be emitted multiple times. + +#### Event: `'secure'` + + +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'` + + +Emitted when a new `QuicStream` has been initiated by the connected peer. + +The `'stream'` event may be emitted multiple times. + +#### quicsession.ackDelayRetransmitCount + + +* Type: {BigInt} + +A `BigInt` representing the number of retransmissions caused by delayed +acknowledgements. + +#### quicsession.address + + +* 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 + + +* Type: {string} + +The ALPN protocol identifier negotiated for this session. + +#### quicsession.authenticated + +* 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 + + +* Type: {BigInt} + +A `BigInt` representing the total number of bidirectional streams +created for this `QuicSession`. + +#### quicsession.blockCount + + +* Type: {BigInt} + +A `BigInt` representing 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 + + +* Type: {number} + +The total number of unacknowledged bytes this QUIC endpoint has transmitted +to the connected peer. + +#### quicsession.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes received from the peer. + +#### quicsession.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes sent to the peer. + +#### quicsession.cipher + + +* 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(\[callback\]) + + +* `callback` {Function} Callback invoked when the close operation is completed + +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. + +#### quicsession.closeCode + +* 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 + + +* Type: {boolean} + +Set to `true` if the `QuicSession` is in the process of a graceful shutdown. + +#### quicsession.destroy(\[error\]) + + +* `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 + + +* Type: {boolean} + +Set to `true` if the `QuicSession` has been destroyed. + +#### quicsession.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time the `QuicSession` was active. + +#### quicsession.getCertificate() + + +* 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\]) + + +* `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 + + +TBD + +#### quicsession.handshakeContinuationHistogram + + +TBD + +#### quicsession.handshakeComplete + + +* Type: {boolean} + +Set to `true` if the TLS handshake has completed. + +#### quicsession.handshakeConfirmed + + +* Type: {boolean} + +Set to `true` when the TLS handshake completion has been confirmed. + +#### quicsession.handshakeDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time taken to complete the TLS handshake. + +#### quicsession.idleTimeout + + +* Type: {boolean} + +Set to `true` if the `QuicSession` was closed due to an idle timeout. + +#### quicsession.keyUpdateCount + + +* Type: {BigInt} + +A `BigInt` representing the number of key update operations that have +occured. + +#### quicsession.latestRTT + + +* Type: {BigInt} + +The most recently recorded RTT for this `QuicSession`. + +#### quicsession.lossRetransmitCount + + +* Type: {BigInt} + +A `BigInt` representing the number of lost-packet retransmissions that have been +performed on this `QuicSession`. + +#### quicsession.maxDataLeft + + +* Type: {number} + +The total number of bytes the `QuicSession` is *currently* allowed to +send to the connected peer. + +#### quicsession.maxInFlightBytes + + +* Type: {BigInt} + +A `BigInt` representing the maximum number of in-flight bytes recorded +for this `QuicSession`. + +#### quicsession.maxStreams + + +* 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 + + +* Type: {BigInt} + +The minimum RTT recorded so far for this `QuicSession`. + +#### quicsession.openStream(\[options\]) + +* `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: {QuicStream} + +Returns a new `QuicStream`. + +An error will be thrown if the `QuicSession` has been destroyed or is in the +process of a graceful shutdown. + +#### quicsession.ping() + + +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 + + +* Type: {BigInt} + +A `BigInt` representing the total number of `QuicStreams` initiated by the +connected peer. + +#### quicsession.remoteAddress + + +* 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 + + +* Type: {BigInt} + +A `BigInt` representing the total number of `QuicStream` instances initiated +by this `QuicSession`. + +#### quicsession.servername + + +* Type: {string} + +The SNI servername requested for this session by the client. + +#### quicsession.smoothedRTT + + +* Type: {BigInt} + +The modified RTT calculated for this `QuicSession`. + +#### quicsession.socket + + +* Type: {QuicSocket} + +The `QuicSocket` the `QuicSession` is associated with. + +#### quicsession.statelessReset + + +* Type: {boolean} + +True if the `QuicSession` was closed due to QUIC stateless reset. + +#### quicsession.uniStreamCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of unidirectional streams +created on this `QuicSession`. + +#### quicsession.updateKey() + + +* 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 + + +* 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 + + +* Extends: {QuicSession} + +The `QuicClientSession` class implements the client side of a QUIC connection. +Instances are created using the `quicsocket.connect()` method. + +#### Event: `'OCSPResponse'` + + +Emitted when the `QuicClientSession` receives a requested OCSP certificate +status response from the QUIC server peer. + +The callback is invoked with a single argument: + +* `response` {Buffer} + +Node.js does not perform any automatic validation or processing of the +response. + +The `'OCSPResponse'` event will not be emitted more than once. + +#### Event: `'sessionTicket'` + + +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: `'usePreferredAddress'` + + +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 + + +* 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.ready + + +* Type: {boolean} + +Set to `true` if the `QuicClientSession` is ready for use. False if the +`QuicSocket` has not yet been bound. + +#### quicclientsession.setSocket(socket, callback]) + + +* `socket` {QuicSocket} A `QuicSocket` instance to move this session to. +* `callback` {Function} A callback function that will be invoked once the + migration to the new `QuicSocket` is complete. + +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. If the `QuicClientSession` is not yet ready to +migrate, the callback will be invoked with an `Error` using the code +`ERR_QUICCLIENTSESSION_FAILED_SETSOCKET`. + +### Class: QuicServerSession extends QuicSession + + +* 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. + +#### Event: `'clientHello'` + + +Emitted at the start of the TLS handshake when the `QuicServerSession` receives +the initial TLS Client Hello. + +The event handler is given a callback function that *must* be invoked for the +handshake to continue. + +The callback is invoked with four arguments: + +* `alpn` {string} The ALPN protocol identifier requested by the client. +* `servername` {string} The SNI servername requested by the client. +* `ciphers` {string[]} The list of TLS cipher algorithms requested by the + client. +* `callback` {Function} A callback function that must be called in order for + the TLS handshake to continue. + +The `'clientHello'` event will not be emitted more than once. + +#### Event: `'OCSPRequest'` + + +Emitted when the `QuicServerSession` has received a OCSP certificate status +request as part of the TLS handshake. + +The callback is invoked with three arguments: + +* `servername` {string} +* `context` {tls.SecureContext} +* `callback` {Function} + +The callback *must* be invoked in order for the TLS handshake to continue. + +The `'OCSPRequest'` event will not be emitted more than once. + +#### quicserversession.addContext(servername\[, context\]) + + +* `servername` {string} A DNS name to associate with the given context. +* `context` {tls.SecureContext} A TLS SecureContext to associate with the `servername`. + +TBD + +### Class: QuicSocket + + +New instances of `QuicSocket` are created using the `quic.createSocket()` +method. + +Once created, a `QuicSocket` can be configured to work as both a client and a +server. + +#### Event: `'busy'` + + +Emitted when the server busy state has been toggled using +`quicSocket.setServerBusy()`. The callback is invoked with a single +boolean argument indicating `true` if busy status is enabled, +`false` otherwise. This event is strictly informational. + +```js +const { createSocket } = require('quic'); + +const socket = createSocket(); + +socket.on('busy', (busy) => { + if (busy) + console.log('Server is busy'); + else + console.log('Server is not busy'); +}); + +socket.setServerBusy(true); +socket.setServerBusy(false); +``` + +This `'busy'` event may be emitted multiple times. + +#### Event: `'close'` + + +Emitted after the `QuicSocket` has been destroyed and is no longer usable. + +The `'close'` event will not be emitted multiple times. + +#### Event: `'error'` + + +Emitted before the `'close'` event if the `QuicSocket` was destroyed with an +`error`. + +The `'error'` event will not be emitted multiple times. + +#### Event: `'ready'` + + +Emitted once the `QuicSocket` has been bound to a local UDP port. + +The `'ready'` event will not be emitted multiple times. + +#### Event: `'session'` + + +Emitted when a new `QuicServerSession` has been created. + +The `'session'` event will be emitted multiple times. + +#### quicsocket.addEndpoint(options) + + +* `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} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6, + respectively. + * `ipv6Only` {boolean} +* Returns: {QuicEndpoint} + +Creates and adds a new `QuicEndpoint` to the `QuicSocket` instance. + +#### quicsocket.bound + + +* Type: {boolean} + +Will be `true` if the `QuicSocket` has been successfully bound to the local UDP +port. + +#### quicsocket.boundDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been bound +to a local port. + +#### quicsocket.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the number of bytes received by this `QuicSocket`. + +#### quicsocket.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the number of bytes sent by this `QuicSocket`. + +#### quicsocket.clientSessions + + +* Type: {BigInt} + +A `BigInt` representing the number of client `QuicSession` instances that +have been associated with this `QuicSocket`. + +#### quicsocket.close(\[callback\]) + + +* `callback` {Function} + +Gracefully closes the `QuicSocket`. Existing `QuicSession` instances will be +permitted to close naturally. New `QuicClientSession` and `QuicServerSession` +instances will not be allowed. + +#### quicsocket.connect(\[options\]) + + +* `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} + * `ipv6Only` {boolean} + * `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: [, + passphrase: ]}`. 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. + * `activeConnectionIdLimit` {number} Must be a value between `2` and `8` + (inclusive). Default: `2`. + * `maxAckDelay` {number} + * `maxData` {number} + * `maxPacketSize` {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} + * `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: [, passphrase: ]}`. 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 emit ['qlog'][] events for this session. + Default: `false`. + * `requestOCSP` {boolean} If `true`, specifies that the OCSP status request + extension will be added to the client hello and an `'OCSPResponse'` event + will be emitted before establishing a secure communication. + * `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. Defaults to `'udp4'`. + +Create a new `QuicClientSession`. This function can be called multiple times +to create sessions associated with different endpoints on the same +client endpoint. + +#### quicsocket.destroy(\[error\]) + + +* `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 + + +* Type: {boolean} + +Will be `true` if the `QuicSocket` has been destroyed. + +#### quicsocket.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been active, + +#### quicsocket.endpoints + + +* Type: {QuicEndpoint[]} + +An array of `QuicEndpoint` instances associated with the `QuicSocket`. + +#### quicsocket.listen(\[options\]\[, callback\]) + + +* `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. + * `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: [, + passphrase: ]}`. 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. + * `activeConnectionIdLimit` {number} + * `maxAckDelay` {number} + * `maxData` {number} + * `maxPacketSize` {number} + * `maxStreamsBidi` {number} + * `maxStreamsUni` {number} + * `maxStreamDataBidiLocal` {number} + * `maxStreamDataBidiRemote` {number} + * `maxStreamDataUni` {number} + * `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: [, passphrase: ]}`. 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. + +* `callback` {Function} + +Listen for new peer-initiated sessions. + +If a `callback` is given, it is registered as a handler for the +`'session'` event. + +#### quicsocket.listenDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been listening +for connections. + +#### quicsocket.listening + + +* Type: {boolean} + +Set to `true` if the `QuicSocket` is listening for new connections. + +#### quicsocket.packetsIgnored + + +* Type: {BigInt} + +A `BigInt` representing the number of packets received by this `QuicSocket` that +have been ignored. + +#### quicsocket.packetsReceived + + +* Type: {BigInt} + +A `BigInt` representing the number of packets successfully received by this +`QuicSocket`. + +#### quicsocket.packetsSent + + +* Type: {BigInt} + +A `BigInt` representing the number of packets sent by this `QuicSocket`. + +#### quicsocket.pending + + +* Type: {boolean} + +Set to `true` if the socket is not yet bound to the local UDP port. + +#### quicsocket.ref() + + +#### quicsocket.serverBusyCount + + +* Type: {BigInt} + +A `BigInt` representing the number of `QuicSession` instances rejected +due to server busy status. + +#### quicsocket.serverSessions + + +* Type: {BigInt} + +A `BigInt` representing the number of server `QuicSession` instances that +have been associated with this `QuicSocket`. + +#### quicsocket.setDiagnosticPacketLoss(options) + + +* `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.setServerBusy(\[on\]) + + +* `on` {boolean} When `true`, the `QuicSocket` will reject new connections. + **Defaults**: `true`. + +Calling `setServerBusy()` or `setServerBusy(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 calling +`setServerBusy(false)`. + +#### quicsocket.statelessResetCount + + +* Type: {BigInt} + +A `BigInt` that represents the number of stateless resets that have been sent. + +#### quicsocket.toggleStatelessReset() + + +* Returns {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 +`toggleStatelessReset()` function allows stateless reset to be turned on and +off dynamically through the lifetime of the `QuicSocket`. + +#### quicsocket.unref(); + + +### Class: QuicStream extends stream.Duplex + + +* Extends: {stream.Duplex} + +#### Event: `'blocked'` + + +Emitted when the `QuicStream` has been prevented from sending queued data for +the `QuicStream` due to congestion control. + +#### Event: `'close'` + + +Emitted when the `QuicStream` has is completely closed and the underlying +resources have been freed. + +#### Event: `'data'` + + +#### Event: `'end'` + + +#### Event: `'error'` + + +#### Event: `'informationalHeaders'` + + +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'` + + +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: `'ready'` + + +Emitted when the underlying `QuicSession` has emitted its `secure` event +this stream has received its id, which is accessible as `stream.id` once this +event is emitted. + +#### Event: `'trailingHeaders'` + + +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'` + + +#### quicstream.aborted + +* Type: {boolean} + +True if dataflow on the `QuicStream` was prematurely terminated. + +#### quicstream.bidirectional + + +* Type: {boolean} + +Set to `true` if the `QuicStream` is bidirectional. + +#### quicstream.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes received for this +`QuicStream`. + +#### quicstream.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes sent by this +`QuicStream`. + +#### quicstream.clientInitiated + + +* Type: {boolean} + +Set to `true` if the `QuicStream` was initiated by a `QuicClientSession` +instance. + +#### quicstream.close(code) + + +* `code` {number} + +Closes the `QuicStream`. + +#### quicstream.dataAckHistogram + + +TBD + +#### quicstream.dataRateHistogram + + +TBD + +#### quicstream.dataSizeHistogram + +TBD + +#### quicstream.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time the `QuicStream` has been active. + +#### quicstream.finalSize + + +* Type: {BigInt} + +A `BigInt` specifying the total number of bytes successfully received by the +`QuicStream`. + +#### quicstream.id + + +* Type: {number} + +The numeric identifier of the `QuicStream`. + +#### quicstream.maxAcknowledgedOffset + + +* Type: {BigInt} + +A `BigInt` representing the highest acknowledged data offset received +for this `QuicStream`. + +#### quicstream.maxExtendedOffset + + +* Type: {BigInt} + +A `BigInt` representing the maximum extended data offset that has been +reported to the connected peer. + +#### quicstream.maxReceivedOffset + + +* Type: {BigInt} + +A `BigInt` representing the maximum received offset for this `QuicStream`. + +#### quicstream.pending + + +* {boolean} + +This property is `true` if the underlying session is not finished yet, +i.e. before the `'ready'` event is emitted. + +#### quicstream.pushStream(headers\[, options\]) + + +* `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 + + +* Type: {boolean} + +Set to `true` if the `QuicStream` was initiated by a `QuicServerSession` +instance. + +#### quicstream.session + + +* Type: {QuicSession} + +The `QuicServerSession` or `QuicClientSession`. + +#### quicstream.sendFD(fd\[, options\]) + + +* `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\]) + + +* `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) + +* `headers` {Object} + +TBD + +#### quicstream.submitInitialHeaders(headers) + +* `headers` {Object} + +TBD + +#### quicstream.submitTrailingHeaders(headers) + +* `headers` {Object} + +TBD + +#### quicstream.unidirectional + + +* Type: {boolean} + +Set to `true` if the `QuicStream` is unidirectional. + +[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves +[`tls.DEFAULT_ECDH_CURVE`]: #tls_tls_default_ecdh_curve +[`tls.getCiphers()`]: tls.html#tls_tls_getciphers +[ALPN]: https://tools.ietf.org/html/rfc7301 +[RFC 4007]: https://tools.ietf.org/html/rfc4007 +[Certificate Object]: https://nodejs.org/dist/latest-v12.x/docs/api/tls.html#tls_certificate_object +[modifying the default cipher suite]: tls.html#tls_modifying_the_default_tls_cipher_suite +[OpenSSL Options]: crypto.html#crypto_openssl_options +[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy +['qlog']: #quic_event_qlog +[qlog standard]: https://tools.ietf.org/id/draft-marx-qlog-event-definitions-quic-h3-00.html diff --git a/lib/_http_client.js b/lib/_http_client.js index c447b695c8b45b..c075daf8507ad0 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -184,10 +184,11 @@ function ClientRequest(input, options, cb) { method = this.method = 'GET'; } - const maxHeaderSize = options.maxHeaderSize; - if (maxHeaderSize !== undefined) - validateInteger(maxHeaderSize, 'maxHeaderSize', 0); - this.maxHeaderSize = maxHeaderSize; + validateInteger( + options.maxHeaderSize, + 'maxHeaderSize', + { min: 0, allowUndefined: true }); + this.maxHeaderSize = options.maxHeaderSize; const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined && diff --git a/lib/_http_server.js b/lib/_http_server.js index 3f9c98e8380363..88b9a1015e77e8 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -330,10 +330,11 @@ function Server(options, requestListener) { this[kIncomingMessage] = options.IncomingMessage || IncomingMessage; this[kServerResponse] = options.ServerResponse || ServerResponse; - const maxHeaderSize = options.maxHeaderSize; - if (maxHeaderSize !== undefined) - validateInteger(maxHeaderSize, 'maxHeaderSize', 0); - this.maxHeaderSize = maxHeaderSize; + validateInteger( + options.maxHeaderSize, + 'maxHeaderSize', + { min: 0, allowUndefined: true }); + this.maxHeaderSize = options.maxHeaderSize; const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined && diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 561bb1f2d55581..3722ba52a6619b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -70,11 +70,14 @@ const { ERR_TLS_INVALID_STATE } = codes; const { onpskexchange: kOnPskExchange } = internalBinding('symbols'); -const { getOptionValue } = require('internal/options'); +const { + getAllowUnauthorized, + getOptionValue, +} = require('internal/options'); const { validateString, validateBuffer, - validateUint32 + validateUint32, } = require('internal/validators'); const traceTls = getOptionValue('--trace-tls'); const tlsKeylog = getOptionValue('--tls-keylog'); @@ -1540,25 +1543,14 @@ function onConnectEnd() { } } -let warnOnAllowUnauthorized = true; - // Arguments: [port,] [host,] [options,] [cb] exports.connect = function connect(...args) { args = normalizeConnectArgs(args); let options = args[0]; const cb = args[1]; - const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; - - if (allowUnauthorized && warnOnAllowUnauthorized) { - warnOnAllowUnauthorized = false; - process.emitWarning('Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + - 'environment variable to \'0\' makes TLS connections ' + - 'and HTTPS requests insecure by disabling ' + - 'certificate verification.'); - } options = { - rejectUnauthorized: !allowUnauthorized, + rejectUnauthorized: !getAllowUnauthorized(), ciphers: tls.DEFAULT_CIPHERS, checkServerIdentity: tls.checkServerIdentity, minDHSize: 1024, diff --git a/lib/fs.js b/lib/fs.js index 52b7bdd810e635..3b1e19963e7943 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1154,8 +1154,8 @@ function chmodSync(path, mode) { function lchown(path, uid, gid, callback) { callback = makeCallback(callback); path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = callback; binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req); @@ -1163,8 +1163,8 @@ function lchown(path, uid, gid, callback) { function lchownSync(path, uid, gid) { path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = { path }; binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); @@ -1172,8 +1172,8 @@ function lchownSync(path, uid, gid) { function fchown(fd, uid, gid, callback) { validateInt32(fd, 'fd', 0); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = makeCallback(callback); @@ -1182,8 +1182,8 @@ function fchown(fd, uid, gid, callback) { function fchownSync(fd, uid, gid) { validateInt32(fd, 'fd', 0); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = {}; binding.fchown(fd, uid, gid, undefined, ctx); @@ -1193,8 +1193,8 @@ function fchownSync(fd, uid, gid) { function chown(path, uid, gid, callback) { callback = makeCallback(callback); path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = callback; @@ -1203,8 +1203,8 @@ function chown(path, uid, gid, callback) { function chownSync(path, uid, gid) { path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = { path }; binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); diff --git a/lib/internal/crypto/scrypt.js b/lib/internal/crypto/scrypt.js index cd18f671b18b73..d8c9d67a10d822 100644 --- a/lib/internal/crypto/scrypt.js +++ b/lib/internal/crypto/scrypt.js @@ -108,7 +108,7 @@ function check(password, salt, keylen, options) { } if (options.maxmem !== undefined) { maxmem = options.maxmem; - validateInteger(maxmem, 'maxmem', 0); + validateInteger(maxmem, 'maxmem', { min: 0 }); } if (N === 0) N = defaults.N; if (r === 0) r = defaults.r; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 9f9a0a66f2866e..bd7804203898b6 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1253,6 +1253,62 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath) => { pkgPath}${sep}package.json`; } }, Error); +E('ERR_QUICCLIENTSESSION_FAILED', + 'Failed to create a new QuicClientSession: %s', Error); +E('ERR_QUICCLIENTSESSION_FAILED_SETSOCKET', + 'Failed to set the QuicSocket', Error); +E('ERR_QUICSESSION_DESTROYED', + 'Cannot call %s after a QuicSession has been destroyed', Error); +E('ERR_QUICSESSION_INVALID_DCID', 'Invalid DCID value: %s', Error); +E('ERR_QUICSESSION_UPDATEKEY', 'Unable to update QuicSession keys', Error); +E('ERR_QUICSESSION_VERSION_NEGOTIATION', + (version, requestedVersions, supportedVersions) => { + return 'QUIC session received version negotiation from server. ' + + `Version: ${version}. Requested: ${requestedVersions.join(', ')} ` + + `Supported: ${supportedVersions.join(', ')}`; + }, + Error); +E('ERR_QUICSOCKET_DESTROYED', + 'Cannot call %s after a QuicSocket has been destroyed', Error); +E('ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH', + 'The stateResetToken must be exactly 16-bytes in length', + Error); +E('ERR_QUICSOCKET_LISTENING', + 'This QuicSocket is already listening', Error); +E('ERR_QUICSOCKET_UNBOUND', + 'Cannot call %s before a QuicSocket has been bound', Error); +E('ERR_QUICSTREAM_DESTROYED', + 'Cannot call %s after a QuicStream has been destroyed', Error); +E('ERR_QUICSTREAM_INVALID_PUSH', + 'Push streams are only supported on client-initiated, bidirectional streams', + Error); +E('ERR_QUICSTREAM_OPEN_FAILED', 'Opening a new QuicStream failed', Error); +E('ERR_QUICSTREAM_UNSUPPORTED_PUSH', + 'Push streams are not supported on this QuicSession', Error); +E('ERR_QUIC_ERROR', function(code, family) { + const { + constants: { + QUIC_ERROR_APPLICATION, + QUIC_ERROR_CRYPTO, + QUIC_ERROR_SESSION, + } + } = internalBinding('quic'); + let familyType = 'unknown'; + switch (family) { + case QUIC_ERROR_APPLICATION: + familyType = 'application'; + break; + case QUIC_ERROR_CRYPTO: + familyType = 'crypto'; + break; + case QUIC_ERROR_SESSION: + familyType = 'session'; + break; + } + return `QUIC session closed with ${familyType} error code ${code}`; +}, Error); +E('ERR_QUIC_TLS13_REQUIRED', 'QUIC requires TLS version 1.3', Error); +E('ERR_QUIC_UNAVAILABLE', 'QUIC is unavailable', 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/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 2d73219a77772c..cd5a284010b2d3 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -110,11 +110,40 @@ function stripBOM(content) { } const builtinLibs = [ - 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto', - 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net', - 'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl', - 'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util', - 'v8', 'vm', 'worker_threads', 'zlib' + 'assert', + 'async_hooks', + 'buffer', + 'child_process', + 'cluster', + 'crypto', + 'dgram', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'http2', + 'https', + 'net', + 'os', + 'path', + 'perf_hooks', + 'punycode', + 'quic', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'tls', + 'trace_events', + 'tty', + 'url', + 'util', + 'v8', + 'vm', + 'worker_threads', + 'zlib', ]; if (internalBinding('config').experimentalWasi) { diff --git a/lib/internal/options.js b/lib/internal/options.js index e494787b96c088..03586f9dae6d76 100644 --- a/lib/internal/options.js +++ b/lib/internal/options.js @@ -3,6 +3,8 @@ const { getOptions } = internalBinding('options'); const { options, aliases } = getOptions(); +let warnOnAllowUnauthorized = true; + function getOptionValue(option) { const result = options.get(option); if (!result) { @@ -11,8 +13,23 @@ function getOptionValue(option) { return result.value; } +function getAllowUnauthorized() { + const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; + + if (allowUnauthorized && warnOnAllowUnauthorized) { + warnOnAllowUnauthorized = false; + process.emitWarning( + 'Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + + 'environment variable to \'0\' makes TLS connections ' + + 'and HTTPS requests insecure by disabling ' + + 'certificate verification.'); + } + return allowUnauthorized; +} + module.exports = { options, aliases, - getOptionValue + getOptionValue, + getAllowUnauthorized, }; diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js new file mode 100644 index 00000000000000..e906fb6b23323c --- /dev/null +++ b/lib/internal/quic/core.js @@ -0,0 +1,3341 @@ +'use strict'; + +/* eslint-disable no-use-before-define */ + +const { + assertCrypto, + customInspectSymbol: kInspect, +} = require('internal/util'); + +assertCrypto(); + +const { + Array, + BigInt64Array, + Boolean, + Error, + Map, + RegExp, + Set, + Symbol, +} = primordials; + +const { Buffer } = require('buffer'); +const { isArrayBufferView } = require('internal/util/types'); +const { + getAllowUnauthorized, + getSocketType, + lookup4, + lookup6, + setTransportParams, + toggleListeners, + validateCloseCode, + validateTransportParams, + validateQuicClientSessionOptions, + validateQuicSocketOptions, + validateQuicStreamOptions, + validateQuicSocketListenOptions, + validateQuicEndpointOptions, + validateCreateSecureContextOptions, + validateQuicSocketConnectOptions, +} = require('internal/quic/util'); +const util = require('util'); +const assert = require('internal/assert'); +const EventEmitter = require('events'); +const fs = require('fs'); +const fsPromisesInternal = require('internal/fs/promises'); +const { Duplex } = require('stream'); +const { + createSecureContext: _createSecureContext +} = require('tls'); +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_CALLBACK, + ERR_QUIC_ERROR, + ERR_QUICSESSION_DESTROYED, + ERR_QUICSESSION_VERSION_NEGOTIATION, + ERR_QUICSOCKET_DESTROYED, + ERR_QUICSOCKET_LISTENING, + ERR_QUICCLIENTSESSION_FAILED, + ERR_QUICCLIENTSESSION_FAILED_SETSOCKET, + ERR_QUICSESSION_UPDATEKEY, + ERR_QUICSTREAM_DESTROYED, + ERR_QUICSTREAM_INVALID_PUSH, + ERR_QUICSTREAM_UNSUPPORTED_PUSH, + ERR_QUICSTREAM_OPEN_FAILED, + ERR_TLS_DH_PARAM_SIZE, + }, + 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_INET, + AF_INET6, + IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, + IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, + IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, + IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, + IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, + IDX_QUIC_SESSION_STATS_CREATED_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_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_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_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, + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU, + 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 { + validateBoolean, + validateInteger, + validateNumber, + validateObject, + validateString, +} = require('internal/validators'); + +const emit = EventEmitter.prototype.emit; + +const kAddSession = Symbol('kAddSession'); +const kAddStream = Symbol('kAddStream'); +const kClose = Symbol('kClose'); +const kCert = Symbol('kCert'); +const kClientHello = Symbol('kClientHello'); +const kContinueBind = Symbol('kContinueBind'); +const kContinueConnect = Symbol('kContinueConnect'); +const kDestroy = Symbol('kDestroy'); +const kEndpointBound = Symbol('kEndpointBound'); +const kEndpointClose = Symbol('kEndpointClose'); +const kGetStreamOptions = Symbol('kGetStreamOptions'); +const kHandshake = Symbol('kHandshake'); +const kHandshakePost = Symbol('kHandshakePost'); +const kHeaders = Symbol('kHeaders'); +const kMaybeBind = Symbol('kMaybeBind'); +const kSocketReady = Symbol('kSocketReady'); +const kRemoveSession = Symbol('kRemove'); +const kRemoveStream = Symbol('kRemoveStream'); +const kServerBusy = Symbol('kServerBusy'); +const kSetHandle = Symbol('kSetHandle'); +const kSetSocket = Symbol('kSetSocket'); +const kStreamClose = Symbol('kStreamClose'); +const kStreamReset = Symbol('kStreamReset'); +const kTrackWriteState = Symbol('kTrackWriteState'); +const kUDPHandleForTesting = Symbol('kUDPHandleForTesting'); +const kUsePreferredAddress = Symbol('kUsePreferredAddress'); +const kVersionNegotiation = Symbol('kVersionNegotiation'); + +const kSocketUnbound = 0; +const kSocketPending = 1; +const kSocketBound = 2; +const kSocketClosing = 3; +const kSocketDestroyed = 4; + +let diagnosticPacketLossWarned = false; +let warnedVerifyHostnameIdentity = false; + +assert(process.versions.ngtcp2 !== undefined); + +// Called by the C++ internals when the socket is closed. +// When this is called, the only thing left to do is destroy +// the QuicSocket instance. +function onSocketClose() { + this[owner_symbol].destroy(); +} + +// Called by the C++ internals when an error occurs on the socket. +// When this is called, the only thing left to do is destroy +// the QuicSocket instance with an error. +// TODO(@jasnell): Should consolidate this with onSocketClose +function onSocketError(err) { + this[owner_symbol].destroy(errnoException(err)); +} + +// Called by the C++ internals when the server busy state of +// the QuicSocket has been changed. +function onSocketServerBusy(on) { + this[owner_symbol][kServerBusy](!!on); +} + +// 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[kGetStreamOptions]()); + process.nextTick(emit.bind(socket, 'session', session)); +} + +// During an immediate close, all currently open QuicStreams are +// abruptly closed. If they are still writable or readable, an abort +// event will be emitted, and RESET_STREAM and STOP_SENDING frames +// will be transmitted as necessary. Once streams have been +// shutdown, a CONNECTION_CLOSE frame will be sent and the +// session will enter the closing period, after which it will +// be destroyed either when the idle timeout expires, the +// QuicSession is silently closed, or destroy is called. +function onSessionClose(code, family) { + if (this[owner_symbol]) { + this[owner_symbol][kClose](family, code); + } else { + // When there's no owner_symbol, the session was closed + // before it could be fully set up. Just immediately + // close everything down on the native side. + this.destroy(code, family); + } +} + +// Called by the C++ internals when a QuicSession has been destroyed. +// When this is called, the QuicSession is no longer usable. Removing +// the handle and emitting close is the only action. +// TODO(@jasnell): In the future, this will need to act differently +// for QuicClientSessions when autoResume is enabled. +function onSessionDestroyed() { + const session = this[owner_symbol]; + this[owner_symbol] = undefined; + + if (session) { + session[kSetHandle](); + process.nextTick(emit.bind(session, 'close')); + } +} + +// Used only within the onSessionClientHello function. Invoked +// to complete the client hello process. +function clientHelloCallback(err, ...args) { + if (err) { + this[owner_symbol].destroy(err); + return; + } + try { + this.onClientHelloDone(...args); + } catch (err) { + this[owner_symbol].destroy(err); + } +} + +// 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 'clientHello' event is +// listened for. +function onSessionClientHello(alpn, servername, ciphers) { + this[owner_symbol][kClientHello]( + alpn, + servername, + ciphers, + clientHelloCallback.bind(this)); +} + +// Used only within the onSessionCert function. Invoked +// to complete the session cert process. +function sessionCertCallback(err, context, ocspResponse) { + if (err) { + this[owner_symbol].destroy(err); + return; + } + if (context != null && !context.context) { + this[owner_symbol].destroy( + new ERR_INVALID_ARG_TYPE( + 'context', + 'SecureContext', + context)); + } + if (ocspResponse != null) { + if (typeof ocspResponse === 'string') + ocspResponse = Buffer.from(ocspResponse); + if (!isArrayBufferView(ocspResponse)) { + this[owner_symbol].destroy( + new ERR_INVALID_ARG_TYPE( + 'ocspResponse', + ['string', 'Buffer', 'TypedArray', 'DataView'], + ocspResponse)); + } + } + try { + this.onCertDone(context ? context.context : undefined, ocspResponse); + } catch (err) { + this[owner_symbol].destroy(err); + } +} + +// 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) { + this[owner_symbol][kCert](servername, sessionCertCallback.bind(this)); +} + +// 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(response) { + this[owner_symbol][kCert](response); +} + +// 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( + emit.bind( + 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( + emit.bind( + 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. +function onSessionQlog(str) { + if (this.qlogBuffer === undefined) this.qlogBuffer = ''; + + const session = this[owner_symbol]; + + if (session && session.listenerCount('qlog') > 0) { + // Emit this chunk along with any previously buffered data. + str = this.qlogBuffer + str; + this.qlogBuffer = ''; + if (str === '') return; + session.emit('qlog', str); + } else { + // Buffer the data until both the JS session object and a listener + // become available. + this.qlogBuffer += str; + + if (!session || this.waitingForQlogListener) return; + this.waitingForQlogListener = true; + + function onNewListener(ev) { + if (ev === 'qlog') { + session.removeListener('newListener', onNewListener); + process.nextTick(() => { + onSessionQlog.call(this, ''); + }); + } + } + + session.on('newListener', onNewListener); + } +} + +// Called when an error occurs in a QuicSession. When this happens, +// the only remedy is to destroy the session. +function onSessionError(error) { + if (this[owner_symbol]) { + this[owner_symbol].destroy(error); + } +} + +// 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); + + // TODO(@jasnell): Get default options from session + const uni = id & 0b10; + const { + highWaterMark, + defaultEncoding, + } = session[kGetStreamOptions](); + const stream = new QuicStream({ + writable: !uni, + highWaterMark, + defaultEncoding, + }, session, push_id); + stream[kSetHandle](streamHandle); + if (uni) + stream.end(); + session[kAddStream](id, stream); + process.nextTick(emit.bind(session, 'stream', stream)); +} + +// 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); +} + +// During a silent close, all currently open QuicStreams are abruptly +// closed. If they are still writable or readable, an abort event will be +// emitted, otherwise the stream is just destroyed. No RESET_STREAM or +// STOP_SENDING is transmitted to the peer. +function onSessionSilentClose(statelessReset, code, family) { + this[owner_symbol][kDestroy](statelessReset, family, code); +} + +// When a stream is flow control blocked, causes a blocked event +// to be emitted. This is a purely informational event. +function onStreamBlocked() { + process.nextTick(emit.bind(this[owner_symbol], 'blocked')); +} + +// Register the callbacks with the QUIC internal binding. +setCallbacks({ + onSocketClose, + onSocketError, + onSocketServerBusy, + onSessionReady, + onSessionCert, + onSessionClientHello, + onSessionClose, + onSessionDestroyed, + onSessionError, + onSessionHandshake, + onSessionKeylog, + onSessionQlog, + onSessionSilentClose, + onSessionStatus, + onSessionTicket, + onSessionVersionNegotiation, + onStreamReady, + onStreamClose, + onStreamError, + onStreamReset, + onSessionPathValidation, + onSessionUsePreferredAddress, + onStreamHeaders, + onStreamBlocked, +}); + +// connectAfterLookup is invoked when the QuicSocket connect() +// method has been invoked. The first step is to resolve the given +// remote hostname into an ip address. Once resolution is complete, +// the resolved ip address needs to be passed on to the [kContinueConnect] +// function or the QuicClientSession needs to be destroyed. +function connectAfterLookup(type, err, ip) { + if (err) { + this.destroy(err); + return; + } + this[kContinueConnect](type, ip); +} + +// 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); + // TODO(@jasnell): Determine if it's really necessary to pass in groups here. + init_cb(sc.context, groups, earlyData); + return sc; +} + +function onNewListener(event) { + toggleListeners(this[kHandle], event, true); +} + +function onRemoveListener(event) { + toggleListeners(this[kHandle], event, false); +} + +// QuicEndpoint wraps a UDP socket and is owned +// by a QuicSocket. It does not exist independently +// of the QuicSocket. +class QuicEndpoint { + #state = kSocketUnbound; + #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); + this.#socket = socket; + this.#address = address || (type === AF_INET6 ? '::' : '0.0.0.0'); + this.#ipv6Only = !!ipv6Only; + this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + this.#port = port; + this.#reuseAddr = !!reuseAddr; + this.#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') { + this.#udpSocket.bind(options[kUDPHandleForTesting]); + this.#state = kSocketBound; + this.#socket[kEndpointBound](this); + } + const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + const handle = new QuicEndpointHandle(socket[kHandle], udpHandle); + handle[owner_symbol] = this; + this[kHandle] = handle; + socket[kHandle].addEndpoint(handle, !!preferred); + } + + [kInspect]() { + const obj = { + address: this.address, + fd: this.#fd, + type: this.#type + }; + return `QuicEndpoint ${util.format(obj)}`; + } + + // afterLookup is invoked when binding a QuicEndpoint. The first + // step to binding is to resolve the given hostname into an ip + // address. Once resolution is complete, the ip address needs to + // be passed on to the [kContinueBind] function or the QuicEndpoint + // needs to be destroyed. + static #afterLookup = function(err, ip) { + if (err) { + this.destroy(err); + return; + } + this[kContinueBind](ip); + }; + + // kMaybeBind binds the endpoint on-demand if it is not already + // bound. If it is bound, we return immediately, otherwise put + // the endpoint into the pending state and initiate the binding + // process by calling the lookup to resolve the IP address. + [kMaybeBind]() { + if (this.#state !== kSocketUnbound) + return; + this.#state = kSocketPending; + this.#lookup(this.#address, QuicEndpoint.#afterLookup.bind(this)); + } + + // IP address resolution is completed and we're ready to finish + // binding to the local port. + [kContinueBind](ip) { + const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + if (udpHandle == null) { + // TODO(@jasnell): We may need to throw an error here. Under + // what conditions does this happen? + return; + } + const flags = + (this.#reuseAddr ? UV_UDP_REUSEADDR : 0) | + (this.#ipv6Only ? UV_UDP_IPV6ONLY : 0); + + const ret = udpHandle.bind(ip, this.#port, flags); + if (ret) { + this.destroy(exceptionWithHostPort(ret, 'bind', ip, this.#port || 0)); + return; + } + + // On Windows, the fd will be meaningless, but we always record it. + this.#fd = udpHandle.fd; + this.#state = kSocketBound; + + // Notify the owning socket that the QuicEndpoint has been successfully + // bound to the local UDP port. + this.#socket[kEndpointBound](this); + } + + [kDestroy](error) { + const handle = this[kHandle]; + if (handle !== undefined) { + this[kHandle] = undefined; + handle[owner_symbol] = undefined; + handle.ondone = () => { + this.#udpSocket.close((err) => { + if (err) error = err; + this.#socket[kEndpointClose](this, error); + }); + }; + handle.waitForPendingCallbacks(); + } + } + + // 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() { + if (this.#state !== kSocketDestroyed) { + try { + return this.#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 {}; + } + + get fd() { + return this.#fd; + } + + // True if the QuicEndpoint has been destroyed and is + // no longer usable. + get destroyed() { + return this.#state === kSocketDestroyed; + } + + // True if binding has been initiated and is in progress. + get pending() { + return this.#state === kSocketPending; + } + + // True if the QuicEndpoint has been bound to the local + // UDP port. + get bound() { + return this.#state === kSocketBound; + } + + setTTL(ttl) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setTTL'); + this.#udpSocket.setTTL(ttl); + return this; + } + + setMulticastTTL(ttl) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastTTL'); + this.#udpSocket.setMulticastTTL(ttl); + return this; + } + + setBroadcast(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setBroadcast'); + this.#udpSocket.setBroadcast(on); + return this; + } + + setMulticastLoopback(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastLoopback'); + this.#udpSocket.setMulticastLoopback(on); + return this; + } + + setMulticastInterface(iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastInterface'); + this.#udpSocket.setMulticastInterface(iface); + return this; + } + + addMembership(address, iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('addMembership'); + this.#udpSocket.addMembership(address, iface); + return this; + } + + dropMembership(address, iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('dropMembership'); + this.#udpSocket.dropMembership(address, iface); + return this; + } + + ref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('ref'); + this.#udpSocket.ref(); + return this; + } + + unref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('unref'); + this.#udpSocket.unref(); + return this; + } + + destroy(error) { + // If the QuicEndpoint is already destroyed, do nothing + if (this.#state === kSocketDestroyed) + return; + + // Mark the QuicEndpoint as being destroyed. + this.#state = kSocketDestroyed; + + this[kDestroy](error); + } +} + +// 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 { + #alpn = undefined; + #autoClose = undefined; + #client = undefined; + #defaultEncoding = undefined; + #endpoints = new Set(); + #highWaterMark = undefined; + #lookup = undefined; + #server = undefined; + #serverBusy = false; + #serverListening = false; + #serverSecureContext = undefined; + #sessions = new Set(); + #state = kSocketUnbound; + #stats = undefined; + + constructor(options) { + const { + endpoint, + + // True if the QuicSocket should automatically enter a graceful shutdown + // if it is not listening as a server and the last QuicClientSession + // closes + autoClose, + + // 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, + + // UDP type + type, + + // True if address verification should be used. + validateAddress, + + // True if an LRU should be used for add validation + validateAddressLRU, + + // 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 }); + + this.#autoClose = autoClose; + this.#client = client; + this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + this.#server = server; + + const socketOptions = + (validateAddress ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS : 0) | + (validateAddressLRU ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU : 0); + + this[kSetHandle]( + new QuicSocketHandle( + socketOptions, + retryTokenTimeout, + maxConnections, + maxConnectionsPerHost, + maxStatelessResetsPerHost, + qlog, + statelessResetSecret, + disableStatelessReset)); + + this.addEndpoint({ + lookup: this.#lookup, + // Keep the lookup and ...endpoint in this order + // to allow the passed in endpoint options to + // override the lookup specifically for that endpoint + ...endpoint, + preferred: true + }); + } + + // Returns the default QuicStream options for peer-initiated + // streams. These are passed on to new client and server + // QuicSession instances when they are created. + [kGetStreamOptions]() { + return { + highWaterMark: this.#highWaterMark, + defaultEncoding: this.#defaultEncoding, + }; + } + + [kSetHandle](handle) { + this[kHandle] = handle; + if (handle !== undefined) { + handle[owner_symbol] = this; + this[async_id_symbol] = handle.getAsyncId(); + } + } + + [kInspect]() { + const obj = { + endpoints: this.endpoints, + sessions: this.#sessions, + }; + return `QuicSocket ${util.format(obj)}`; + } + + [kAddSession](session) { + this.#sessions.add(session); + } + + [kRemoveSession](session) { + this.#sessions.delete(session); + this[kMaybeDestroy](); + } + + // Bind all QuicEndpoints on-demand, only if they haven't already been bound. + // Function is a non-op if the socket is already bound or in the process of + // being bound, and will call the callback once the socket is ready. + [kMaybeBind](callback = () => {}) { + if (this.#state === kSocketBound) + return process.nextTick(callback); + + this.once('ready', callback); + + if (this.#state === kSocketPending) + return; + this.#state = kSocketPending; + + for (const endpoint of this.#endpoints) + endpoint[kMaybeBind](); + } + + [kEndpointBound](endpoint) { + if (this.#state === kSocketBound) + return; + this.#state = kSocketBound; + + // Once the QuicSocket has been bound, we notify all currently + // existing QuicSessions. QuicSessions created after this + // point will automatically be notified that the QuicSocket + // is ready. + for (const session of this.#sessions) + session[kSocketReady](); + + // 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(emit.bind(this, 'ready')); + } + + // Called when a QuicEndpoint closes + [kEndpointClose](endpoint, error) { + this.#endpoints.delete(endpoint); + process.nextTick(emit.bind(this, 'endpointClose', endpoint, error)); + + // If there are no more QuicEndpoints, the QuicSocket is no + // longer usable. + if (this.#endpoints.size === 0) { + // Ensure that there are absolutely no additional sessions + for (const session of this.#sessions) + session.destroy(error); + + if (error) process.nextTick(emit.bind(this, 'error', error)); + process.nextTick(emit.bind(this, 'close')); + } + } + + // kDestroy is called to actually free the QuicSocket resources and + // cause the error and close events to be emitted. + [kDestroy](error) { + // The QuicSocket will be destroyed once all QuicEndpoints + // are destroyed. See [kEndpointClose]. + for (const endpoint of this.#endpoints) + endpoint.destroy(error); + } + + // kMaybeDestroy is called one or more times after the close() method + // is called. The QuicSocket will be destroyed if there are no remaining + // open sessions. + [kMaybeDestroy]() { + if (this.closing && this.#sessions.size === 0) { + this.destroy(); + return true; + } + return false; + } + + // Called by the C++ internals to notify when server busy status is toggled. + [kServerBusy](on) { + this.#serverBusy = on; + process.nextTick(emit.bind(this, 'busy', on)); + } + + addEndpoint(options = {}) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('listen'); + + // TODO(@jasnell): Also forbid adding an endpoint if + // the QuicSocket is closing. + + const endpoint = new QuicEndpoint(this, options); + this.#endpoints.add(endpoint); + + // If the QuicSocket is already bound at this point, + // also bind the newly created QuicEndpoint. + if (this.#state !== kSocketUnbound) + endpoint[kMaybeBind](); + + return endpoint; + } + + // Used only from within the #continueListen function. When a preferred + // address has been provided, the hostname given must be resolved into an + // IP address, which must be passed on to #completeListen or the QuicSocket + // needs to be destroyed. + static #afterPreferredAddressLookup = function( + transportParams, + port, + type, + err, + address) { + if (err) { + this.destroy(err); + return; + } + this.#completeListen(transportParams, { address, port, type }); + }; + + // The #completeListen function is called after all of the necessary + // DNS lookups have been performed and we're ready to let the C++ + // internals begin listening for new QuicServerSession instances. + #completeListen = function(transportParams, preferredAddress) { + const { + address, + port, + type = AF_INET, + } = { ...preferredAddress }; + + 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); + + const options = + (rejectUnauthorized ? QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED : 0) | + (requestCert ? QUICSERVERSESSION_OPTION_REQUEST_CERT : 0); + + // 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. + this[kHandle].listen( + this.#serverSecureContext.context, + address, + type, + port, + this.#alpn, + options); + process.nextTick(emit.bind(this, 'listening')); + }; + + // When the QuicSocket listen() function is called, the first step is to bind + // the underlying QuicEndpoint's. Once at least one endpoint has been bound, + // the continueListen function is invoked to continue the process of starting + // to listen. + // + // The preferredAddress is a preferred network endpoint that the server wishes + // connecting clients to use. If specified, this will be communicate to the + // client as part of the tranport parameters exchanged during the TLS + // handshake. + #continueListen = function(transportParams, lookup) { + const { preferredAddress } = transportParams; + + // TODO(@jasnell): Currently, we wait to start resolving the + // preferred address until after we know the QuicSocket is + // bound. That's not strictly necessary as we can resolve the + // preferred address in parallel, which out to be faster but + // is a slightly more complicated to coordinate. In the future, + // we should do those things in parallel. + if (preferredAddress && typeof preferredAddress === 'object') { + const { + address, + port, + type = 'udp4', + } = { ...preferredAddress }; + const typeVal = getSocketType(type); + // If preferred address is set, we need to perform a lookup on it + // to get the IP address. Only after that lookup completes can we + // continue with the listen operation, passing in the resolved + // preferred address. + lookup( + address || (typeVal === AF_INET6 ? '::' : '0.0.0.0'), + QuicSocket.#afterPreferredAddressLookup.bind( + this, + transportParams, + port, + typeVal)); + return; + } + // If preferred address is not set, we can skip directly to the listen + this.#completeListen(transportParams); + }; + + // Begin listening for server connections. The callback that may be + // passed to this function is registered as a handler for the + // on('session') event. Errors may be thrown synchronously by this + // function. + listen(options, callback) { + if (this.#serverListening) + throw new ERR_QUICSOCKET_LISTENING(); + + if (this.#state === kSocketDestroyed || + this.#state === kSocketClosing) { + throw new ERR_QUICSOCKET_DESTROYED('listen'); + } + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (callback !== undefined && typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(callback); + + options = { + secureProtocol: 'TLSv1_3_server_method', + ...this.#server, + ...options + }; + + // The ALPN protocol identifier is strictly required. + const { + alpn, + defaultEncoding, + highWaterMark, + transportParams, + } = validateQuicSocketListenOptions(options); + + // Store the secure context so that it is not garbage collected + // while we still need to make use of it. + this.#serverSecureContext = + createSecureContext( + options, + initSecureContext); + + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + this.#serverListening = true; + this.#alpn = alpn; + + // If the callback function is provided, it is registered as a + // handler for the on('session') event and will be called whenever + // there is a new QuicServerSession instance created. + if (callback) + this.on('session', callback); + + // Bind the QuicSocket to the local port if it hasn't been bound already. + this[kMaybeBind](this.#continueListen.bind( + this, + transportParams, + this.#lookup)); + } + + // When the QuicSocket connect() function is called, the first step is to bind + // the underlying QuicEndpoint's. Once at least one endpoint has been bound, + // the connectAfterBind function is invoked to continue the connection + // process. + // + // The immediate next step is to resolve the address into an ip address. + #continueConnect = function(session, lookup, address, type) { + // TODO(@jasnell): Currently, we perform the DNS resolution after + // the QuicSocket has been bound. We don't have to. We could do + // it in parallel while we're waitint to be bound but doing so + // is slightly more complicated. + + // Notice here that connectAfterLookup is bound to the QuicSession + // that was created... + lookup( + address || (type === AF_INET6 ? '::' : '0.0.0.0'), + connectAfterLookup.bind(session, type)); + }; + + // Creates and returns a new QuicClientSession. + connect(options, callback) { + if (this.#state === kSocketDestroyed || + this.#state === kSocketClosing) { + throw new ERR_QUICSOCKET_DESTROYED('connect'); + } + + if (typeof options === 'function') { + callback = options; + options = undefined; + } + + options = { + ...this.#client, + ...options + }; + + const { + type, + address, + } = validateQuicSocketConnectOptions(options); + + const session = new QuicClientSession(this, options); + + // TODO(@jasnell): This likely should listen for the secure event + // rather than the ready event + if (typeof callback === 'function') + session.once('ready', callback); + + this[kMaybeBind](this.#continueConnect.bind( + this, + session, + this.#lookup, + address, + type)); + + return session; + } + + // 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. + // + // If specified, the callback will be registered for once('close'). + // + // 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. + // + // Subsequent calls to close(callback) will register the close callback + // if one is defined but will otherwise do nothing. + // + // 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 callback is registered for the + // once('close') event (if specified) and the QuicSocket is destroyed + // immediately. + close(callback) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('close'); + + // If a callback function is specified, it is registered as a + // handler for the once('close') event. If the close occurs + // immediately, the close event will be emitted as soon as the + // process.nextTick queue is processed. Otherwise, the close + // event will occur at some unspecified time in the near future. + if (callback) { + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + this.once('close', callback); + } + + // If we are already closing, do nothing else and wait + // for the close event to be invoked. + if (this.#state === kSocketClosing) + return; + + // If the QuicSocket is otherwise not bound to the local + // port, destroy the QuicSocket immediately. + if (this.#state !== kSocketBound) { + this.destroy(); + } + + // Mark the QuicSocket as closing to prevent re-entry + this.#state = kSocketClosing; + + // Otherwise, gracefully close each QuicSession, with + // [kMaybeDestroy]() being called after each closes. + const maybeDestroy = this[kMaybeDestroy].bind(this); + + // 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]) { + this[kHandle].stopListening(); + } + this.#serverListening = false; + + // If there are no sessions, calling maybeDestroy + // will immediately and synchronously destroy the + // QuicSocket. + if (maybeDestroy()) + return; + + // If we got this far, there a QuicClientSession and + // QuicServerSession instances still, we need to trigger + // a graceful close for each of them. As each closes, + // they will call the kMaybeDestroy function. When there + // are no remaining session instances, the QuicSocket + // will be closed and destroyed. + for (const session of this.#sessions) + session.close(maybeDestroy); + } + + // 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) { + // If the QuicSocket is already destroyed, do nothing + if (this.#state === kSocketDestroyed) + return; + + // Mark the QuicSocket as being destroyed. + this.#state = kSocketDestroyed; + + // 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 this.#sessions) + session.destroy(error); + + this[kDestroy](error); + } + + ref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('ref'); + for (const endpoint of this.#endpoints) + endpoint.ref(); + return this; + } + + unref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('unref'); + for (const endpoint of this.#endpoints) + endpoint.unref(); + return this; + } + + get endpoints() { + return Array.from(this.#endpoints); + } + + // The sever secure context is the SecureContext specified when calling + // listen. It is the context that will be used with all new server + // QuicSession instances. + get serverSecureContext() { + return this.#serverSecureContext; + } + + // True if at least one associated QuicEndpoint has been successfully + // bound to a local UDP port. + get bound() { + return this.#state === kSocketBound; + } + + // True if graceful close has been initiated by calling close() + get closing() { + return this.#state === kSocketClosing; + } + + // True if the QuicSocket has been destroyed and is no longer usable + get destroyed() { + return this.#state === kSocketDestroyed; + } + + // True if listen() has been called successfully + get listening() { + return this.#serverListening; + } + + // True if the QuicSocket is currently waiting on at least one + // QuicEndpoint to succesfully bind.g + get pending() { + return this.#state === kSocketPending; + } + + // Marking a server as busy will cause all new + // connection attempts to fail with a SERVER_BUSY CONNECTION_CLOSE. + setServerBusy(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setServerBusy'); + validateBoolean(on, 'on'); + if (this.#serverBusy !== on) + this[kHandle].setServerBusy(on); + } + + get duration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_CREATED_AT]; + } + + get boundDuration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_BOUND_AT]; + } + + get listenDuration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_LISTEN_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_BYTES_SENT]; + } + + get packetsReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED]; + } + + get packetsSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_SENT]; + } + + get packetsIgnored() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED]; + } + + get serverBusy() { + return this.#serverBusy; + } + + get serverSessions() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS]; + } + + get clientSessions() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS]; + } + + get statelessResetCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT]; + } + + get serverBusyCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[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.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setDiagnosticPacketLoss'); + 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); + } + + // Toggles stateless reset on/off. By default, stateless reset tokens + // are generated when necessary. The disableStatelessReset option may + // be used when the QuicSocket is created to disable generation of + // stateless resets. The toggleStatelessReset method allows the setting + // to be switched on/off dynamically through the lifetime of the + // socket. + toggleStatelessReset() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('toggleStatelessReset'); + return this[kHandle].toggleStatelessReset(); + } +} + +class QuicSession extends EventEmitter { + #alpn = undefined; + #cipher = undefined; + #cipherVersion = undefined; + #closeCode = NGTCP2_NO_ERROR; + #closeFamily = QUIC_ERROR_APPLICATION; + #closing = false; + #destroyed = false; + #earlyData = false; + #handshakeComplete = false; + #idleTimeout = false; + #maxPacketLength = IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT; + #servername = undefined; + #socket = undefined; + #statelessReset = false; + #stats = undefined; + #pendingStreams = new Set(); + #streams = new Map(); + #verifyErrorReason = undefined; + #verifyErrorCode = undefined; + #handshakeAckHistogram = undefined; + #handshakeContinuationHistogram = undefined; + #highWaterMark = undefined; + #defaultEncoding = undefined; + + constructor(socket, options) { + const { + alpn, + servername, + highWaterMark, + defaultEncoding, + } = options; + super({ captureRejections: true }); + this.on('newListener', onNewListener); + this.on('removeListener', onRemoveListener); + this.#socket = socket; + this.#servername = servername; + this.#alpn = alpn; + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + socket[kAddSession](this); + } + + // kGetStreamOptions is called to get the configured options + // for peer initiated QuicStream instances. + [kGetStreamOptions]() { + return { + highWaterMark: this.#highWaterMark, + defaultEncoding: this.#defaultEncoding, + }; + } + + // 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) { + this[kHandle] = handle; + if (handle !== undefined) { + handle[owner_symbol] = this; + this.#handshakeAckHistogram = new Histogram(handle.ack); + this.#handshakeContinuationHistogram = new Histogram(handle.rate); + } else { + if (this.#handshakeAckHistogram) + this.#handshakeAckHistogram[kDestroyHistogram](); + if (this.#handshakeContinuationHistogram) + this.#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_QUICSESSION_VERSION_NEGOTIATION( + version, + requestedVersions, + supportedVersions); + err.detail = { + version, + requestedVersions, + supportedVersions, + }; + this.destroy(err); + } + + // Causes the QuicSession to be immediately destroyed, but with + // additional metadata set. + [kDestroy](statelessReset, family, code) { + this.#statelessReset = !!statelessReset; + this.#closeCode = code; + this.#closeFamily = family; + this.destroy(); + } + + // Immediate close has been initiated for the session. Any + // still open QuicStreams must be abandoned and shutdown + // with RESET_STREAM and STOP_SENDING frames transmitted + // as appropriate. Once the streams have been shutdown, a + // CONNECTION_CLOSE will be generated and sent, switching + // the session into the closing period. + [kClose](family, code) { + // Do nothing if the QuicSession has already been destroyed. + if (this.#destroyed) + return; + + // Set the close code and family so we can keep track. + this.#closeCode = code; + this.#closeFamily = family; + + // Shutdown all pending streams. These are Streams that + // have been created but do not yet have a handle assigned. + for (const stream of this.#pendingStreams) + stream[kClose](family, code); + + // Shutdown all of the remaining streams + for (const stream of this.#streams.values()) + stream[kClose](family, code); + + // By this point, all necessary RESET_STREAM and + // STOP_SENDING frames ought to have been sent, + // so now we just trigger sending of the + // CONNECTION_CLOSE frame. + this[kHandle].close(family, code); + } + + // Closes the specified stream with the given code. The + // QuicStream object will be destroyed. + [kStreamClose](id, code) { + const stream = this.#streams.get(id); + if (stream === undefined) + return; + + stream.destroy(); + } + + // 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.#streams.get(id); + if (stream === undefined) + return; + + stream[kHeaders](headers, kind, push_id); + } + + [kStreamReset](id, code) { + const stream = this.#streams.get(id); + if (stream === undefined) + return; + + stream[kStreamReset](code); + } + + [kInspect]() { + const obj = { + alpn: this.#alpn, + cipher: this.cipher, + closing: this.closing, + closeCode: this.closeCode, + destroyed: this.destroyed, + earlyData: this.#earlyData, + maxStreams: this.maxStreams, + servername: this.servername, + streams: this.#streams.size, + stats: { + handshakeAck: this.handshakeAckHistogram, + handshakeContinuation: this.handshakeContinuationHistogram, + } + }; + return `${this.constructor.name} ${util.format(obj)}`; + } + + [kSetSocket](socket) { + this.#socket = socket; + } + + // Called at the completion of the TLS handshake for the local peer + [kHandshake]( + servername, + alpn, + cipher, + cipherVersion, + maxPacketLength, + verifyErrorReason, + verifyErrorCode, + earlyData) { + this.#handshakeComplete = true; + this.#servername = servername; + this.#alpn = alpn; + this.#cipher = cipher; + this.#cipherVersion = cipherVersion; + this.#maxPacketLength = maxPacketLength; + this.#verifyErrorReason = verifyErrorReason; + this.#verifyErrorCode = verifyErrorCode; + this.#earlyData = earlyData; + if (!this[kHandshakePost]()) + return; + + process.nextTick( + emit.bind(this, 'secure', servername, alpn, this.cipher)); + } + + // Non-op for the default case. QuicClientSession + // overrides this with some client-side specific + // checks + [kHandshakePost]() { + return true; + } + + [kRemoveStream](stream) { + this.#streams.delete(stream.id); + } + + [kAddStream](id, stream) { + stream.once('close', this[kMaybeDestroy].bind(this)); + this.#streams.set(id, stream); + } + + // The QuicSession will be destroyed if closing has been + // called and there are no remaining streams + [kMaybeDestroy]() { + if (this.#closing && this.#streams.size === 0) + this.destroy(); + } + + // 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( + emit.bind(this, 'usePreferredAddress', address)); + } + + // Closing allows any existing QuicStream's to complete + // normally but disallows any new QuicStreams from being + // opened. Calls to openStream() will fail, and new streams + // from the peer will be rejected/ignored. + close(callback) { + if (this.#destroyed) + throw new ERR_QUICSESSION_DESTROYED('close'); + + if (callback) { + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + this.once('close', callback); + } + + // If we're already closing, do nothing else. + // Callback will be invoked once the session + // has been destroyed + if (this.#closing) + return; + + this.#closing = true; + this[kHandle].gracefulClose(); + + // See if we can close immediately. + this[kMaybeDestroy](); + } + + // Destroying synchronously shuts down and frees the + // QuicSession immediately, even if there are still open + // streams. + // + // A CONNECTION_CLOSE packet is sent to the + // connected peer and the session is 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) { + // Destroy can only be called once. Multiple calls will be ignored + if (this.#destroyed) + return; + this.#destroyed = true; + this.#closing = false; + + if (typeof error === 'number' || + (error != null && + typeof error === 'object' && + !(error instanceof Error))) { + const { + closeCode, + closeFamily + } = validateCloseCode(error); + this.#closeCode = closeCode; + this.#closeFamily = closeFamily; + error = new ERR_QUIC_ERROR(closeCode, closeFamily); + } + + // Destroy any pending streams immediately. These + // are streams that have been created but have not + // yet been assigned an internal handle. + for (const stream of this.#pendingStreams) + stream.destroy(error); + + // Destroy any remaining streams immediately. + for (const stream of this.#streams.values()) + stream.destroy(error); + + this.removeListener('newListener', onNewListener); + this.removeListener('removeListener', onRemoveListener); + + if (error) process.nextTick(emit.bind(this, 'error', error)); + + const handle = this[kHandle]; + if (handle !== undefined) { + // Copy the stats for use after destruction + this.#stats = new BigInt64Array(handle.stats); + this.#idleTimeout = !!handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]; + // Calling destroy will cause a CONNECTION_CLOSE to be + // sent to the peer and will destroy the QuicSession + // handler immediately. + handle.destroy(this.#closeCode, this.#closeFamily); + } else { + process.nextTick(emit.bind(this, 'close')); + } + + // Remove the QuicSession JavaScript object from the + // associated QuicSocket. + this.#socket[kRemoveSession](this); + this.#socket = undefined; + } + + // 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.#earlyData; + } + + get maxStreams() { + let bidi = 0; + let uni = 0; + if (this[kHandle]) { + bidi = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI]; + uni = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI]; + } + return { bidi, uni }; + } + + get address() { + return this.#socket ? this.#socket.address : {}; + } + + get maxDataLeft() { + return this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] : 0; + } + + get bytesInFlight() { + return this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] : 0; + } + + get blockCount() { + return this[kHandle] ? + this[kHandle].state[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.#verifyErrorReason == null; + } + + get authenticationError() { + if (this.authenticated) + return undefined; + // eslint-disable-next-line no-restricted-syntax + const err = new Error(this.#verifyErrorReason); + const code = 'ERR_QUIC_VERIFY_' + this.#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.#handshakeComplete; + } + + get handshakeConfirmed() { + return Boolean(this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] : 0); + } + + get idleTimeout() { + return this.#idleTimeout; + } + + get alpnProtocol() { + return this.#alpn; + } + + get cipher() { + const name = this.#cipher; + const version = this.#cipherVersion; + return this.handshakeComplete ? { name, version } : {}; + } + + getCertificate() { + return this[kHandle] ? + translatePeerCertificate(this[kHandle].getCertificate() || {}) : {}; + } + + getPeerCertificate(detailed = false) { + return this[kHandle] ? + translatePeerCertificate( + this[kHandle].getPeerCertificate(detailed) || {}) : {}; + } + + ping() { + if (!this[kHandle]) + throw new ERR_QUICSESSION_DESTROYED('ping'); + this[kHandle].ping(); + } + + get servername() { + return this.#servername; + } + + get destroyed() { + return this.#destroyed; + } + + get closing() { + return this.#closing; + } + + get closeCode() { + return { + code: this.#closeCode, + family: this.#closeFamily + }; + } + + get socket() { + return this.#socket; + } + + get statelessReset() { + return this.#statelessReset; + } + + openStream(options) { + if (this.#destroyed || this.#closing) + throw new ERR_QUICSESSION_DESTROYED('openStream'); + const { + halfOpen, // Unidirectional or Bidirectional + highWaterMark, + defaultEncoding, + } = validateQuicStreamOptions(options); + + const stream = new QuicStream({ + highWaterMark, + defaultEncoding, + readable: !halfOpen + }, this); + + // TODO(@jasnell): This really shouldn't be necessary + if (halfOpen) { + stream.push(null); + stream.read(); + } + + this.#pendingStreams.add(stream); + + // If early data is being used, we can create the internal QuicStream on the + // ready event, that is immediately after the internal QuicSession handle + // has been created. Otherwise, we have to wait until the secure event + // signaling the completion of the TLS handshake. + const makeStream = QuicSession.#makeStream.bind(this, stream, halfOpen); + let deferred = false; + if (this.allowEarlyData && !this.ready) { + deferred = true; + this.once('ready', makeStream); + } else if (!this.handshakeComplete) { + deferred = true; + this.once('secure', makeStream); + } + + if (!deferred) + makeStream(stream, halfOpen); + + return stream; + } + + static #makeStream = function(stream, halfOpen) { + this.#pendingStreams.delete(stream); + const handle = + halfOpen ? + _openUnidirectionalStream(this[kHandle]) : + _openBidirectionalStream(this[kHandle]); + + if (handle === undefined) { + stream.destroy(new ERR_QUICSTREAM_OPEN_FAILED()); + return; + } + + stream[kSetHandle](handle); + this[kAddStream](stream.id, stream); + }; + + get duration() { + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SESSION_STATS_CREATED_AT]; + } + + get handshakeDuration() { + const stats = this.#stats || this[kHandle].stats; + const end = + this.handshakeComplete ? + stats[4] : process.hrtime.bigint(); + return end - stats[IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BYTES_SENT]; + } + + get bidiStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT]; + } + + get uniStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT]; + } + + get maxInFlightBytes() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT]; + } + + get lossRetransmitCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT]; + } + + get ackDelayRetransmitCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT]; + } + + get peerInitiatedStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT]; + } + + get selfInitiatedStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT]; + } + + get keyUpdateCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT]; + } + + get minRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_MIN_RTT]; + } + + get latestRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_LATEST_RTT]; + } + + get smoothedRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_SMOOTHED_RTT]; + } + + updateKey() { + // Initiates a key update for the connection. + if (this.#destroyed || this.#closing) + throw new ERR_QUICSESSION_DESTROYED('updateKey'); + if (!this.handshakeConfirmed) + throw new ERR_QUICSESSION_UPDATEKEY(); + return this[kHandle].updateKey(); + } + + get handshakeAckHistogram() { + return this.#handshakeAckHistogram; + } + + get handshakeContinuationHistogram() { + return this.#handshakeContinuationHistogram; + } + + // TODO(addaleax): This is a temporary solution for testing and should be + // removed later. + removeFromSocket() { + return this[kHandle].removeFromSocket(); + } +} + +class QuicServerSession extends QuicSession { + #contexts = []; + + constructor(socket, handle, options) { + const { + highWaterMark, + defaultEncoding, + } = options; + super(socket, { highWaterMark, defaultEncoding }); + this[kSetHandle](handle); + + // Both the handle and socket are immediately usable + // at this point so trigger the ready event. + this[kSocketReady](); + } + + // Called only when a clientHello event handler is registered. + // Allows user code an opportunity to interject into the start + // of the TLS handshake. + [kClientHello](alpn, servername, ciphers, callback) { + this.emit( + 'clientHello', + alpn, + servername, + ciphers, + callback.bind(this[kHandle])); + } + + // Called only when an OCSPRequest event handler is registered. + // Allows user code the ability to answer the OCSP query. + [kCert](servername, callback) { + const { serverSecureContext } = this.socket; + let { context } = serverSecureContext; + + for (var i = 0; i < this.#contexts.length; i++) { + const elem = this.#contexts[i]; + if (elem[0].test(servername)) { + context = elem[1]; + break; + } + } + + this.emit( + 'OCSPRequest', + servername, + context, + callback.bind(this[kHandle])); + } + + [kSocketReady]() { + process.nextTick(emit.bind(this, 'ready')); + } + + get ready() { return true; } + + get allowEarlyData() { return false; } + + addContext(servername, context = {}) { + validateString(servername, 'servername'); + validateObject(context, 'context'); + + // TODO(@jasnell): Consider unrolling regex + const re = new RegExp('^' + + servername.replace(/([.^$+?\-\\[\]{}])/g, '\\$1') + .replace(/\*/g, '[^.]*') + + '$'); + this.#contexts.push([re, _createSecureContext(context)]); + } +} + +class QuicClientSession extends QuicSession { + #allowEarlyData = false; + #autoStart = true; + #dcid = undefined; + #handshakeStarted = false; + #ipv6Only = undefined; + #minDHSize = undefined; + #port = undefined; + #ready = 0; + #remoteTransportParams = undefined; + #requestOCSP = undefined; + #secureContext = undefined; + #sessionTicket = undefined; + #transportParams = undefined; + #preferredAddressPolicy; + #verifyHostnameIdentity = true; + #qlogEnabled = false; + + constructor(socket, options) { + const sc_options = { + secureProtocol: 'TLSv1_3_client_method', + ...options + }; + const { + autoStart, + alpn, + dcid, + ipv6Only, + minDHSize, + port, + preferredAddressPolicy, + remoteTransportParams, + requestOCSP, + 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 }); + this.#autoStart = autoStart; + this.#handshakeStarted = autoStart; + this.#dcid = dcid; + this.#ipv6Only = ipv6Only; + this.#minDHSize = minDHSize; + this.#port = port || 0; + this.#preferredAddressPolicy = preferredAddressPolicy; + this.#requestOCSP = requestOCSP; + this.#secureContext = + createSecureContext( + sc_options, + initSecureContextClient); + this.#transportParams = validateTransportParams(options); + this.#verifyHostnameIdentity = verifyHostnameIdentity; + this.#qlogEnabled = qlog; + + // If provided, indicates that the client is attempting to + // resume a prior session. Early data would be enabled. + this.#remoteTransportParams = remoteTransportParams; + this.#sessionTicket = sessionTicket; + this.#allowEarlyData = + remoteTransportParams !== undefined && + sessionTicket !== undefined; + + if (socket.bound) + this[kSocketReady](); + } + + [kHandshakePost]() { + const { type, size } = this.ephemeralKeyInfo; + if (type === 'DH' && size < this.#minDHSize) { + this.destroy(new ERR_TLS_DH_PARAM_SIZE(size)); + return false; + } + return true; + } + + [kCert](response) { + this.emit('OCSPResponse', response); + } + + [kContinueConnect](type, ip) { + setTransportParams(this.#transportParams); + + const options = + (this.#verifyHostnameIdentity ? + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) | + (this.#requestOCSP ? + QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0); + + const handle = + _createClientSession( + this.socket[kHandle], + type, + ip, + this.#port, + this.#secureContext.context, + this.servername || ip, + this.#remoteTransportParams, + this.#sessionTicket, + this.#dcid, + this.#preferredAddressPolicy, + this.alpnProtocol, + options, + this.#qlogEnabled, + this.#autoStart); + + // We no longer need these, unset them so + // memory can be garbage collected. + this.#remoteTransportParams = undefined; + this.#sessionTicket = undefined; + this.#dcid = undefined; + + // If handle is a number, creating the session failed. + if (typeof handle === 'number') { + let reason; + switch (handle) { + case ERR_FAILED_TO_CREATE_SESSION: + reason = 'QuicSession bootstrap failed'; + break; + case ERR_INVALID_REMOTE_TRANSPORT_PARAMS: + reason = 'Invalid Remote Transport Params'; + break; + case ERR_INVALID_TLS_SESSION_TICKET: + reason = 'Invalid TLS Session Ticket'; + break; + default: + reason = `${handle}`; + } + this.destroy(new ERR_QUICCLIENTSESSION_FAILED(reason)); + return; + } + + this[kSetHandle](handle); + + // Listeners may have been added before the handle was created. + // Ensure that we toggle those listeners in the handle state. + + if (this.listenerCount('keylog') > 0) + toggleListeners(handle, 'keylog', true); + + if (this.listenerCount('pathValidation') > 0) + toggleListeners(handle, 'pathValidation', true); + + if (this.listenerCount('usePreferredAddress') > 0) + toggleListeners(handle, 'usePreferredAddress', true); + + this.#maybeReady(0x2); + } + + [kSocketReady]() { + this.#maybeReady(0x1); + } + + // The QuicClientSession is ready for use only after + // (a) The QuicSocket has been bound and + // (b) The internal handle has been created + #maybeReady = function(flag) { + this.#ready |= flag; + if (this.ready) + process.nextTick(emit.bind(this, 'ready')); + }; + + get allowEarlyData() { + return this.#allowEarlyData; + } + + get ready() { + return this.#ready === 0x3; + } + + get handshakeStarted() { + return this.#handshakeStarted; + } + + startHandshake() { + if (this.destroyed) + throw new ERR_QUICSESSION_DESTROYED('startHandshake'); + if (this.#handshakeStarted) + return; + this.#handshakeStarted = true; + if (!this.ready) { + this.once('ready', () => this[kHandle].startHandshake()); + } else { + this[kHandle].startHandshake(); + } + } + + get ephemeralKeyInfo() { + return this[kHandle] !== undefined ? + this[kHandle].getEphemeralKeyInfo() : + {}; + } + + #setSocketAfterBind = function(socket, callback) { + if (socket.destroyed) { + callback(new ERR_QUICSOCKET_DESTROYED('setSocket')); + return; + } + + if (!this[kHandle].setSocket(socket[kHandle])) { + callback(new ERR_QUICCLIENTSESSION_FAILED_SETSOCKET()); + return; + } + + if (this.socket) { + this.socket[kRemoveSession](this); + this[kSetSocket](undefined); + } + socket[kAddSession](this); + this[kSetSocket](socket); + + callback(); + }; + + setSocket(socket, callback) { + if (!(socket instanceof QuicSocket)) + throw new ERR_INVALID_ARG_TYPE('socket', 'QuicSocket', socket); + + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + + socket[kMaybeBind](() => this.#setSocketAfterBind(socket, callback)); + } +} + +function streamOnResume() { + if (!this.destroyed) + this[kHandle].readStart(); +} + +function streamOnPause() { + if (!this.destroyed /* && !this.pending */) + this[kHandle].readStop(); +} + +class QuicStream extends Duplex { + #closed = false; + #aborted = false; + #defaultEncoding = undefined; + #didRead = false; + #id = undefined; + #highWaterMark = undefined; + #push_id = undefined; + #resetCode = undefined; + #session = undefined; + #dataRateHistogram = undefined; + #dataSizeHistogram = undefined; + #dataAckHistogram = undefined; + #stats = undefined; + + constructor(options, session, push_id) { + const { + highWaterMark, + defaultEncoding, + } = options; + super({ + highWaterMark, + defaultEncoding, + allowHalfOpen: true, + decodeStrings: true, + emitClose: true, + autoDestroy: false, + captureRejections: true, + }); + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + this.#session = session; + this.#push_id = push_id; + this._readableState.readingMore = true; + this.on('pause', streamOnPause); + + // See src/node_quic_stream.h for an explanation + // of the initial states for unidirectional streams. + if (this.unidirectional) { + if (session instanceof QuicServerSession) { + if (this.serverInitiated) { + // Close the readable side + this.push(null); + this.read(); + } else { + // Close the writable side + this.end(); + } + } else if (this.serverInitiated) { + // Close the writable side + this.end(); + } else { + this.push(null); + this.read(); + } + } + + // The QuicStream writes are corked until kSetHandle + // is set, ensuring that writes are buffered in JavaScript + // until we have somewhere to send them. + this.cork(); + } + + // 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) { + this[kHandle] = handle; + if (handle !== undefined) { + handle.onread = onStreamRead; + handle[owner_symbol] = this; + this[async_id_symbol] = handle.getAsyncId(); + this.#id = handle.id(); + this.#dataRateHistogram = new Histogram(handle.rate); + this.#dataSizeHistogram = new Histogram(handle.size); + this.#dataAckHistogram = new Histogram(handle.ack); + this.uncork(); + this.emit('ready'); + } else { + if (this.#dataRateHistogram) + this.#dataRateHistogram[kDestroyHistogram](); + if (this.#dataSizeHistogram) + this.#dataSizeHistogram[kDestroyHistogram](); + if (this.#dataAckHistogram) + this.#dataAckHistogram[kDestroyHistogram](); + } + } + + [kStreamReset](code) { + this.#resetCode = code | 0; + this.push(null); + this.read(); + } + + [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(emit.bind(this, name, headers, push_id)); + } + + [kClose](family, code) { + // Trigger the abrupt shutdown of the stream. If the stream is + // already no-longer readable or writable, this does nothing. If + // the stream is readable or writable, then the abort event will + // be emitted immediately after triggering the send of the + // RESET_STREAM and STOP_SENDING frames. The stream will no longer + // be readable or writable, but will not be immediately destroyed + // as we need to wait until ngtcp2 recognizes the stream as + // having been closed to be destroyed. + + // Do nothing if we've already been destroyed + if (this.destroyed || this.#closed) + return; + + if (this.pending) + return this.once('ready', () => this[kClose](family, code)); + + this.#closed = true; + + this.#aborted = this.readable || this.writable; + + // Trigger scheduling of the RESET_STREAM and STOP_SENDING frames + // as appropriate. Notify ngtcp2 that the stream is to be shutdown. + // Once sent, the stream will be closed and destroyed as soon as + // the shutdown is acknowledged by the peer. + this[kHandle].resetStream(code, family); + + // Close down the readable side of the stream + if (this.readable) { + this.push(null); + this.read(); + } + + // It is important to call shutdown on the handle before shutting + // down the writable side of the stream in order to prevent an + // empty STREAM frame with fin set to be sent to the peer. + if (this.writable) + this.end(); + } + + [kAfterAsyncWrite]({ bytes }) { + // TODO(@jasnell): Implement this + } + + [kInspect]() { + const direction = this.bidirectional ? 'bidirectional' : 'unidirectional'; + const initiated = this.serverInitiated ? 'server' : 'client'; + const obj = { + id: this.#id, + direction, + initiated, + writableState: this._writableState, + readableState: this._readableState, + stats: { + dataRate: this.dataRateHistogram, + dataSize: this.dataSizeHistogram, + dataAck: this.dataAckHistogram, + } + }; + return `QuicStream ${util.format(obj)}`; + } + + [kTrackWriteState](stream, bytes) { + // TODO(@jasnell): Not yet sure what we want to do with these + // this.#writeQueueSize += bytes; + // this.#writeQueueSize += bytes; + // this[kHandle].chunksSentSinceLastWrite = 0; + } + + [kUpdateTimer]() { + // TODO(@jasnell): Implement this later + } + + get pending() { + // The id is set in the kSetHandle function + return this.#id === undefined; + } + + get aborted() { + return this.#aborted; + } + + get serverInitiated() { + return !!(this.#id & 0b01); + } + + get clientInitiated() { + return !this.serverInitiated; + } + + get unidirectional() { + return !!(this.#id & 0b10); + } + + get bidirectional() { + return !this.unidirectional; + } + + #writeGeneric = function(writev, data, encoding, cb) { + if (this.destroyed) + return; // TODO(addaleax): Can this happen? + + // The stream should be corked while still pending + // but just in case uncork + // was called early, defer the actual write until the + // ready event is emitted. + if (this.pending) { + return this.once('ready', () => { + this.#writeGeneric(writev, data, encoding, cb); + }); + } + + this[kUpdateTimer](); + const req = (writev) ? + writevGeneric(this, data, cb) : + writeGeneric(this, data, encoding, cb); + + this[kTrackWriteState](this, req.bytes); + }; + + _write(data, encoding, cb) { + this.#writeGeneric(false, data, encoding, cb); + } + + _writev(data, cb) { + this.#writeGeneric(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. + _final(cb) { + // The QuicStream should be corked while pending + // so this shouldn't be called, but just in case + // the stream was prematurely uncorked, defer the + // operation until the ready event is emitted. + if (this.pending) + return this.once('ready', () => this._final(cb)); + + const handle = this[kHandle]; + if (handle === undefined) { + cb(); + return; + } + + const req = new ShutdownWrap(); + req.oncomplete = () => cb(); + req.handle = handle; + const err = handle.shutdown(req); + if (err === 1) + return cb(); + } + + _read(nread) { + if (this.pending) + return this.once('ready', () => this._read(nread)); + + if (this.destroyed) { // TODO(addaleax): Can this happen? + this.push(null); + return; + } + if (!this.#didRead) { + this._readableState.readingMore = false; + this.#didRead = true; + } + + streamOnResume.call(this); + } + + sendFile(path, options = {}) { + fs.open(path, 'r', QuicStream.#onFileOpened.bind(this, options)); + } + + static #onFileOpened = function(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, (err) => { if (err) throw err; }); + return; + } + + this.sendFD(fd, options, true); + }; + + sendFD(fd, { offset = -1, length = -1 } = {}, ownsFd = false) { + if (this.destroyed || this.#closed) + return; + + 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); + + if (this.pending) { + return this.once('ready', () => { + this.sendFD(fd, { offset, length }, ownsFd); + }); + } + + this[kUpdateTimer](); + this.ownsFd = ownsFd; + + // Close the writable side of the stream, but only as far as the writable + // stream implementation is concerned. + this._final = null; + this.end(); + + defaultTriggerAsyncIdScope(this[async_id_symbol], + QuicStream.#startFilePipe, + this, fd, offset, length); + } + + static #startFilePipe = (stream, fd, offset, length) => { + const handle = new FileHandle(fd, offset, length); + handle.onread = QuicStream.#onPipedFileHandleRead; + handle.stream = stream; + + const pipe = new StreamPipe(handle, stream[kHandle]); + pipe.onunpipe = QuicStream.#onFileUnpipe; + 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 #onFileUnpipe = function() { // Called on the StreamPipe instance. + const stream = this.sink[owner_symbol]; + if (stream.ownsFd) + this.source.close().catch(stream.destroy.bind(stream)); + else + this.source.releaseFD(); + }; + + static #onPipedFileHandleRead = function() { + const err = streamBaseState[kReadBytesOrError]; + if (err < 0 && err !== UV_EOF) { + this.stream.destroy(errnoException(err, 'sendFD')); + } + }; + + get resetReceived() { + return (this.#resetCode !== undefined) ? + { code: this.#resetCode | 0 } : + undefined; + } + + get bufferSize() { + // TODO(@jasnell): Implement this + return undefined; + } + + get id() { + return this.#id; + } + + get push_id() { + return this.#push_id; + } + + close(code) { + this[kClose](QUIC_ERROR_APPLICATION, code); + } + + get session() { + return this.#session; + } + + _destroy(error, callback) { + this.#session[kRemoveStream](this); + const handle = this[kHandle]; + // Do not use handle after this point as the underlying C++ + // object has been destroyed. Any attempt to use the object + // will segfault and crash the process. + if (handle !== undefined) { + this.#stats = new BigInt64Array(handle.stats); + handle.destroy(); + } + // The destroy callback must be invoked in a nextTick + process.nextTick(() => callback(error)); + } + + _onTimeout() { + // TODO(@jasnell): Implement this + } + + get dataRateHistogram() { + return this.#dataRateHistogram; + } + + get dataSizeHistogram() { + return this.#dataSizeHistogram; + } + + get dataAckHistogram() { + return this.#dataAckHistogram; + } + + pushStream(headers = {}, options = {}) { + if (this.destroyed) + throw new ERR_QUICSTREAM_DESTROYED('push'); + + const { + highWaterMark = this.#highWaterMark, + defaultEncoding = this.#defaultEncoding, + } = validateQuicStreamOptions(options); + + validateObject(headers, 'headers'); + + // Push streams are only supported on QUIC servers, and + // only if the original stream is bidirectional. + // TODO(@jasnell): This is really an http/3 specific + // requirement so if we end up later with another + // QUIC application protocol that has a similar + // notion of push streams without this restriction, + // then we'll need to check alpn value here also. + if (!this.clientInitiated && !this.bidirectional) + throw new ERR_QUICSTREAM_INVALID_PUSH(); + + // 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_QUICSTREAM_UNSUPPORTED_PUSH(); + + const stream = new QuicStream({ + readable: false, + highWaterMark, + defaultEncoding, + }, this.session); + + // TODO(@jasnell): The null push and subsequent read shouldn't be necessary + stream.push(null); + stream.read(); + + stream[kSetHandle](handle); + this.session[kAddStream](stream.id, stream); + return stream; + } + + submitInformationalHeaders(headers = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + 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 = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + const { terminal } = { ...options }; + + validateBoolean(terminal, 'options.terminal', { allowUndefined: true }); + 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 = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + 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 now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_STREAM_STATS_CREATED_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_BYTES_SENT]; + } + + get maxExtendedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET]; + } + + get finalSize() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_FINAL_SIZE]; + } + + get maxAcknowledgedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK]; + } + + get maxReceivedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV]; + } +} + +function createSocket(options) { + return new QuicSocket(options); +} + +module.exports = { + createSocket, + kUDPHandleForTesting +}; + +/* 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 new file mode 100644 index 00000000000000..d6e5dc10697622 --- /dev/null +++ b/lib/internal/quic/util.js @@ -0,0 +1,720 @@ +'use strict'; + +const { + codes: { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_QUICSESSION_INVALID_DCID, + ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH, + }, +} = require('internal/errors'); + +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, + NGTCP2_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_PACKET_SIZE, + IDX_QUIC_SESSION_CONFIG_COUNT, + IDX_QUIC_SESSION_STATE_CERT_ENABLED, + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, + IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, + IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + 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, + QUIC_PREFERRED_ADDRESS_IGNORE, + QUIC_PREFERRED_ADDRESS_ACCEPT, + QUIC_ERROR_APPLICATION, + } +} = internalBinding('quic'); + +const { + validateBoolean, + validateBuffer, + validateInteger, + validateNumber, + 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'); + return dns; +} + +function getSocketType(type = 'udp4') { + switch (type) { + case 'udp4': return AF_INET; + case 'udp6': return AF_INET6; + } + throw new ERR_INVALID_ARG_VALUE('options.type', type); +} + +function lookup4(address, callback) { + const { lookup } = lazyDNS(); + lookup(address || '127.0.0.1', 4, callback); +} + +function lookup6(address, callback) { + const { lookup } = lazyDNS(); + lookup(address || '::1', 6, callback); +} + +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 && typeof lookup !== 'function') + throw new ERR_INVALID_ARG_TYPE('options.lookup', 'Function', 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, + maxPacketSize, + maxAckDelay, + preferredAddress, + rejectUnauthorized, + requestCert, + h3: { + qpackMaxTableCapacity, + qpackBlockedStreams, + maxHeaderListSize, + maxPushes, + maxHeaderPairs, + maxHeaderLength = getOptionValue('--max-http-header-size'), + }, + } = { h3: {}, ...params }; + + validateInteger( + activeConnectionIdLimit, + 'options.activeConnectionIdLimit', + { min: 2, max: 8, allowUndefined: true }); + validateInteger( + maxStreamDataBidiLocal, + 'options.maxStreamDataBidiLocal', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamDataBidiRemote, + 'options.maxStreamDataBidiRemote', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamDataUni, + 'options.maxStreamDataUni', + { min: 0, allowUndefined: true }); + validateInteger( + maxData, + 'options.maxData', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamsBidi, + 'options.maxStreamsBidi', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamsUni, + 'options.maxStreamsUni', + { min: 0, allowUndefined: true }); + validateInteger( + idleTimeout, + 'options.idleTimeout', + { min: 0, allowUndefined: true }); + validateInteger( + maxPacketSize, + 'options.maxPacketSize', + { min: 0, allowUndefined: true }); + validateInteger( + maxAckDelay, + 'options.maxAckDelay', + { min: 0, allowUndefined: true }); + validateInteger( + qpackMaxTableCapacity, + 'options.h3.qpackMaxTableCapacity', + { min: 0, allowUndefined: true }); + validateInteger( + qpackBlockedStreams, + 'options.h3.qpackBlockedStreams', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderListSize, + 'options.h3.maxHeaderListSize', + { min: 0, allowUndefined: true }); + validateInteger( + maxPushes, + 'options.h3.maxPushes', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderPairs, + 'options.h3.maxHeaderPairs', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderLength, + 'options.h3.maxHeaderLength', + { min: 0, allowUndefined: true }); + + validatePreferredAddress(preferredAddress); + + return { + activeConnectionIdLimit, + maxStreamDataBidiLocal, + maxStreamDataBidiRemote, + maxStreamDataUni, + maxData, + maxStreamsBidi, + maxStreamsUni, + idleTimeout, + maxPacketSize, + maxAckDelay, + preferredAddress, + rejectUnauthorized, + requestCert, + h3: { + qpackMaxTableCapacity, + qpackBlockedStreams, + maxHeaderListSize, + maxPushes, + maxHeaderPairs, + maxHeaderLength, + } + }; +} + +function validateQuicClientSessionOptions(options = {}) { + if (options !== null && typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); + const { + autoStart = true, + address = 'localhost', + alpn = '', + dcid: dcid_value, + ipv6Only = false, + minDHSize = 1024, + port = 0, + preferredAddressPolicy = 'ignore', + remoteTransportParams, + requestOCSP = false, + 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'); + } + + validateBuffer( + remoteTransportParams, + 'options.remoteTransportParams', + { allowUndefined: true }); + + validateBuffer( + sessionTicket, + 'options.sessionTicket', + { allowUndefined: true }); + + let dcid; + if (dcid_value !== undefined) { + if (typeof dcid_value === 'string') { + // If it's a string, it must be a hex encoded string + try { + dcid = Buffer.from(dcid_value, 'hex'); + } catch { + throw new ERR_QUICSESSION_INVALID_DCID(dcid); + } + } + + 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_QUICSESSION_INVALID_DCID(dcid_value.toString('hex')); + } + + dcid = dcid_value; + } + + if (preferredAddressPolicy !== undefined) + validateString(preferredAddressPolicy, 'options.preferredAddressPolicy'); + + validateBoolean(ipv6Only, 'options.ipv6Only'); + validateBoolean(requestOCSP, 'options.requestOCSP'); + validateBoolean(verifyHostnameIdentity, 'options.verifyHostnameIdentity'); + validateBoolean(qlog, 'options.qlog'); + + return { + autoStart, + address, + alpn, + dcid, + ipv6Only, + minDHSize, + port, + preferredAddressPolicy: + preferredAddressPolicy === 'accept' ? + QUIC_PREFERRED_ADDRESS_ACCEPT : + QUIC_PREFERRED_ADDRESS_IGNORE, + remoteTransportParams, + requestOCSP, + 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'); + } + validateBoolean(halfOpen, 'options.halfOpen', { allowUndefined: true }); + validateInteger(highWaterMark, 'options.highWaterMark', { + min: 0, + allowUndefined: true + }); + return { + defaultEncoding, + halfOpen, + highWaterMark, + }; +} + +function validateQuicEndpointOptions(options = {}, name = 'options') { + validateObject(options, name); + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); + const { + address, + ipv6Only = false, + lookup, + port = 0, + reuseAddr = false, + type = 'udp4', + preferred = false, + } = options; + validateString(address, 'options.address', { allowUndefined: true }); + validatePort(port, 'options.port'); + validateString(type, 'options.type'); + validateLookup(lookup); + validateBoolean(ipv6Only, 'options.ipv6Only'); + validateBoolean(reuseAddr, 'options.reuseAddr'); + validateBoolean(preferred, 'options.preferred'); + return { + address, + ipv6Only, + lookup, + port, + preferred, + reuseAddr, + type: getSocketType(type), + }; +} + +function validateQuicSocketOptions(options = {}) { + validateObject(options, 'options'); + + const { + autoClose = false, + client = {}, + disableStatelessReset = false, + endpoint = { port: 0, type: 'udp4' }, + lookup, + 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, + type = endpoint.type || 'udp4', + validateAddressLRU = false, + validateAddress = false, + } = options; + + validateQuicEndpointOptions(endpoint, 'options.endpoint'); + validateObject(client, 'options.client'); + validateObject(server, 'options.server'); + validateString(type, 'options.type'); + validateLookup(lookup); + validateBoolean(validateAddress, 'options.validateAddress'); + validateBoolean(validateAddressLRU, 'options.validateAddressLRU'); + validateBoolean(autoClose, 'options.autoClose'); + validateBoolean(qlog, 'options.qlog'); + validateBoolean(disableStatelessReset, 'options.disableStatelessReset'); + + validateInteger( + retryTokenTimeout, + 'options.retryTokenTimeout', + { + min: MIN_RETRYTOKEN_EXPIRATION, + max: MAX_RETRYTOKEN_EXPIRATION, + allowUndefined: true + }); + validateInteger( + maxConnections, + 'options.maxConnections', + { min: 1, allowUndefined: true }); + validateInteger( + maxConnectionsPerHost, + 'options.maxConnectionsPerHost', + { min: 1, allowUndefined: true }); + validateInteger( + maxStatelessResetsPerHost, + 'options.maxStatelessResetsPerHost', + { min: 1, allowUndefined: true }); + + if (statelessResetSecret !== undefined) { + validateBuffer(statelessResetSecret, 'options.statelessResetSecret'); + if (statelessResetSecret.length !== 16) + throw new ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH(); + } + + return { + endpoint, + autoClose, + client, + lookup, + maxConnections, + maxConnectionsPerHost, + maxStatelessResetsPerHost, + retryTokenTimeout, + server, + type: getSocketType(type), + validateAddress: validateAddress || validateAddressLRU, + validateAddressLRU, + qlog, + statelessResetSecret, + disableStatelessReset, + }; +} + +function validateQuicSocketListenOptions(options = {}) { + validateObject(options); + const { + alpn = NGTCP2_ALPN_H3, + defaultEncoding, + highWaterMark, + requestCert, + rejectUnauthorized, + } = options; + validateString(alpn, 'options.alpn'); + validateBoolean( + rejectUnauthorized, + 'options.rejectUnauthorized', + { allowUndefined: true }); + validateBoolean( + requestCert, + 'options.requestCert', + { allowUndefined: true }); + + const transportParams = + validateTransportParams( + options, + NGTCP2_MAX_CIDLEN, + NGTCP2_MIN_CIDLEN); + + return { + alpn, + rejectUnauthorized, + requestCert, + transportParams, + ...validateQuicStreamOptions({ highWaterMark, defaultEncoding }) + }; +} + +function validateQuicSocketConnectOptions(options = {}) { + validateObject(options); + const { + type = 'udp4', + address, + } = options; + validateString(address, 'options.address', { allowUndefined: true }); + return { + type: getSocketType(type), + address, + }; +} + +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'); + validateBoolean(earlyData, 'option.earlyData', { allowUndefined: true }); + + // 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, + maxPacketSize, + maxAckDelay, + 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, + maxPacketSize, + IDX_QUIC_SESSION_MAX_PACKET_SIZE); + + 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(handle, event, on) { + if (handle === undefined) + return; + const val = on ? 1 : 0; + switch (event) { + case 'keylog': + handle.state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = val; + break; + case 'clientHello': + handle.state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = val; + break; + case 'pathValidation': + handle.state[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] = val; + break; + case 'OCSPRequest': + handle.state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = val; + break; + case 'usePreferredAddress': + handle.state[IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED] = on; + break; + } +} + +module.exports = { + getAllowUnauthorized, + getSocketType, + lookup4, + lookup6, + setTransportParams, + toggleListeners, + validateCloseCode, + validateTransportParams, + validateQuicClientSessionOptions, + validateQuicSocketOptions, + validateQuicStreamOptions, + validateQuicSocketListenOptions, + validateQuicEndpointOptions, + validateCreateSecureContextOptions, + validateQuicSocketConnectOptions, +}; diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js index 800ba4cefd6370..7db73141cb34bd 100644 --- a/lib/internal/stream_base_commons.js +++ b/lib/internal/stream_base_commons.js @@ -101,7 +101,7 @@ function onWriteComplete(status) { this.callback(null); } -function createWriteWrap(handle) { +function createWriteWrap(handle, callback) { const req = new WriteWrap(); req.handle = handle; @@ -109,12 +109,13 @@ function createWriteWrap(handle) { req.async = false; req.bytes = 0; req.buffer = null; + req.callback = callback; return req; } function writevGeneric(self, data, cb) { - const req = createWriteWrap(self[kHandle]); + const req = createWriteWrap(self[kHandle], cb); const allBuffers = data.allBuffers; let chunks; if (allBuffers) { @@ -134,29 +135,28 @@ function writevGeneric(self, data, cb) { // Retain chunks if (err === 0) req._chunks = chunks; - afterWriteDispatched(self, req, err, cb); + afterWriteDispatched(self, req, err); return req; } function writeGeneric(self, data, encoding, cb) { - const req = createWriteWrap(self[kHandle]); + const req = createWriteWrap(self[kHandle], cb); const err = handleWriteReq(req, data, encoding); - - afterWriteDispatched(self, req, err, cb); + afterWriteDispatched(self, req, err); return req; } -function afterWriteDispatched(self, req, err, cb) { +function afterWriteDispatched(self, req, err) { req.bytes = streamBaseState[kBytesWritten]; req.async = !!streamBaseState[kLastWriteWasAsync]; if (err !== 0) - return self.destroy(errnoException(err, 'write', req.error), cb); + return self.destroy( + errnoException(err, 'write', req.error), + req.callback); - if (!req.async) { - cb(); - } else { - req.callback = cb; + if (!req.async && typeof req.callback === 'function') { + req.callback(); } } diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 46237e54342e5b..a7e1dd9c7fae05 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -2,9 +2,11 @@ const { ArrayIsArray, + NumberINFINITY, NumberIsInteger, NumberMAX_SAFE_INTEGER, NumberMIN_SAFE_INTEGER, + NumberNEGATIVE_INFINITY, } = primordials; const { @@ -70,7 +72,12 @@ function parseFileMode(value, name, def) { } const validateInteger = hideStackFrames( - (value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => { + (value, name, + { min = NumberMIN_SAFE_INTEGER, + max = NumberMAX_SAFE_INTEGER, + allowUndefined = false } = {}) => { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value); if (!NumberIsInteger(value)) @@ -115,17 +122,25 @@ const validateUint32 = hideStackFrames((value, name, positive) => { } }); -function validateString(value, name) { +function validateString(value, name, { allowUndefined = false } = {}) { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'string') throw new ERR_INVALID_ARG_TYPE(name, 'string', value); } -function validateNumber(value, name) { +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 validateBoolean(value, name) { +function validateBoolean(value, name, { allowUndefined = false } = {}) { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'boolean') throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value); } @@ -163,13 +178,16 @@ function validateSignalName(signal, name = 'signal') { } } -const validateBuffer = hideStackFrames((buffer, name = 'buffer') => { - if (!isArrayBufferView(buffer)) { - throw new ERR_INVALID_ARG_TYPE(name, - ['Buffer', 'TypedArray', 'DataView'], - buffer); - } -}); +const validateBuffer = hideStackFrames( + (buffer, name = 'buffer', { allowUndefined = false } = {}) => { + if (allowUndefined && buffer === undefined) + return; + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE(name, + ['Buffer', 'TypedArray', 'DataView'], + buffer); + } + }); function validateEncoding(data, encoding) { const normalizedEncoding = normalizeEncoding(encoding); diff --git a/lib/quic.js b/lib/quic.js new file mode 100644 index 00000000000000..4a9f05665da9a4 --- /dev/null +++ b/lib/quic.js @@ -0,0 +1,23 @@ +'use strict'; + +const { + codes: { + ERR_QUIC_UNAVAILABLE, + }, +} = require('internal/errors'); +const { assertCrypto } = require('internal/util'); + +assertCrypto(); +if (process.versions.ngtcp2 === undefined) + throw new ERR_QUIC_UNAVAILABLE(); + +const { + createSocket +} = require('internal/quic/core'); + +module.exports = { createSocket }; + +process.emitWarning( + 'QUIC protocol support is experimental and not yet ' + + 'supported for production use', + 'ExperimentalWarning'); diff --git a/node.gyp b/node.gyp index 170501477a62d1..45cd0a475635bd 100644 --- a/node.gyp +++ b/node.gyp @@ -1,5 +1,6 @@ { 'variables': { + 'experimental_quic': 'false', 'v8_use_siphash%': 0, 'v8_trace_maps%': 0, 'v8_enable_pointer_compression%': 0, @@ -19,6 +20,8 @@ '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%': '', @@ -70,6 +73,7 @@ 'lib/process.js', 'lib/punycode.js', 'lib/querystring.js', + 'lib/quic.js', 'lib/readline.js', 'lib/repl.js', 'lib/stream.js', @@ -185,6 +189,8 @@ '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', @@ -567,6 +573,7 @@ 'src/js_native_api_v8.h', 'src/js_native_api_v8_internals.h', 'src/js_stream.cc', + 'src/js_udp_wrap.cc', 'src/module_wrap.cc', 'src/node.cc', 'src/node_api.cc', @@ -657,6 +664,8 @@ 'src/node_api.h', 'src/node_api_types.h', 'src/node_binding.h', + 'src/node_bob.h', + 'src/node_bob-inl.h', 'src/node_buffer.h', 'src/node_constants.h', 'src/node_context_data.h', @@ -888,6 +897,36 @@ '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/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. @@ -1168,6 +1207,16 @@ 'HAVE_OPENSSL=1', ], }], + [ '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', + 'test/cctest/test_quic_verifyhostnameidentity.cc' + ] + }], ['v8_enable_inspector==1', { 'sources': [ 'test/cctest/test_inspector_socket.cc', diff --git a/node.gypi b/node.gypi index 87bcb37fc7f3d5..d0b73a739c83ff 100644 --- a/node.gypi +++ b/node.gypi @@ -187,6 +187,24 @@ 'dependencies': [ 'deps/nghttp2/nghttp2.gyp:nghttp2' ], }], + [ + 'node_use_openssl=="true" and 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/aliased_buffer.h b/src/aliased_buffer.h index 281c8fed581645..69cf2404c44632 100644 --- a/src/aliased_buffer.h +++ b/src/aliased_buffer.h @@ -32,6 +32,31 @@ template ::value>> class AliasedBufferBase { public: + /** + * Create an AliasedBufferBase over an existing buffer + */ + AliasedBufferBase( + v8::Isolate* isolate, + const size_t count, + NativeT* buffer) : + isolate_(isolate), + count_(count), + byte_offset_(0), + buffer_(buffer) { + CHECK_GT(count, 0); + const v8::HandleScope handle_scope(isolate_); + const size_t size_in_bytes = + MultiplyWithOverflowCheck(sizeof(NativeT), count); + v8::Local ab = + v8::ArrayBuffer::New( + isolate_, + buffer, + size_in_bytes); + + v8::Local js_array = V8T::New(ab, byte_offset_, count); + js_array_ = v8::Global(isolate, js_array); + } + AliasedBufferBase(v8::Isolate* isolate, const size_t count) : isolate_(isolate), count_(count), byte_offset_(0) { CHECK_GT(count, 0); diff --git a/src/async_wrap.h b/src/async_wrap.h index 33bed41a6439c5..143f8910f18100 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -51,6 +51,7 @@ namespace node { V(HTTPINCOMINGMESSAGE) \ V(HTTPCLIENTREQUEST) \ V(JSSTREAM) \ + V(JSUDPWRAP) \ V(MESSAGEPORT) \ V(PIPECONNECTWRAP) \ V(PIPESERVERWRAP) \ @@ -58,6 +59,11 @@ namespace node { V(PROCESSWRAP) \ V(PROMISE) \ V(QUERYWRAP) \ + V(QUICCLIENTSESSION) \ + V(QUICSERVERSESSION) \ + V(QUICSENDWRAP) \ + V(QUICSOCKET) \ + V(QUICSTREAM) \ V(SHUTDOWNWRAP) \ V(SIGNALWRAP) \ V(STATWATCHER) \ diff --git a/src/debug_utils.h b/src/debug_utils.h index b654159ac2a24e..528778c0a89182 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -43,6 +43,7 @@ void FWrite(FILE* file, const std::string& str); V(INSPECTOR_SERVER) \ V(INSPECTOR_PROFILER) \ V(CODE_CACHE) \ + V(NGTCP2_DEBUG) \ V(WASI) enum class DebugCategory { diff --git a/src/env-inl.h b/src/env-inl.h index 69b7316e405d41..c760134af14865 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -609,6 +609,17 @@ inline void Environment::set_http2_state( http2_state_ = std::move(buffer); } +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +inline QuicState* Environment::quic_state() const { + return quic_state_.get(); +} + +inline void Environment::set_quic_state(std::unique_ptr buffer) { + CHECK(!quic_state_); // Should be set only once. + quic_state_ = std::move(buffer); +} +#endif + inline AliasedFloat64Array* Environment::fs_stats_field_array() { return &fs_stats_field_array_; } @@ -1001,6 +1012,10 @@ inline void AllocatedBuffer::Resize(size_t len) { buffer_ = uv_buf_init(new_data, len); } +inline bool AllocatedBuffer::empty() { + return env_ == nullptr; +} + inline uv_buf_t AllocatedBuffer::release() { uv_buf_t ret = buffer_; buffer_ = uv_buf_init(nullptr, 0); diff --git a/src/env.h b/src/env.h index 13af1874050a95..fa6ffa3a3fcdc3 100644 --- a/src/env.h +++ b/src/env.h @@ -30,6 +30,9 @@ #include "inspector_profiler.h" #endif #include "debug_utils.h" +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +#include "quic/node_quic_state.h" +#endif #include "handle_wrap.h" #include "node.h" #include "node_binding.h" @@ -171,6 +174,7 @@ constexpr size_t kFsStatsBufferLength = // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. #define PER_ISOLATE_STRING_PROPERTIES(V) \ + V(ack_string, "ack") \ V(address_string, "address") \ V(aliases_string, "aliases") \ V(args_string, "args") \ @@ -332,6 +336,8 @@ constexpr size_t kFsStatsBufferLength = V(psk_string, "psk") \ V(pubkey_string, "pubkey") \ V(query_string, "query") \ + V(quic_alpn_string, "h3-27") \ + V(rate_string, "rate") \ V(raw_string, "raw") \ V(read_host_object_string, "_readHostObject") \ V(readable_string, "readable") \ @@ -359,6 +365,8 @@ constexpr size_t kFsStatsBufferLength = V(stack_string, "stack") \ V(standard_name_string, "standardName") \ V(start_time_string, "startTime") \ + V(state_string, "state") \ + V(stats_string, "stats") \ V(status_string, "status") \ V(stdio_string, "stdio") \ V(subject_string, "subject") \ @@ -398,6 +406,16 @@ constexpr size_t kFsStatsBufferLength = V(x_forwarded_string, "x-forwarded-for") \ V(zero_return_string, "ZERO_RETURN") +#if HAVE_OPENSSL && defined(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(as_callback_data_template, v8::FunctionTemplate) \ V(async_wrap_ctor_template, v8::FunctionTemplate) \ @@ -425,7 +443,38 @@ constexpr size_t kFsStatsBufferLength = V(tcp_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ V(write_wrap_template, v8::ObjectTemplate) \ - V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) + V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \ + QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) + +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ + V(quic_on_socket_close_function, v8::Function) \ + V(quic_on_socket_error_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_destroyed_function, v8::Function) \ + V(quic_on_session_error_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_silent_close_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 #define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ V(as_callback_data, v8::Object) \ @@ -473,7 +522,8 @@ 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) + V(url_constructor_function, v8::Function) \ + QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) class Environment; @@ -562,6 +612,7 @@ struct AllocatedBuffer { inline ~AllocatedBuffer(); inline void Resize(size_t len); + inline bool empty(); inline uv_buf_t release(); inline char* data(); inline const char* data() const; @@ -1017,6 +1068,11 @@ class Environment : public MemoryRetainer { EnabledDebugList* enabled_debug_list() { return &enabled_debug_list_; } +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) + inline QuicState* quic_state() const; + inline void set_quic_state(std::unique_ptr state); +#endif + inline AliasedFloat64Array* fs_stats_field_array(); inline AliasedBigUint64Array* fs_stats_field_bigint_array(); @@ -1374,6 +1430,9 @@ class Environment : public MemoryRetainer { char* http_parser_buffer_ = nullptr; bool http_parser_buffer_in_use_ = false; std::unique_ptr http2_state_; +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) + std::unique_ptr quic_state_; +#endif EnabledDebugList enabled_debug_list_; AliasedFloat64Array fs_stats_field_array_; diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 4e039d5dbf2d37..b29cd132eed4e3 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -99,6 +99,10 @@ void HandleWrap::MarkAsUninitialized() { state_ = kClosed; } +void HandleWrap::MarkAsClosing() { + state_ = kClosing; +} + HandleWrap::HandleWrap(Environment* env, Local object, diff --git a/src/handle_wrap.h b/src/handle_wrap.h index a555da9479de93..0e2b4a715f107a 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -88,15 +88,18 @@ class HandleWrap : public AsyncWrap { void MarkAsInitialized(); void MarkAsUninitialized(); + void MarkAsClosing(); + bool IsInitialized() { return state_ == kInitialized; } inline bool IsHandleClosing() const { return state_ == kClosing || state_ == kClosed; } + static void OnClose(uv_handle_t* handle); + private: friend class Environment; friend void GetActiveHandles(const v8::FunctionCallbackInfo&); - static void OnClose(uv_handle_t* handle); // handle_wrap_queue_ needs to be at a fixed offset from the start of the // class because it is used by src/node_postmortem_metadata.cc to calculate diff --git a/src/js_udp_wrap.cc b/src/js_udp_wrap.cc new file mode 100644 index 00000000000000..d956a4009e9463 --- /dev/null +++ b/src/js_udp_wrap.cc @@ -0,0 +1,219 @@ +#include "udp_wrap.h" +#include "async_wrap-inl.h" +#include "node_errors.h" +#include "node_sockaddr-inl.h" + +namespace node { + +using errors::TryCatchScope; +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Int32; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Value; + +class JSUDPWrap final : public UDPWrapBase, public AsyncWrap { + public: + JSUDPWrap(Environment* env, Local obj); + + int RecvStart() override; + int RecvStop() override; + ssize_t Send(uv_buf_t* bufs, + size_t nbufs, + const sockaddr* addr) override; + SocketAddress GetPeerName() override; + SocketAddress GetSockName() override; + AsyncWrap* GetAsyncWrap() override { return this; } + + static void New(const FunctionCallbackInfo& args); + static void EmitReceived(const FunctionCallbackInfo& args); + static void OnSendDone(const FunctionCallbackInfo& args); + static void OnAfterBind(const FunctionCallbackInfo& args); + + static void Initialize(Local target, + Local unused, + Local context, + void* priv); + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(JSUDPWrap) + SET_SELF_SIZE(JSUDPWrap) +}; + +JSUDPWrap::JSUDPWrap(Environment* env, Local obj) + : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) { + MakeWeak(); + + obj->SetAlignedPointerInInternalField( + kUDPWrapBaseField, static_cast(this)); +} + +int JSUDPWrap::RecvStart() { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int32_t value_int = UV_EPROTO; + if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || + !value->Int32Value(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +int JSUDPWrap::RecvStop() { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int32_t value_int = UV_EPROTO; + if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || + !value->Int32Value(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +ssize_t JSUDPWrap::Send(uv_buf_t* bufs, + size_t nbufs, + const sockaddr* addr) { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int64_t value_int = UV_EPROTO; + size_t total_len = 0; + + MaybeStackBuffer, 16> buffers(nbufs); + for (size_t i = 0; i < nbufs; i++) { + buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len) + .ToLocalChecked(); + total_len += bufs[i].len; + } + + Local args[] = { + listener()->CreateSendWrap(total_len)->object(), + Array::New(env()->isolate(), buffers.out(), nbufs), + AddressToJS(env(), addr) + }; + + if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) + .ToLocal(&value) || + !value->IntegerValue(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +SocketAddress JSUDPWrap::GetPeerName() { + // TODO(jasnell): Maybe turn this into a real JS-based method. + SocketAddress ret; + CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); + return ret; +} + +SocketAddress JSUDPWrap::GetSockName() { + // TODO(jasnell): Maybe turn this into a real JS-based method. + SocketAddress ret; + CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); + return ret; +} + +void JSUDPWrap::New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + new JSUDPWrap(env, args.Holder()); +} + +void JSUDPWrap::EmitReceived(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + Environment* env = wrap->env(); + + ArrayBufferViewContents buffer(args[0]); + const char* data = buffer.data(); + int len = buffer.length(); + + CHECK(args[1]->IsInt32()); // family + CHECK(args[2]->IsString()); // address + CHECK(args[3]->IsInt32()); // port + CHECK(args[4]->IsInt32()); // flags + int family = args[1].As()->Value() == 4 ? AF_INET : AF_INET6; + Utf8Value address(env->isolate(), args[2]); + int port = args[3].As()->Value(); + int flags = args[3].As()->Value(); + + sockaddr_storage addr; + CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0); + + // Repeatedly ask the stream's owner for memory, copy the data that we + // just read from JS into those buffers and emit them as reads. + while (len != 0) { + uv_buf_t buf = wrap->listener()->OnAlloc(len); + ssize_t avail = len; + if (static_cast(buf.len) < avail) + avail = buf.len; + + memcpy(buf.base, data, avail); + data += avail; + len -= avail; + wrap->listener()->OnRecv( + avail, buf, reinterpret_cast(&addr), flags); + } +} + +void JSUDPWrap::OnSendDone(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + + CHECK(args[0]->IsObject()); + CHECK(args[1]->IsInt32()); + ReqWrap* req_wrap; + ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As()); + int status = args[1].As()->Value(); + + wrap->listener()->OnSendDone(req_wrap, status); +} + +void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + + wrap->listener()->OnAfterBind(); +} + +void JSUDPWrap::Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + Local t = env->NewFunctionTemplate(New); + Local js_udp_wrap_string = + FIXED_ONE_BYTE_STRING(env->isolate(), "JSUDPWrap"); + t->SetClassName(js_udp_wrap_string); + t->InstanceTemplate() + ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1); + t->Inherit(AsyncWrap::GetConstructorTemplate(env)); + + UDPWrapBase::AddMethods(env, t); + env->SetProtoMethod(t, "emitReceived", EmitReceived); + env->SetProtoMethod(t, "onSendDone", OnSendDone); + env->SetProtoMethod(t, "onAfterBind", OnAfterBind); + + target->Set(env->context(), + js_udp_wrap_string, + t->GetFunction(context).ToLocalChecked()).Check(); +} + + +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize) diff --git a/src/node_binding.cc b/src/node_binding.cc index 37c64000650085..fdb2e52ac34635 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -7,8 +7,14 @@ #if HAVE_OPENSSL #define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap) +#if defined(NODE_EXPERIMENTAL_QUIC) +#define NODE_BUILTIN_QUIC_MODULES(V) V(quic) +#else +#define NODE_BUILTIN_QUIC_MODULES(V) +#endif #else #define NODE_BUILTIN_OPENSSL_MODULES(V) +#define NODE_BUILTIN_QUIC_MODULES(V) #endif #if NODE_HAVE_I18N_SUPPORT @@ -58,6 +64,7 @@ V(http_parser) \ V(inspector) \ V(js_stream) \ + V(js_udp_wrap) \ V(messaging) \ V(module_wrap) \ V(native_module) \ @@ -93,6 +100,7 @@ #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_REPORT_MODULES(V) \ NODE_BUILTIN_PROFILER_MODULES(V) \ diff --git a/src/node_bob-inl.h b/src/node_bob-inl.h new file mode 100644 index 00000000000000..578e45e9ff6149 --- /dev/null +++ b/src/node_bob-inl.h @@ -0,0 +1,37 @@ +#ifndef SRC_NODE_BOB_INL_H_ +#define SRC_NODE_BOB_INL_H_ + +#include "node_bob.h" + +#include + +namespace node { +namespace bob { + +template +int SourceImpl::Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint) { + + int status; + if (eos_) { + status = bob::Status::STATUS_EOS; + std::move(next)(status, nullptr, 0, [](size_t len) {}); + return status; + } + + status = DoPull(std::move(next), options, data, count, max_count_hint); + + if (status == bob::Status::STATUS_END) + eos_ = true; + + return status; +} + +} // namespace bob +} // namespace node + +#endif // SRC_NODE_BOB_INL_H_ diff --git a/src/node_bob.h b/src/node_bob.h new file mode 100644 index 00000000000000..74571608f3ae56 --- /dev/null +++ b/src/node_bob.h @@ -0,0 +1,111 @@ +#ifndef SRC_NODE_BOB_H_ +#define SRC_NODE_BOB_H_ + +#include + +namespace node { +namespace bob { + +constexpr size_t kMaxCountHint = 16; + +// Negative status codes indicate error conditions. +enum Status : int { + // Indicates that an attempt was made to pull after end. + STATUS_EOS = -1, + + // Indicates the end of the stream. No additional + // data will be available and the consumer should stop + // pulling. + STATUS_END = 0, + + // Indicates that there is additional data available + // and the consumer may continue to pull. + STATUS_CONTINUE = 1, + + // Indicates that there is no additional data available + // but the stream has not ended. The consumer should not + // continue to pull but may resume pulling later when + // data is available. + STATUS_BLOCK = 2, + + // Indicates that there is no additional data available + // but the stream has not ended and the source will call + // next again later when data is available. STATUS_WAIT + // must not be used with the OPTIONS_SYNC option. + STATUS_WAIT = 3, +}; + +enum Options : int { + OPTIONS_NONE = 0, + + // Indicates that the consumer is requesting the end + // of the stream. + OPTIONS_END = 1, + + // Indicates that the consumer requires the source to + // invoke Next synchronously. If the source is + // unable to provide data immediately but the + // stream has not yet ended, it should call Next + // using STATUS_BLOCK. When not set, the source + // may call Next asynchronously. + OPTIONS_SYNC = 2 +}; + +// There are Sources and there are Consumers. +// +// Consumers get data by calling Source::Pull, +// optionally passing along a status and allocated +// buffer space for the source to fill, and a Next +// function the Source will call when data is +// available. +// +// The source calls Next to deliver the data. It can +// choose to either use the allocated buffer space +// provided by the consumer or it can allocate its own +// buffers and push those instead. If it allocates +// its own, it can send a Done function that the +// Sink will call when it is done consuming the data. +using Done = std::function; +template +using Next = std::function; + +template +class Source { + public: + virtual int Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint = kMaxCountHint) = 0; +}; + + +template +class SourceImpl : public Source { + public: + int Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint = kMaxCountHint) override; + + bool is_eos() const { return eos_; } + + protected: + virtual int DoPull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint) = 0; + + private: + bool eos_ = false; +}; + +} // namespace bob +} // namespace node + +#endif // SRC_NODE_BOB_H_ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index fdaf91acdc0a1c..0e3f840088ce8f 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -621,6 +621,17 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { min_version = TLS1_2_VERSION; max_version = TLS1_2_VERSION; method = TLS_client_method(); + } else if (strcmp(*sslmethod, "TLSv1_3_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + } else if (strcmp(*sslmethod, "TLSv1_3_server_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + method = TLS_server_method(); + } else if (strcmp(*sslmethod, "TLSv1_3_client_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + method = TLS_client_method(); } else { const std::string msg("Unknown method: "); THROW_ERR_TLS_INVALID_PROTOCOL_METHOD(env, (msg + * sslmethod).c_str()); @@ -2482,7 +2493,7 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { // Store the SNI context for later use. w->sni_context_ = BaseObjectPtr(sc); - if (UseSNIContext(w->ssl_, sc) && !w->SetCACerts(sc)) { + if (UseSNIContext(w->ssl_, w->sni_context_) && !w->SetCACerts(sc)) { // Not clear why sometimes we throw error, and sometimes we call // onerror(). Both cause .destroy(), but onerror does a bit more. unsigned long err = ERR_get_error(); // NOLINT(runtime/int) diff --git a/src/node_crypto_common.cc b/src/node_crypto_common.cc index 197bc5cd5913a4..d890ca5dd998ee 100644 --- a/src/node_crypto_common.cc +++ b/src/node_crypto_common.cc @@ -1,4 +1,5 @@ #include "env-inl.h" +#include "base_object-inl.h" #include "node_buffer.h" #include "node_crypto.h" #include "node_crypto_common.h" @@ -223,7 +224,7 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) return err; } -int UseSNIContext(const SSLPointer& ssl, SecureContext* context) { +int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context) { SSL_CTX* ctx = context->ctx_.get(); X509* x509 = SSL_CTX_get0_certificate(ctx); EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); @@ -329,11 +330,15 @@ const char* X509ErrorCode(long err) { // NOLINT(runtime/int) } MaybeLocal GetValidationErrorReason(Environment* env, int err) { + if (err == 0) + return Undefined(env->isolate()); const char* reason = X509_verify_cert_error_string(err); return OneByteString(env->isolate(), reason); } MaybeLocal GetValidationErrorCode(Environment* env, int err) { + if (err == 0) + return Undefined(env->isolate()); return OneByteString(env->isolate(), X509ErrorCode(err)); } diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h index e42e249ef2ba2e..b6017d5300a104 100644 --- a/src/node_crypto_common.h +++ b/src/node_crypto_common.h @@ -72,7 +72,7 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) const SSLPointer& ssl, long def = X509_V_ERR_UNSPECIFIED); // NOLINT(runtime/int) -int UseSNIContext(const SSLPointer& ssl, SecureContext* context); +int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context); const char* GetClientHelloALPN(const SSLPointer& ssl); diff --git a/src/node_errors.h b/src/node_errors.h index 960cb725323e92..9792d293711505 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -63,6 +63,8 @@ void OnFatalError(const char* location, const char* message); V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \ V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \ + V(ERR_QUIC_CANNOT_SET_GROUPS, Error) \ + V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, Error) #define V(code, type) \ inline v8::Local code(v8::Isolate* isolate, \ @@ -110,7 +112,9 @@ void OnFatalError(const char* location, const char* message); "Script execution was interrupted by `SIGINT`") \ V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \ "Cannot serialize externalized SharedArrayBuffer") \ - V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") + V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") \ + V(ERR_QUIC_CANNOT_SET_GROUPS, "Cannot set groups") \ + V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, "Failure setting SNI context") #define V(code, message) \ inline v8::Local code(v8::Isolate* isolate) { \ diff --git a/src/node_http_common-inl.h b/src/node_http_common-inl.h index d63cdf79a4b468..dccc78bdf3b099 100644 --- a/src/node_http_common-inl.h +++ b/src/node_http_common-inl.h @@ -76,6 +76,14 @@ size_t GetServerMaxHeaderPairs(size_t max_header_pairs) { return std::max(max_header_pairs, min_header_pairs); } +template +std::string NgHeaderImpl::ToString() const { + std::string ret = name(); + ret += " = "; + ret += value(); + return ret; +} + template bool NgHeader::IsZeroLength( NgHeader::rcbuf_t* name, @@ -121,6 +129,12 @@ NgHeader::NgHeader( value_.reset(value); } +template +void NgHeader::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("name", name_); + tracker->TrackField("value", value_); +} + template NgHeader::NgHeader(NgHeader&& other) noexcept : name_(std::move(other.name_)), diff --git a/src/node_http_common.h b/src/node_http_common.h index 41b5f419d94338..5c6c1f4de1e5c5 100644 --- a/src/node_http_common.h +++ b/src/node_http_common.h @@ -453,6 +453,16 @@ class NgRcBufPointer : public MemoryRetainer { bool internalizable_ = false; }; +template +struct NgHeaderImpl : public MemoryRetainer { + virtual v8::MaybeLocal GetName(allocator_t* allocator) const = 0; + virtual v8::MaybeLocal GetValue(allocator_t* allocator) const = 0; + virtual std::string name() const = 0; + virtual std::string value() const = 0; + virtual size_t length() const = 0; + virtual std::string ToString() const; +}; + // The ng libraries use nearly identical structs to represent // received http headers. The NgHeader class wraps those in a // consistent way and allows converting the name and value to @@ -461,7 +471,7 @@ class NgRcBufPointer : public MemoryRetainer { // memory tracking, and implementation of static utility functions. // See Http2HeaderTraits in node_http2.h for an example. template -class NgHeader : public MemoryRetainer { +class NgHeader : public NgHeaderImpl { public: typedef typename T::rcbufferpointer_t rcbufferpointer_t; typedef typename T::rcbufferpointer_t::rcbuf_t rcbuf_t; @@ -487,28 +497,19 @@ class NgHeader : public MemoryRetainer { // object to the v8 string. Once the v8 string is garbage collected, // the reference counter will be decremented. - inline v8::MaybeLocal GetName(allocator_t* allocator) const; - inline v8::MaybeLocal GetValue(allocator_t* allocator) const; + inline v8::MaybeLocal GetName( + allocator_t* allocator) const override; + inline v8::MaybeLocal GetValue( + allocator_t* allocator) const override; + inline std::string name() const override; + inline std::string value() const override; + inline size_t length() const override; - inline std::string name() const; - inline std::string value() const; - inline size_t length() const; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("name", name_); - tracker->TrackField("value", value_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(NgHeader) SET_SELF_SIZE(NgHeader) - std::string ToString() const { - std::string ret = name(); - ret += " = "; - ret += value(); - return ret; - } - private: Environment* env_; rcbufferpointer_t name_; diff --git a/src/node_mem.h b/src/node_mem.h index 0d3388ad4766bb..f8cdc20848f82e 100644 --- a/src/node_mem.h +++ b/src/node_mem.h @@ -13,8 +13,12 @@ namespace mem { // use different struct names. To allow for code re-use, // the NgLibMemoryManager template class can be used for both. +struct NgLibMemoryManagerBase { + virtual void StopTrackingMemory(void* ptr) = 0; +}; + template -class NgLibMemoryManager { +class NgLibMemoryManager : public NgLibMemoryManagerBase { public: // Class needs to provide these methods: // void CheckAllocatedSize(size_t previous_size) const; @@ -24,7 +28,7 @@ class NgLibMemoryManager { AllocatorStructName MakeAllocator(); - void StopTrackingMemory(void* ptr); + void StopTrackingMemory(void* ptr) override; private: static void* ReallocImpl(void* ptr, size_t size, void* user_data); diff --git a/src/node_metadata.cc b/src/node_metadata.cc index 8d0a725de45421..4841163f2a8673 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -11,6 +11,10 @@ #if HAVE_OPENSSL #include +#if defined(NODE_EXPERIMENTAL_QUIC) +#include +#include +#endif #endif // HAVE_OPENSSL #ifdef NODE_HAVE_I18N_SUPPORT @@ -89,6 +93,10 @@ Metadata::Versions::Versions() { #if HAVE_OPENSSL openssl = GetOpenSSLVersion(); +#if defined(NODE_EXPERIMENTAL_QUIC) + ngtcp2 = NGTCP2_VERSION; + nghttp3 = NGHTTP3_VERSION; +#endif #endif #ifdef NODE_HAVE_I18N_SUPPORT diff --git a/src/node_metadata.h b/src/node_metadata.h index bf7e5d3ff4e811..7e9f522d21a9e5 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -34,8 +34,14 @@ namespace node { #if HAVE_OPENSSL #define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl) +#if defined(NODE_EXPERIMENTAL_QUIC) +#define NODE_VERSIONS_KEY_QUIC(V) V(ngtcp2) V(nghttp3) +#else +#define NODE_VERSIONS_KEY_QUIC(V) +#endif #else #define NODE_VERSIONS_KEY_CRYPTO(V) +#define NODE_VERSIONS_KEY_QUIC(V) #endif #ifdef NODE_HAVE_I18N_SUPPORT @@ -51,6 +57,7 @@ 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 680c585d0fb3df..786b9cc1546d62 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -96,7 +96,11 @@ void NativeModuleLoader::InitializeModuleCategories() { "internal/process/policy", "internal/streams/lazy_transform", #endif // !HAVE_OPENSSL - +#if !defined(NODE_EXPERIMENTAL_QUIC) + "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 new file mode 100644 index 00000000000000..c868eaeaa8bc3a --- /dev/null +++ b/src/quic/node_quic.cc @@ -0,0 +1,244 @@ +#include "debug_utils-inl.h" +#include "node.h" +#include "env-inl.h" +#include "node_crypto.h" // SecureContext +#include "node_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 +#include + +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 { + +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& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsObject()); + Local obj = args[0].As(); + +#define SETFUNCTION(name, callback) \ + do { \ + Local 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()); \ + } while (0) + + SETFUNCTION("onSocketClose", socket_close); + SETFUNCTION("onSocketError", socket_error); + SETFUNCTION("onSessionReady", session_ready); + SETFUNCTION("onSessionCert", session_cert); + SETFUNCTION("onSessionClientHello", session_client_hello); + SETFUNCTION("onSessionClose", session_close); + SETFUNCTION("onSessionDestroyed", session_destroyed); + SETFUNCTION("onSessionError", session_error); + SETFUNCTION("onSessionHandshake", session_handshake); + SETFUNCTION("onSessionKeylog", session_keylog); + SETFUNCTION("onSessionUsePreferredAddress", session_use_preferred_address); + SETFUNCTION("onSessionPathValidation", session_path_validation); + SETFUNCTION("onSessionQlog", session_qlog); + SETFUNCTION("onSessionSilentClose", session_silent_close); + 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 +void QuicInitSecureContext(const FunctionCallbackInfo& 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(), + args.GetReturnValue().Set(UV_EBADF)); + const node::Utf8Value groups(env->isolate(), args[1]); + + bool early_data = args[2]->BooleanValue(env->isolate()); + + InitializeSecureContext( + BaseObjectPtr(sc), + early_data, + side); + + if (!crypto::SetGroups(sc, *groups)) + THROW_ERR_QUIC_CANNOT_SET_GROUPS(env); +} +} // namespace + +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + HandleScope scope(isolate); + + HistogramBase::Initialize(env); + + std::unique_ptr state = std::make_unique(isolate); + +#define SET_STATE_TYPEDARRAY(name, field) \ + target->Set(context, \ + FIXED_ONE_BYTE_STRING(isolate, (name)), \ + (field.GetJSArray())).FromJust() + SET_STATE_TYPEDARRAY("sessionConfig", state->quicsessionconfig_buffer); + SET_STATE_TYPEDARRAY("http3Config", state->http3config_buffer); +#undef SET_STATE_TYPEDARRAY + + env->set_quic_state(std::move(state)); + + 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); + env->SetMethod(target, + "initSecureContextClient", + QuicInitSecureContext); + + Local 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_PACKET_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_CONFIG_COUNT) \ + V(IDX_QUIC_SESSION_STATE_CERT_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI) \ + V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI) \ + V(IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT) \ + V(IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT) \ + V(IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED) \ + V(IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT) \ + 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(QUIC_ERROR_APPLICATION) \ + V(QUIC_ERROR_CRYPTO) \ + V(QUIC_ERROR_SESSION) \ + V(QUIC_PREFERRED_ADDRESS_ACCEPT) \ + 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(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU) \ + 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(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_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, AF_INET); + NODE_DEFINE_CONSTANT(constants, AF_INET6); + NODE_DEFINE_STRING_CONSTANT(constants, + NODE_STRINGIFY_HELPER(NGTCP2_ALPN_H3), + NGTCP2_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 new file mode 100644 index 00000000000000..e03378a331154b --- /dev/null +++ b/src/quic/node_quic_buffer-inl.h @@ -0,0 +1,99 @@ +#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 + +namespace node { + +namespace quic { + +QuicBufferChunk::QuicBufferChunk(size_t len) + : data_buf_(len, 0), + buf_(uv_buf_init(reinterpret_cast(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 chunk = + std::make_unique(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 new file mode 100644 index 00000000000000..dc924da8a3190e --- /dev/null +++ b/src/quic/node_quic_buffer.cc @@ -0,0 +1,160 @@ +#include "node_quic_buffer-inl.h" // NOLINT(build/include) +#include "node_bob-inl.h" +#include "util.h" +#include "uv.h" + +#include +#include +#include +#include + +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 (nbufs == 0 || bufs == nullptr || is_empty(bufs[0])) { + done(0); + return 0; + } + size_t n = 0; + while (nbufs > 1) { + if (!is_empty(bufs[n])) { + Push(bufs[n]); + len += bufs[n].len; + } + n++; + nbufs--; + } + len += bufs[n].len; + Push(bufs[n], done); + return len; +} + +void QuicBuffer::Push(std::unique_ptr 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 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 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 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(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, + std::move([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 new file mode 100644 index 00000000000000..17f59a7e759161 --- /dev/null +++ b/src/quic/node_quic_buffer.h @@ -0,0 +1,239 @@ +#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 + +namespace node { +namespace quic { + +class QuicBuffer; + +constexpr size_t kMaxVectorCount = 16; + +using DoneCB = std::function; + +// When data is sent over QUIC, we are required to retain it in memory +// until we receive an acknowledgement that it has been successfully +// acknowledged. 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 essentially 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 as 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 : 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(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 data_buf_; + uv_buf_t buf_; + DoneCB done_ = default_done; + size_t length_ = 0; + bool done_called_ = false; + std::unique_ptr next_; + + friend class QuicBuffer; +}; + +class QuicBuffer : public bob::SourceImpl, + 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 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 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 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 new file mode 100644 index 00000000000000..b48a402df56016 --- /dev/null +++ b/src/quic/node_quic_crypto.cc @@ -0,0 +1,960 @@ +#include "node_quic_crypto.h" +#include "env-inl.h" +#include "node_crypto.h" +#include "node_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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace node { + +using crypto::EntropySource; +using v8::Local; +using v8::String; +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(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 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(&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; + } + + size_t plaintextlen = std::distance(std::begin(plaintext), p); + if (NGTCP2_ERR(ngtcp2_crypto_encrypt( + token, + &ctx.aead, + plaintext.data(), + plaintextlen, + token_key, + 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 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; + CHECK_LE(pktlen, NGTCP2_MAX_PKT_SIZE); + + 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_pkt_hd& hd, + const SocketAddress& addr, + QuicCID* ocid, + const uint8_t* token_secret, + uint64_t verification_expiration) { + + if (hd.tokenlen < 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 = hd.tokenlen - kTokenRandLen; + const uint8_t* ciphertext = hd.token; + const uint8_t* rand_data = hd.token + 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]; + + if (NGTCP2_ERR(ngtcp2_crypto_decrypt( + plaintext, + &ctx.aead, + ciphertext, + ciphertextlen, + token_key, + 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; +} + +namespace { + +bool SplitHostname( + const char* hostname, + std::vector* parts, + const char delim = '.') { + static std::string check_str = + "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30" + "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40" + "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50" + "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60" + "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70" + "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"; + + std::stringstream str(hostname); + std::string part; + while (getline(str, part, delim)) { + // if (part.length() == 0 || + // part.find_first_not_of(check_str) != std::string::npos) { + // return false; + // } + for (size_t n = 0; n < part.length(); n++) { + if (part[n] >= 'A' && part[n] <= 'Z') + part[n] = (part[n] | 0x20); // Lower case the letter + if (check_str.find(part[n]) == std::string::npos) + return false; + } + parts->push_back(part); + } + return true; +} + +bool CheckCertNames( + const std::vector& host_parts, + const std::string& name, + bool use_wildcard = true) { + + if (name.length() == 0) + return false; + + std::vector name_parts; + if (!SplitHostname(name.c_str(), &name_parts)) + return false; + + if (name_parts.size() != host_parts.size()) + return false; + + for (size_t n = host_parts.size() - 1; n > 0; --n) { + if (host_parts[n] != name_parts[n]) + return false; + } + + if (name_parts[0].find("*") == std::string::npos || + name_parts[0].find("xn--") != std::string::npos) { + return host_parts[0] == name_parts[0]; + } + + if (!use_wildcard) + return false; + + std::vector sub_parts; + SplitHostname(name_parts[0].c_str(), &sub_parts, '*'); + + if (sub_parts.size() > 2) + return false; + + if (name_parts.size() <= 2) + return false; + + std::string prefix; + std::string suffix; + if (sub_parts.size() == 2) { + prefix = sub_parts[0]; + suffix = sub_parts[1]; + } else { + prefix = ""; + suffix = sub_parts[0]; + } + + if (prefix.length() + suffix.length() > host_parts[0].length()) + return false; + + if (host_parts[0].compare(0, prefix.length(), prefix)) + return false; + + if (host_parts[0].compare( + host_parts[0].length() - suffix.length(), + suffix.length(), suffix)) { + return false; + } + + return true; +} + +} // namespace + +int VerifyHostnameIdentity( + const crypto::SSLPointer& ssl, + const char* hostname) { + int err = X509_V_ERR_HOSTNAME_MISMATCH; + crypto::X509Pointer cert(SSL_get_peer_certificate(ssl.get())); + if (!cert) + return err; + + // There are several pieces of information we need from the cert at this point + // 1. The Subject (if it exists) + // 2. The collection of Alt Names (if it exists) + // + // The certificate may have many Alt Names. We only care about the ones that + // are prefixed with 'DNS:', 'URI:', or 'IP Address:'. We might check + // additional ones later but we'll start with these. + // + // Ideally, we'd be able to *just* use OpenSSL's built in name checking for + // this (SSL_set1_host and X509_check_host) but it does not appear to do + // checking on URI or IP Address Alt names, which is unfortunate. We need + // both of those to retain compatibility with the peer identity verification + // Node.js already does elsewhere. At the very least, we'll use + // X509_check_host here first as a first step. If it is successful, awesome, + // there's nothing else for us to do. Return and be happy! + if (X509_check_host( + cert.get(), + hostname, + strlen(hostname), + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT | + X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS | + X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, + nullptr) > 0) { + return 0; + } + + if (X509_check_ip_asc( + cert.get(), + hostname, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) > 0) { + return 0; + } + + // If we've made it this far, then we have to perform a more check + return VerifyHostnameIdentity( + hostname, + crypto::GetCertificateCN(cert.get()), + crypto::GetCertificateAltNames(cert.get())); +} + +int VerifyHostnameIdentity( + const char* hostname, + const std::string& cert_cn, + const std::unordered_multimap& altnames) { + + int err = X509_V_ERR_HOSTNAME_MISMATCH; + + // 1. If the hostname is an IP address (v4 or v6), the certificate is valid + // if and only if there is an 'IP Address:' alt name specifying the same + // IP address. The IP address must be canonicalized to ensure a proper + // check. It's possible that the X509_check_ip_asc covers this. If so, + // we can remove this check. + + if (SocketAddress::is_numeric_host(hostname)) { + auto ips = altnames.equal_range("ip"); + for (auto ip = ips.first; ip != ips.second; ++ip) { + if (ip->second.compare(hostname) == 0) { + // Success! + return 0; + } + } + // No match, and since the hostname is an IP address, skip any + // further checks + return err; + } + + auto dns_names = altnames.equal_range("dns"); + auto uri_names = altnames.equal_range("uri"); + + size_t dns_count = std::distance(dns_names.first, dns_names.second); + size_t uri_count = std::distance(uri_names.first, uri_names.second); + + std::vector host_parts; + SplitHostname(hostname, &host_parts); + + // 2. If there no 'DNS:' or 'URI:' Alt names, if the certificate has a + // Subject, then we need to extract the CN field from the Subject. and + // check that the hostname matches the CN, taking into consideration + // the possibility of a wildcard in the CN. If there is a match, congrats, + // we have a valid certificate. Return and be happy. + + if (dns_count == 0 && uri_count == 0) { + if (cert_cn.length() > 0 && CheckCertNames(host_parts, cert_cn)) + return 0; + // No match, and since there are no dns or uri entries, return + return err; + } + + // 3. If, however, there are 'DNS:' and 'URI:' Alt names, things become more + // complicated. Essentially, we need to iterate through each 'DNS:' and + // 'URI:' Alt name to find one that matches. The 'DNS:' Alt names are + // relatively simple but may include wildcards. The 'URI:' Alt names + // require the name to be parsed as a URL, then extract the hostname from + // the URL, which is then checked against the hostname. If you find a + // match, yay! Return and be happy. (Note, it's possible that the 'DNS:' + // check in this step is redundant to the X509_check_host check. If so, + // we can simplify by removing those checks here.) + + // First, let's check dns names + for (auto name = dns_names.first; name != dns_names.second; ++name) { + if (name->first.length() > 0 && + CheckCertNames(host_parts, name->second)) { + return 0; + } + } + + // Then, check uri names + for (auto name = uri_names.first; name != uri_names.second; ++name) { + if (name->first.length() > 0 && + CheckCertNames(host_parts, name->second, false)) { + return 0; + } + } + + // 4. Failing all of the previous checks, we assume the certificate is + // invalid for an unspecified reason. + return err; +} + +// Get the ALPN protocol identifier that was negotiated for the session +Local GetALPNProtocol(const QuicSession& session) { + QuicCryptoContext* ctx = session.crypto_context(); + Environment* env = session.env(); + std::string alpn = ctx->selected_alpn(); + if (alpn == NGTCP2_ALPN_H3 + 1) { + return env->quic_alpn_string(); + } else { + return ToV8Value( + env->context(), + alpn, + env->isolate()).FromMaybe(Local()); + } +} + +namespace { +int CertCB(SSL* ssl, void* arg) { + QuicSession* session = static_cast(arg); + return SSL_get_tlsext_status_type(ssl) == TLSEXT_STATUSTYPE_ocsp ? + session->crypto_context()->OnOCSP() : 1; +} + +void Keylog_CB(const SSL* ssl, const char* line) { + QuicSession* session = static_cast(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(SSL_get_app_data(ssl)); + int ret = session->crypto_context()->OnClientHello(); + 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(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(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(SSL_get_app_data(ssl)); + return session->allow_early_data() ? 1 : 0; +} + +int TLS_Status_Callback(SSL* ssl, void* arg) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + return session->crypto_context()->OnTLSStatus(); +} + +int New_Session_Callback(SSL* ssl, SSL_SESSION* session) { + QuicSession* s = static_cast(SSL_get_app_data(ssl)); + return s->set_session(session); +} + +int GenerateSessionTicket(SSL* ssl, void* arg) { + QuicSession* s = static_cast(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(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(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(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(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. + if (SocketAddress::is_numeric_host(hostname.c_str())) { + SSL_set_tlsext_host_name(ssl.get(), ""); + } else { + SSL_set_tlsext_host_name(ssl.get(), hostname.c_str()); + } +} + +} // namespace + +void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl) { + QuicCryptoContext* ctx = session->crypto_context(); + Environment* env = session->env(); + + SSL_set_app_data(ssl.get(), session); + SSL_set_cert_cb(ssl.get(), CertCB, + const_cast(reinterpret_cast(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 (env->quic_state()->warn_trace_tls) { + env->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(); + } + + SetTransportParams(session, ssl); +} + +void InitializeSecureContext( + BaseObjectPtr sc, + bool early_data, + ngtcp2_crypto_side side) { + // TODO(@jasnell): Using a static value for this at the moment but + // we need to determine if a non-static or per-session value is better. + 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); +} + +bool DeriveAndInstallInitialKey( + const QuicSession& session, + const QuicCID& dcid) { + uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + return NGTCP2_OK(ngtcp2_crypto_derive_and_install_initial_key( + session.connection(), + rx_secret, + tx_secret, + initial_secret, + rx_key, + rx_iv, + rx_hp, + tx_key, + tx_iv, + tx_hp, + dcid.cid(), + session.crypto_context()->side())); +} + +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 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(&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 new file mode 100644 index 00000000000000..5bada2d209765b --- /dev/null +++ b/src/quic/node_quic_crypto.h @@ -0,0 +1,159 @@ +#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 +#include +#include + +#include + +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 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); + +// Called when the client QuicSession is created and +// when the server QuicSession first receives the +// client hello. +bool DeriveAndInstallInitialKey( + const QuicSession& session, + const QuicCID& dcid); + +// 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 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_pkt_hd& hd, + const SocketAddress& addr, + QuicCID* ocid, + const uint8_t* token_secret, + uint64_t verification_expiration); + +int VerifyHostnameIdentity(const crypto::SSLPointer& ssl, const char* hostname); +int VerifyHostnameIdentity( + const char* hostname, + const std::string& cert_cn, + const std::unordered_multimap& altnames); + +// Get the ALPN protocol identifier that was negotiated for the session +v8::Local 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 new file mode 100644 index 00000000000000..8f3f0349cf9673 --- /dev/null +++ b/src/quic/node_quic_default_application.cc @@ -0,0 +1,181 @@ +#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 + +#include + +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 needs_init(); +} + +void DefaultApplication::ScheduleStream(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + Debug(session(), "Scheduling stream %" PRIu64, stream_id); + if (LIKELY(stream)) + stream->Schedule(&stream_queue_); +} + +void DefaultApplication::UnscheduleStream(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + Debug(session(), "Unscheduling stream %" PRIu64, stream_id); + if (LIKELY(stream)) + stream->Unschedule(); +} + +void DefaultApplication::StreamClose( + int64_t stream_id, + uint64_t app_error_code) { + if (!session()->HasStream(stream_id)) + return; + if (app_error_code == 0) + app_error_code = NGTCP2_APP_NOERROR; + UnscheduleStream(stream_id); + QuicApplication::StreamClose(stream_id, app_error_code); +} + +void DefaultApplication::ResumeStream(int64_t stream_id) { + Debug(session(), "Stream %" PRId64 " has data to send", stream_id); + ScheduleStream(stream_id); +} + +bool DefaultApplication::ReceiveStreamData( + int64_t stream_id, + int fin, + 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 stream = session()->FindStream(stream_id); + if (!stream) { + // Shutdown the stream explicitly if the session is being closed. + if (session()->is_gracefully_closing()) { + session()->ResetStream(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); + + stream->ReceiveData(fin, 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 new file mode 100644 index 00000000000000..9fb5e050aa021f --- /dev/null +++ b/src/quic/node_quic_default_application.h @@ -0,0 +1,61 @@ +#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( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) override; + + int GetStreamData(StreamData* stream_data) override; + + void ResumeStream(int64_t stream_id) override; + void StreamClose(int64_t stream_id, uint64_t app_error_code) 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 new file mode 100644 index 00000000000000..94b815ac7a732b --- /dev/null +++ b/src/quic/node_quic_http3_application.cc @@ -0,0 +1,937 @@ +#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 +#include +#include +#include + +namespace node { + +using v8::Eternal; +using v8::MaybeLocal; +using v8::String; +using v8::Value; + +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 +void Http3Application::SetConfig( + int idx, + M T::*member) { + AliasedFloat64Array& buffer = env()->quic_state()->http3config_buffer; + uint64_t flags = static_cast(buffer[IDX_HTTP3_CONFIG_COUNT]); + if (flags & (1ULL << idx)) + config_.*member = static_cast(buffer[idx]); +} + +Http3Application::Http3Application( + QuicSession* session) + : QuicApplication(session), + alloc_info_(MakeAllocator()) { + // Collect Configuration Details. + SetConfig(IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, + &Http3ApplicationConfig::qpack_max_table_capacity); + SetConfig(IDX_HTTP3_QPACK_BLOCKED_STREAMS, + &Http3ApplicationConfig::qpack_blocked_streams); + SetConfig(IDX_HTTP3_MAX_HEADER_LIST_SIZE, + &Http3ApplicationConfig::max_header_list_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->env()->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, it's important to recognize that 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 Http3Application::SubmitPush( + int64_t id, + v8::Local 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(); +} + +// 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, + v8::Local 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, + v8::Local 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, + v8::Local 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_header_list_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( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + Debug(session(), "Receiving %" PRIu64 " bytes for stream %" PRIu64 "%s", + datalen, stream_id, fin == 1 ? " (fin)" : ""); + ssize_t nread = + nghttp3_conn_read_stream( + connection(), stream_id, data, datalen, 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(stream_data->data), + sizeof(stream_data->data)); + if (ret < 0) + return static_cast(ret); + else + stream_data->remaining = stream_data->count = static_cast(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 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(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(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 stream = session()->FindStream(stream_id); + CHECK(stream); + stream->ReceiveData(1, nullptr, 0, 0); + session()->listener()->OnStreamClose(stream_id, app_error_code); +} + +BaseObjectPtr Http3Application::FindOrCreateStream( + int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + if (!stream) { + if (session()->is_gracefully_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 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( + 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 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) { + session()->ResetStream(stream_id, app_error_code); +} + +void Http3Application::EndStream(int64_t stream_id) { + BaseObjectPtr 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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 new file mode 100644 index 00000000000000..1430b5f4f77054 --- /dev/null +++ b/src/quic/node_quic_http3_application.h @@ -0,0 +1,333 @@ +#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 +#include + +#include +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; + static const uint8_t kNoneFlag = NGHTTP3_NV_FLAG_NONE; +}; + +using Http3ConnectionPointer = DeleteFnPtr; +using Http3RcBufferPointer = NgRcBufPointer; +using Http3Headers = NgHeaders; + +struct Http3HeaderTraits { + typedef Http3RcBufferPointer rcbufferpointer_t; + typedef QuicApplication allocator_t; + + static const char* ToHttpHeaderName(int32_t token); +}; + +using Http3Header = NgHeader; + +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_header_list_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 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( + int64_t stream_id, + int fin, + 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 headers) override; + + bool SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) override; + + bool SubmitTrailers( + int64_t stream_id, + v8::Local headers) override; + + BaseObjectPtr SubmitPush( + int64_t id, + v8::Local 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 + void SetConfig(int idx, M T::*member); + + nghttp3_conn* connection() const { return connection_.get(); } + BaseObjectPtr 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 new file mode 100644 index 00000000000000..d7e5acbb769881 --- /dev/null +++ b/src/quic/node_quic_session-inl.h @@ -0,0 +1,614 @@ +#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 "node_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 +#include +#include + +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) { + if (ocid) { + transport_params.original_connection_id = *ocid; + transport_params.original_connection_id_present = 1; + } +} + +void QuicSessionConfig::set_qlog(const ngtcp2_qlog_settings& qlog_) { + qlog = qlog_; +} + +QuicCryptoContext::QuicCryptoContext( + QuicSession* session, + BaseObjectPtr 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 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_[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] == 1)) + session_->listener()->OnKeylog(line, strlen(line)); +} + +void QuicCryptoContext::OnClientHelloDone() { + // Continue the TLS handshake when this function exits + // otherwise it will stall and fail. + TLSHandshakeScope handshake(this, &in_client_hello_); + // Disable the callback at this point so we don't loop continuously + session_->state_[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 0; +} + +// 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) { + early_data_ = + SSL_SESSION_get_max_early_data(session.get()) == 0xffffffffUL; + } + return crypto::SetTLSSession(ssl_, std::move(session)); +} + +v8::MaybeLocal QuicCryptoContext::hello_ciphers() const { + return crypto::GetClientHelloCiphers(session()->env(), ssl_); +} + +v8::MaybeLocal QuicCryptoContext::cipher_name() const { + return crypto::GetCipherName(session()->env(), ssl_); +} + +v8::MaybeLocal 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 QuicCryptoContext::ephemeral_key() const { + return crypto::GetEphemeralKey(session()->env(), ssl_); +} + +v8::MaybeLocal QuicCryptoContext::peer_cert(bool abbreviated) const { + return crypto::GetPeerCert( + session()->env(), + ssl_, + abbreviated, + session()->is_server()); +} + +v8::MaybeLocal 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(alpn_buf), alpnlen) : + std::string(); +} + +bool QuicCryptoContext::early_data() const { + return + (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)); +} + +// Derives and installs the initial keying material for a newly +// created session. +bool QuicCryptoContext::SetupInitialKey(const QuicCID& dcid) { + Debug(session(), "Deriving and installing initial keys"); + return DeriveAndInstallInitialKey(*session(), dcid); +} + +QuicApplication::QuicApplication(QuicSession* session) : session_(session) {} + +void QuicApplication::set_stream_fin(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + stream->set_fin_sent(); +} + +ssize_t QuicApplication::WriteVStream( + QuicPathStorage* path, + uint8_t* buf, + ssize_t* ndatalen, + const StreamData& stream_data) { + CHECK_LE(stream_data.count, kMaxVectorCount); + return ngtcp2_conn_writev_stream( + session()->connection(), + &path->path, + buf, + session()->max_packet_length(), + ndatalen, + stream_data.remaining > 0 ? + NGTCP2_WRITE_STREAM_FLAG_MORE : + NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_data.id, + stream_data.fin, + stream_data.buf, + stream_data.count, + uv_hrtime()); +} + +std::unique_ptr QuicApplication::CreateStreamDataPacket() { + return QuicPacket::Create( + "stream data", + session()->max_packet_length()); +} + +Environment* QuicApplication::env() const { + return session()->env(); +} + +// Every QUIC session will have multiple CIDs associated with it. +void QuicSession::AssociateCID(const QuicCID& cid) { + socket()->AssociateCID(cid, scid_); +} + +void QuicSession::DisassociateCID(const QuicCID& cid) { + if (is_server()) + socket()->DisassociateCID(cid); +} + +void QuicSession::StartHandshake() { + if (crypto_context_->is_handshake_started() || is_server()) + return; + crypto_context_->handshake_started(); + SendPendingData(); +} + +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); +} + +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::ExtendMaxStreamsUni(uint64_t max_streams) { + Debug(this, "Setting max unidirectional streams to %" PRIu64, max_streams); + state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI] = + static_cast(max_streams); +} + +void QuicSession::ExtendMaxStreamsBidi(uint64_t max_streams) { + Debug(this, "Setting max bidirectional streams to %" PRIu64, max_streams); + state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI] = + static_cast(max_streams); +} + +// 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_flag_set(QUICSESSION_FLAG_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_flag_set(QUICSESSION_FLAG_DESTROYED)); + return ngtcp2_conn_get_negotiated_version(connection()); +} + +// 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_[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] = 1; +} + +bool QuicSession::is_handshake_completed() const { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + return ngtcp2_conn_get_handshake_completed(connection()); +} + +void QuicSession::InitApplication() { + Debug(this, "Initializing application handler for ALPN %s", + alpn().c_str() + 1); + application_->Initialize(); +} + +// 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_flag_set(QUICSESSION_FLAG_DESTROYED)) { + state_[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT] = 1; + Debug(this, "Idle timeout"); + SilentClose(); + } +} + +// 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)); +} + +// Removes the given connection id from the QuicSession. +void QuicSession::RemoveConnectionID(const QuicCID& cid) { + if (!is_flag_set(QUICSESSION_FLAG_DESTROYED)) + DisassociateCID(cid); +} + +QuicCID QuicSession::dcid() const { + return QuicCID(ngtcp2_conn_get_dcid(connection())); +} + +// 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) { + DCHECK_NOT_NULL(retransmit_); + retransmit_->Update(timeout); +} + +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_gracefully_closing() const { + return is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING); +} + +bool QuicSession::is_destroyed() const { + return is_flag_set(QUICSESSION_FLAG_DESTROYED); +} + +bool QuicSession::is_stateless_reset() const { + return is_flag_set(QUICSESSION_FLAG_STATELESS_RESET); +} + +bool QuicSession::is_server() const { + return crypto_context_->side() == NGTCP2_CRYPTO_SIDE_SERVER; +} + +void QuicSession::StartGracefulClose() { + set_flag(QUICSESSION_FLAG_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; +} + +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 QuicPreferredAddress& 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 packet, + const ngtcp2_path_storage& path) { + UpdateEndpoint(path.path); + return SendPacket(std::move(packet)); +} + +void QuicSession::set_local_address(const ngtcp2_addr* addr) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + ngtcp2_conn_set_local_addr(connection(), addr); +} + +// Set the transport parameters received from the remote peer +void QuicSession::set_remote_transport_params() { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + ngtcp2_conn_get_remote_transport_params(connection(), &transport_params_); + set_flag(QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS); +} + +void QuicSession::StopIdleTimer() { + CHECK_NOT_NULL(idle_); + idle_->Stop(); +} + +void QuicSession::StopRetransmitTimer() { + CHECK_NOT_NULL(retransmit_); + retransmit_->Stop(); +} + +// 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()); + if (!is_flag_set(QUICSESSION_FLAG_DESTROYED)) + listener()->OnVersionNegotiation(NGTCP2_PROTO_VER, sv, nsv); +} + +// 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); + } +} + +// Submits information headers only if the selected application +// supports headers. +bool QuicSession::SubmitInformation( + int64_t stream_id, + v8::Local 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 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 headers) { + return application_->SubmitTrailers(stream_id, headers); +} + +// Submits a new push stream +BaseObjectPtr QuicSession::SubmitPush( + int64_t stream_id, + v8::Local 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 new file mode 100644 index 00000000000000..a6f7b6df0427ea --- /dev/null +++ b/src/quic/node_quic_session.cc @@ -0,0 +1,3763 @@ +#include "node_quic_session-inl.h" // NOLINT(build/include) +#include "aliased_buffer.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "node_crypto_common.h" +#include "ngtcp2/ngtcp2.h" +#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_state.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 "v8.h" +#include "uv.h" + +#include +#include +#include +#include + +namespace node { + +using crypto::EntropySource; +using crypto::SecureContext; + +using v8::Array; +using v8::ArrayBufferView; +using v8::Context; +using v8::Function; +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; + +namespace quic { + +namespace { +void SetConfig(Environment* env, int idx, uint64_t* val) { + AliasedFloat64Array& buffer = env->quic_state()->quicsessionconfig_buffer; + uint64_t flags = static_cast(buffer[IDX_QUIC_SESSION_CONFIG_COUNT]); + if (flags & (1ULL << idx)) + *val = static_cast(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(addr); + memcpy(dest, &src->sin_addr, destlen); + *port = SocketAddress::GetPort(addr); +} + +} // 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_packet_size) + "\n"; + + if (!session->is_server()) { + if (params.original_connection_id_present) { + QuicCID cid(params.original_connection_id); + out += " Original Connection ID: " + cid.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(Environment* env) { + 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(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_packet_size = + NGTCP2_MAX_PKT_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; +} + +// Sets the QuicSessionConfig using an AliasedBuffer for efficiency. +void QuicSessionConfig::Set( + Environment* env, + const sockaddr* preferred_addr) { + ResetToDefaults(env); + SetConfig(env, IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, + &transport_params.active_connection_id_limit); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, + &transport_params.initial_max_stream_data_bidi_local); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, + &transport_params.initial_max_stream_data_bidi_remote); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, + &transport_params.initial_max_stream_data_uni); + SetConfig(env, IDX_QUIC_SESSION_MAX_DATA, + &transport_params.initial_max_data); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAMS_BIDI, + &transport_params.initial_max_streams_bidi); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAMS_UNI, + &transport_params.initial_max_streams_uni); + SetConfig(env, IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, + &transport_params.max_idle_timeout); + SetConfig(env, IDX_QUIC_SESSION_MAX_PACKET_SIZE, + &transport_params.max_packet_size); + SetConfig(env, IDX_QUIC_SESSION_MAX_ACK_DELAY, + &transport_params.max_ack_delay); + + 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. + 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 ocsp) { + if (previous_listener_ != nullptr) + previous_listener_->OnOCSP(ocsp); +} + +void QuicSessionListener::OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& 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::OnSessionDestroyed() { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionDestroyed(); +} + +void QuicSessionListener::OnSessionClose(QuicError error) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionClose(error); +} + +void QuicSessionListener::OnStreamReady(BaseObjectPtr 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::OnSessionSilentClose( + bool stateless_reset, + QuicError error) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionSilentClose(stateless_reset, error); +} + +void QuicSessionListener::OnUsePreferredAddress( + int family, + const QuicPreferredAddress& 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(const uint8_t* data, size_t len) { + if (previous_listener_ != nullptr) + previous_listener_->OnQLog(data, len); +} + +void JSQuicSessionListener::OnKeylog(const char* line, size_t len) { + Environment* env = session()->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local line_bf = Buffer::Copy(env, line, 1 + len).ToLocalChecked(); + char* data = Buffer::Data(line_bf); + data[len] = '\n'; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_keylog_function(), 1, &line_bf); +} + +void JSQuicSessionListener::OnStreamBlocked(int64_t stream_id) { + Environment* env = session()->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + BaseObjectPtr stream = session()->FindStream(stream_id); + stream->MakeCallback(env->quic_on_stream_blocked_function(), 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()); + + Local argv[] = { + Undefined(env->isolate()), + Undefined(env->isolate()), + session()->crypto_context()->hello_ciphers().ToLocalChecked() + }; + + if (alpn != nullptr) { + argv[0] = String::NewFromUtf8( + env->isolate(), + alpn, + v8::NewStringType::kNormal).ToLocalChecked(); + } + if (server_name != nullptr) { + argv[1] = String::NewFromUtf8( + env->isolate(), + server_name, + v8::NewStringType::kNormal).ToLocalChecked(); + } + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_client_hello_function(), + 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()); + + Local servername = Undefined(env->isolate()); + if (server_name != nullptr) { + servername = OneByteString( + env->isolate(), + server_name, + strlen(server_name)); + } + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_cert_function(), 1, &servername); +} + +void JSQuicSessionListener::OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + MaybeStackBuffer, 16> head(headers.size()); + size_t n = 0; + for (const auto& header : headers) { + // name and value should never be empty here, and if + // they are, there's an actual bug so go ahead and crash + Local pair[] = { + header->GetName(session()->application()).ToLocalChecked(), + header->GetValue(session()->application()).ToLocalChecked() + }; + head[n++] = Array::New(env->isolate(), pair, arraysize(pair)); + } + Local argv[] = { + Number::New(env->isolate(), static_cast(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(push_id)); + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_headers_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnOCSP(Local ocsp) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_status_function(), 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()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(stream_id)), + Number::New(env->isolate(), static_cast(app_error_code)) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_close_function(), + 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()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(stream_id)), + Number::New(env->isolate(), static_cast(app_error_code)) + }; + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_reset_function(), + arraysize(argv), + argv); +} + +void JSQuicSessionListener::OnSessionDestroyed() { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + // Emit the 'close' event in JS. This needs to happen after destroying the + // connection, because doing so also releases the last qlog data. + session()->MakeCallback( + env->quic_on_session_destroyed_function(), 0, nullptr); +} + +void JSQuicSessionListener::OnSessionClose(QuicError error) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(error.code)), + Integer::New(env->isolate(), error.family) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_close_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnStreamReady(BaseObjectPtr stream) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local argv[] = { + stream->object(), + Number::New(env->isolate(), static_cast(stream->id())), + Number::New(env->isolate(), static_cast(stream->push_id())) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_ready_function(), + 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(); + Local servername = Undefined(env->isolate()); + const char* hostname = ctx->servername(); + if (hostname != nullptr) { + servername = + String::NewFromUtf8( + env->isolate(), + hostname, + v8::NewStringType::kNormal).ToLocalChecked(); + } + + int err = ctx->VerifyPeerIdentity( + hostname != nullptr ? + hostname : + session()->hostname().c_str()); + + Local argv[] = { + servername, + GetALPNProtocol(*session()), + ctx->cipher_name().ToLocalChecked(), + ctx->cipher_version().ToLocalChecked(), + Integer::New(env->isolate(), session()->max_pktlen_), + crypto::GetValidationErrorReason(env, err).ToLocalChecked(), + crypto::GetValidationErrorCode(env, err).ToLocalChecked(), + 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 ptr(session()); + session()->MakeCallback( + env->quic_on_session_handshake_function(), + 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 = env->context(); + Context::Scope context_scope(context); + Local 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 ptr(session()); + session()->MakeCallback( + env->quic_on_session_path_validation_function(), + 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()); + + Local argv[] = { + v8::Undefined(env->isolate()), + v8::Undefined(env->isolate()) + }; + + AllocatedBuffer session_ticket = env->AllocateManaged(size); + unsigned char* session_data = + reinterpret_cast(session_ticket.data()); + memset(session_data, 0, size); + i2d_SSL_SESSION(sess, &session_data); + if (!session_ticket.empty()) + argv[0] = session_ticket.ToBuffer().ToLocalChecked(); + + if (session()->is_flag_set( + QuicSession::QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS)) { + argv[1] = Buffer::Copy( + env, + reinterpret_cast(&session()->transport_params_), + sizeof(session()->transport_params_)).ToLocalChecked(); + } + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_ticket_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnSessionSilentClose( + bool stateless_reset, + QuicError error) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + stateless_reset ? v8::True(env->isolate()) : v8::False(env->isolate()), + Number::New(env->isolate(), static_cast(error.code)), + Integer::New(env->isolate(), error.family) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_silent_close_function(), arraysize(argv), argv); +} + +void JSQuicSessionListener::OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Local context = env->context(); + Context::Scope context_scope(context); + + std::string hostname = family == AF_INET ? + preferred_address.preferred_ipv4_address(): + preferred_address.preferred_ipv6_address(); + int16_t port = + family == AF_INET ? + preferred_address.preferred_ipv4_port() : + preferred_address.preferred_ipv6_port(); + + Local argv[] = { + String::NewFromOneByte( + env->isolate(), + reinterpret_cast(hostname.c_str()), + v8::NewStringType::kNormal).ToLocalChecked(), + Integer::New(env->isolate(), port), + Integer::New(env->isolate(), family) + }; + + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_use_preferred_address_function(), + 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 = env->context(); + Context::Scope context_scope(context); + + MaybeStackBuffer, 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 supported = + Integer::New(env->isolate(), supported_version); + + Local 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 ptr(session()); + session()->MakeCallback( + env->quic_on_session_version_negotiation_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnQLog(const uint8_t* data, size_t len) { + Environment* env = session()->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local str = + String::NewFromOneByte(env->isolate(), + data, + v8::NewStringType::kNormal, + len).ToLocalChecked(); + + session()->MakeCallback(env->quic_on_session_qlog_function(), 1, &str); +} + +// 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 + + +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_key( + session()->connection(), + ssl_.get(), + rx_key, + rx_iv, + rx_hp, + tx_key, + tx_iv, + tx_hp, + level, + rx_secret, + tx_secret, + secretlen, + side_))) { + 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, + size_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 %d crypto bytes for %s level", + datalen, + crypto_level_name(level)); + + // Consumes (frees) the given number of bytes in the handshake buffer. + handshake_[level].Consume(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_[ + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] == 0)) { + return 0; + } + + TLSCallbackScope callback_scope(this); + + // Not an error but does suspend the handshake until we're ready to go. + // A callback function is passed to the JavaScript function below that + // must be called in order to turn QUICSESSION_FLAG_CLIENT_HELLO_CB_RUNNING + // off. Once that callback is invoked, the TLS Handshake will resume. + // It is recommended that the user not take a long time to invoke the + // callback in order to avoid stalling out the QUIC connection. + if (in_client_hello_) + return -1; + in_client_hello_ = true; + + 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 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_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] == 0)) { + Debug(session(), "No OCSPRequest handler registered"); + 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 (in_ocsp_request_) + return -1; + in_ocsp_request_ = true; + + 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 in_ocsp_request_ ? -1 : 1; +} + +// The OnCertDone function is called by the QuicSessionOnCertDone +// function when usercode is done handling the OCSPRequest event. +void QuicCryptoContext::OnOCSPDone( + BaseObjectPtr context, + Local ocsp_response) { + Debug(session(), + "OCSPRequest completed. Context Provided? %s, OCSP Provided? %s", + context ? "Yes" : "No", + ocsp_response->IsArrayBufferView() ? "Yes" : "No"); + // Continue the TLS handshake when this function exits + // otherwise it will stall and fail. + TLSHandshakeScope handshake_scope(this, &in_ocsp_request_); + + // Disable the callback at this point so we don't loop continuously + session_->state_[IDX_QUIC_SESSION_STATE_CERT_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); + } + } + + if (ocsp_response->IsArrayBufferView()) { + ocsp_response_.Reset( + session_->env()->isolate(), + ocsp_response.As()); + } +} + +// 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) { + + auto maybe_init_app = OnScopeLeave([&]() { + if (level == NGTCP2_CRYPTO_LEVEL_APP) + session()->InitApplication(); + }); + + 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(); + + 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 obj = + PersistentToLocal::Default( + env->isolate(), + ocsp_response_); + size_t len = obj->ByteLength(); + + unsigned char* data = crypto::MallocOpenSSL(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: { + Local 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(), + ssl_.get(), + 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([&]() { in_key_update_ = false; }); + CHECK(!in_key_update_); + in_key_update_ = true; + Debug(session(), "Initiating Key Update"); + + session_->IncrementStat(&QuicSessionStats::keyupdate_count); + + return ngtcp2_conn_initiate_key_update( + session_->connection(), + uv_hrtime()) == 0; +} + +int QuicCryptoContext::VerifyPeerIdentity(const char* hostname) { + int err = crypto::VerifyPeerCertificate(ssl_); + if (err) + return err; + + // QUIC clients are required to verify the peer identity, servers are not. + switch (side_) { + case NGTCP2_CRYPTO_SIDE_CLIENT: + if (LIKELY(is_option_set( + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY))) { + return VerifyHostnameIdentity(ssl_, hostname); + } + break; + case NGTCP2_CRYPTO_SIDE_SERVER: + // TODO(@jasnell): In the future, we may want to implement this but + // for now we keep things simple and skip peer identity verification. + break; + } + + return 0; +} + +// 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 buffer = + std::make_unique(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 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 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()->SilentClose(); + 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_STREAM_MORE: + CHECK_GT(ndatalen, 0); + CHECK(StreamCommit(&stream_data, ndatalen)); + pos += ndatalen; + continue; + } + session()->set_last_error(QUIC_ERROR_SESSION, static_cast(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; +} + +void QuicApplication::MaybeSetFin(const StreamData& stream_data) { + if (ShouldSetFin(stream_data)) + set_stream_fin(stream_data.id); +} + +void QuicApplication::StreamOpen(int64_t stream_id) { + Debug(session(), "QUIC Stream %" PRId64 " is open", stream_id); +} + +void QuicApplication::StreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& 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) { + 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 == NGTCP2_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 wrap, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + 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 + rcid, + options, + nullptr) { + // The config is copied by assignment in the call below. + InitServer(config, local_addr, remote_addr, dcid, ocid, version, qlog); +} + +// Client QuicSession Constructor +QuicSession::QuicSession( + QuicSocket* socket, + v8::Local wrap, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + Local 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) { + 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 wrap, + BaseObjectPtr secure_context, + AsyncWrap::ProviderType provider_type, + const std::string& alpn, + const std::string& hostname, + const QuicCID& rcid, + 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_(new Timer(socket->env(), [this]() { OnIdleTimeout(); })), + retransmit_(new Timer(socket->env(), [this]() { MaybeTimeout(); })), + rcid_(rcid), + state_(env()->isolate(), IDX_QUIC_SESSION_STATE_COUNT) { + PushListener(&default_listener_); + set_connection_id_strategy(RandomConnectionIDStrategy); + set_preferred_address_strategy(preferred_address_strategy); + crypto_context_.reset( + new QuicCryptoContext( + this, + secure_context, + side, + options)); + application_.reset(SelectApplication(this)); + + // TODO(@jasnell): For now, the following is a check rather than properly + // handled. Before this code moves out of experimental, this should be + // properly handled. + wrap->DefineOwnProperty( + env()->context(), + env()->state_string(), + state_.GetJSArray(), + PropertyAttribute::ReadOnly).Check(); + + // TODO(@jasnell): memory accounting + // env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); +} + +QuicSession::~QuicSession() { + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + crypto_context_->Cancel(); + connection_.reset(); + + QuicSessionListener* listener_ = listener(); + listener_->OnSessionDestroyed(); + if (listener_ == listener()) + RemoveListener(listener_); + + DebugStats(); +} + +template +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(get_async_id())) + ")"; +} + +// Locate the QuicStream with the given id or return nullptr +BaseObjectPtr QuicSession::FindStream(int64_t id) const { + auto it = streams_.find(id); + return it == std::end(streams_) ? BaseObjectPtr() : it->second; +} + +// Invoked when ngtcp2 receives an acknowledgement for stream data. +void QuicSession::AckedStreamDataOffset( + int64_t stream_id, + uint64_t offset, + size_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 (LIKELY(!is_flag_set(QUICSESSION_FLAG_DESTROYED))) { + Debug(this, + "Received acknowledgement for %" PRIu64 + " bytes of stream %" PRId64 " data", + datalen, stream_id); + + application_->AcknowledgeStreamData(stream_id, offset, 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(this)); + switch (crypto_context_->side()) { + case NGTCP2_CRYPTO_SIDE_SERVER: { + socket->AssociateCID(rcid_, scid_); + socket->AssociateCID(pscid_, scid_); + break; + } + case NGTCP2_CRYPTO_SIDE_CLIENT: { + std::vector 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 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(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 stream) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_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; + } +} + +// Like the silent close, the immediate close must start with +// the JavaScript side, first shutting down any existing +// streams before entering the closing period. Unlike silent +// close, however, all streams are closed using proper +// STOP_SENDING and RESET_STREAM frames and a CONNECTION_CLOSE +// frame is ultimately sent to the peer. This makes the +// naming a bit of a misnomer in that the connection is +// not immediately torn down, but is allowed to drain +// properly per the QUIC spec description of "immediate close". +void QuicSession::ImmediateClose() { + // Calling either ImmediateClose or SilentClose will cause + // the QUICSESSION_FLAG_CLOSING to be set. In either case, + // we should never re-enter ImmediateClose or SilentClose. + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + set_flag(QUICSESSION_FLAG_CLOSING); + + QuicError err = last_error(); + Debug(this, "Immediate close with code %" PRIu64 " (%s)", + err.code, + err.family_name()); + + listener()->OnSessionClose(err); +} + +// Creates a new stream object and passes it off to the javascript side. +// This has to be called from within a handlescope/contextscope. +BaseObjectPtr QuicSession::CreateStream(int64_t stream_id) { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + CHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + + BaseObjectPtr stream = QuicStream::New(this, stream_id); + CHECK(stream); + listener()->OnStreamReady(stream); + return stream; +} + +// Mark the QuicSession instance destroyed. After this is called, +// the QuicSession instance will be generally unusable but most +// likely will not be immediately freed. +void QuicSession::Destroy() { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + // If we're not in the closing or draining periods, + // then we should at least attempt to send a connection + // close to the peer. + if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) && + !is_in_closing_period() && + !is_in_draining_period()) { + Debug(this, "Making attempt to send a connection close"); + set_last_error(); + SendConnectionClose(); + } + + // Streams should have already been destroyed by this point. + CHECK(streams_.empty()); + + // Mark the session destroyed. + set_flag(QUICSESSION_FLAG_DESTROYED); + set_flag(QUICSESSION_FLAG_CLOSING, false); + set_flag(QUICSESSION_FLAG_GRACEFUL_CLOSING, false); + + // Stop and free the idle and retransmission timers if they are active. + StopIdleTimer(); + StopRetransmitTimer(); + + // 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 shared_ptr 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. +bool QuicSession::GetNewConnectionID( + ngtcp2_cid* cid, + uint8_t* token, + size_t cidlen) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + CHECK_NOT_NULL(connection_id_strategy_); + connection_id_strategy_(this, cid, cidlen); + QuicCID cid_(cid); + StatelessResetToken(token, socket()->session_reset_secret(), cid_); + AssociateCID(cid_); + return true; +} + +void QuicSession::HandleError() { + if (is_destroyed() || (is_in_closing_period() && !is_server())) + return; + + if (!SendConnectionClose()) { + set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_INTERNAL); + ImmediateClose(); + } +} + +// The the retransmit libuv timer fires, it will call MaybeTimeout, +// which determines whether or not we need to retransmit data to +// to packet loss or ack delay. +void QuicSession::MaybeTimeout() { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + uint64_t now = uv_hrtime(); + bool transmit = false; + if (ngtcp2_conn_loss_detection_expiry(connection()) <= now) { + Debug(this, "Retransmitting due to loss detection"); + CHECK_EQ(ngtcp2_conn_on_loss_detection_timer(connection(), now), 0); + IncrementStat(&QuicSessionStats::loss_retransmit_count); + transmit = true; + } else if (ngtcp2_conn_ack_delay_expiry(connection()) <= now) { + Debug(this, "Retransmitting due to ack delay"); + ngtcp2_conn_cancel_expired_ack_delay_timer(connection(), now); + IncrementStat(&QuicSessionStats::ack_delay_retransmit_count); + transmit = true; + } + if (transmit) + SendPendingData(); +} + +bool QuicSession::OpenBidirectionalStream(int64_t* stream_id) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + return ngtcp2_conn_open_bidi_stream(connection(), stream_id, nullptr) == 0; +} + +bool QuicSession::OpenUnidirectionalStream(int64_t* stream_id) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + if (ngtcp2_conn_open_uni_stream(connection(), stream_id, nullptr)) + return false; + ngtcp2_conn_shutdown_stream_read(connection(), *stream_id, 0); + return true; +} + +// When ngtcp2 receives a successfull 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_[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] == 1)) { + listener_->OnPathValidation( + res, + reinterpret_cast(path->local.addr), + reinterpret_cast(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 (Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) || + is_flag_set(QUICSESSION_FLAG_DESTROYED) || + is_flag_set(QUICSESSION_FLAG_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(); +} + +// A Retry will effectively restart the TLS handshake process +// by generating new initial crypto material. This should only ever +// be called on client sessions +bool QuicSession::ReceiveRetry() { + CHECK(!is_server()); + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + Debug(this, "A retry packet was received. Restarting the handshake"); + IncrementStat(&QuicSessionStats::retry_count); + return DeriveAndInstallInitialKey(*this, dcid()); +} + +// 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) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) { + Debug(this, "Ignoring packet because session is destroyed"); + return false; + } + + Debug(this, "Receiving QUIC packet"); + IncrementStat(&QuicSessionStats::bytes_received, nread); + + // Closing period starts once ngtcp2 has detected that the session + // is being shutdown locally. Note that this is different that the + // is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING) function, which + // indicates a graceful shutdown that allows the session and streams + // to finish naturally. When is_in_closing_period is true, ngtcp2 is + // actively in the process of shutting down the connection and a + // CONNECTION_CLOSE has already been sent. The only thing we can do + // at this point is either ignore the packet or send another + // CONNECTION_CLOSE. + if (is_in_closing_period()) { + Debug(this, "QUIC 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 (!ShouldAttemptConnectionClose()) { + Debug(this, "Not sending connection close"); + return false; + } + Debug(this, "Sending connection close"); + return SendConnectionClose(); + } + + // When is_in_draining_period is true, ngtcp2 has received a + // connection close and we are simply discarding received packets. + // No outbound packets may be sent. Return true here because + // the packet was correctly processed, even tho it is being + // ignored. + if (is_in_draining_period()) { + Debug(this, "QUIC packet received while in draining period"); + return true; + } + + // It's possible for the remote address to change from one + // packet to the next so we have to look at the addr on + // every packet. + remote_address_ = remote_addr; + QuicPath path(local_addr, remote_address_); + + { + // 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(); + }); + Debug(this, "Processing received packet"); + HandleScope handle_scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + if (!ReceivePacket(&path, data, nread)) { + Debug(this, "Failure processing received packet (code %" PRIu64 ")", + last_error().code); + HandleError(); + return false; + } + } + + if (is_destroyed()) { + Debug(this, "Session was destroyed while processing the received packet"); + // 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. + if (!is_in_closing_period() && !is_in_draining_period()) { + Debug(this, "Attempting to send connection close"); + set_last_error(); + SendConnectionClose(); + } + return true; + } + + // 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. + GetConnectionCloseInfo(); + SilentClose(); + return true; + } + Debug(this, "Sending pending data after processing packet"); + SendPendingData(); + UpdateIdleTimer(); + UpdateRecoveryStats(); + Debug(this, "Successfully processed received packet"); + return true; +} + +// The ReceiveClientInitial function is called by ngtcp2 when +// a new connection has been initiated. The very first step to +// establishing a communication channel is to setup the keys +// that will be used to secure the communication. +bool QuicSession::ReceiveClientInitial(const QuicCID& dcid) { + if (UNLIKELY(is_flag_set(QUICSESSION_FLAG_DESTROYED))) + return false; + Debug(this, "Receiving client initial parameters"); + crypto_context_->handshake_started(); + return DeriveAndInstallInitialKey(*this, dcid); +} + +// 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) { + DCHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + + // If the QuicSession has been destroyed, we're not going + // to process any more packets for it. + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return true; + + 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) { + // In either of the following two cases, the caller will + // handle the next steps... + case NGTCP2_ERR_DRAINING: + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + break; + default: + // Per ngtcp2: If NGTCP2_ERR_RETRY is returned, + // QuicSession must be a server and must perform + // address validation by sending a Retry packet + // then immediately close the connection. + if (err == NGTCP2_ERR_RETRY && is_server()) { + socket()->SendRetry(scid_, rcid_, local_address_, remote_address_); + ImmediateClose(); + break; + } + set_last_error(QUIC_ERROR_SESSION, err); + return false; + } + } + 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( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + auto leave = OnScopeLeave([&]() { + // This extends the flow control window for the entire session + // but not for the individual Stream. Stream flow control is + // only expanded as data is read on the JavaScript side. + // TODO(jasnell): The strategy for extending the flow control + // window is going to be revisited soon. We don't need to + // extend on every chunk of data. + ExtendOffset(datalen); + }); + + // QUIC does not permit zero-length stream packets if + // fin is not set. ngtcp2 prevents these from coming + // through but just in case of regression in that impl, + // let's double check and simply ignore such packets + // so we do not commit any resources. + if (UNLIKELY(fin == 0 && datalen == 0)) + return true; + + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + // From here, we defer to the QuicApplication specific processing logic + return application_->ReceiveStreamData(stream_id, fin, 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(rcid_); + socket_->DisassociateCID(pscid_); + } + + std::vector cids(ngtcp2_conn_get_num_scid(connection())); + std::vector 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 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 no 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 (!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); + // Ensure that the stream state is closed and discarded by ngtcp2 + // Be sure to call this after removing the stream from the map + // above so that when ngtcp2 closes the stream, the callback does + // not attempt to loop back around and destroy the already removed + // QuicStream instance. Typically, the stream is already going to + // be closed by this point. + ngtcp2_conn_shutdown_stream(connection(), stream_id, NGTCP2_NO_ERROR); +} + +// 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()); + uint64_t interval = (expiry - now) / 1000000UL; + if (expiry < now || interval == 0) interval = 1; + Debug(this, "Scheduling the retransmit timer for %" PRIu64, interval); + UpdateRetransmitTimer(interval); +} + +// 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(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(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_flag_set(QUICSESSION_FLAG_SILENT_CLOSE)) + 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(); + + // 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 we're not already in the closing period, + // first attempt to write any pending packets, then + // start the closing period. If closing period has + // already started, skip this. + if (!is_in_closing_period() && + (!WritePackets("server connection close - write packets") || + !StartClosingPeriod())) { + return false; + } + + UpdateIdleTimer(); + CHECK_GT(conn_closebuf_->length(), 0); + + return SendPacket(QuicPacket::Copy(conn_closebuf_)); + } + case NGTCP2_CRYPTO_SIDE_CLIENT: { + UpdateIdleTimer(); + auto packet = QuicPacket::Create("client connection close"); + + // If we're not already in the closing period, + // first attempt to write any pending packets, then + // start the closing period. Note that the behavior + // here is different than the server + if (!is_in_closing_period() && + !WritePackets("client connection close - write packets")) { + return false; + } + ssize_t nwrite = + SelectCloseFn(error.family)( + connection(), + nullptr, + packet->data(), + max_pktlen_, + error.code, + uv_hrtime()); + if (nwrite < 0) { + Debug(this, "Error writing connection close: %d", nwrite); + set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); + return false; + } + packet->set_length(nwrite); + return SendPacket(std::move(packet)); + } + default: + UNREACHABLE(); + } +} + +void QuicSession::IgnorePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address) { + Debug(session, "Ignoring server preferred address"); +} + +void QuicSession::UsePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address) { + static constexpr int idx = + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED; + 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_[idx] == 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 packet) { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + 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(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() { + // Do not proceed if: + // * We are in the ngtcp2 callback scope + // * The QuicSession has been destroyed + // * The QuicSession is in the draining period + // * The QuicSession is a server in the closing period + // * The QuicSession is not currently associated with a QuicSocket + if (Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) || + is_destroyed() || + is_in_draining_period() || + (is_server() && is_in_closing_period()) || + socket() == nullptr) { + return; + } + + if (!application_->SendPendingData()) { + Debug(this, "Error sending QUIC application data"); + HandleError(); + } +} + +// 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_flag_set(QUICSESSION_FLAG_DESTROYED)); + int size = i2d_SSL_SESSION(session, nullptr); + if (size > SecureContext::kMaxSessionSize) + return 0; + listener_->OnSessionTicket(size, session); + return 1; +} + +// A client QuicSession can be migrated to a different QuicSocket instance. +// TODO(@jasnell): This will be revisited. +bool QuicSession::set_socket(QuicSocket* socket, bool nat_rebinding) { + CHECK(!is_server()); + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + + if (is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)) + return false; + + if (socket == nullptr || socket == socket_.get()) + return true; + + Debug(this, "Migrating to %s", socket->diagnostic_name()); + + SendSessionScope send(this); + + // Ensure that we maintain a reference to keep this from being + // destroyed while we are starting the migration. + BaseObjectPtr 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(); + if (nat_rebinding) { + ngtcp2_addr addr; + ngtcp2_addr_init( + &addr, + local_address.data(), + local_address.length(), + nullptr); + ngtcp2_conn_set_local_addr(connection(), &addr); + } else { + QuicPath path(local_address, remote_address_); + if (ngtcp2_conn_initiate_migration( + connection(), + &path, + uv_hrtime()) != 0) { + return false; + } + } + + return true; +} + +void QuicSession::ResumeStream(int64_t stream_id) { + application()->ResumeStream(stream_id); +} + +void QuicSession::ResetStream(int64_t stream_id, uint64_t code) { + SendSessionScope scope(this); + CHECK_EQ(ngtcp2_conn_shutdown_stream(connection(), stream_id, code), 0); +} + +// Silent Close must start with the JavaScript side, which must +// clean up state, abort any still existing QuicSessions, then +// destroy the handle when done. The most important characteristic +// of the SilentClose is that no frames are sent to the peer. +// +// When a valid stateless reset is received, the connection is +// immediately and unrecoverably closed at the ngtcp2 level. +// Specifically, it will be put into the draining_period so +// absolutely no frames can be sent. What we need to do is +// notify the JavaScript side and destroy the connection with +// a flag set that indicates stateless reset. +void QuicSession::SilentClose() { + // Calling either ImmediateClose or SilentClose will cause + // the QUICSESSION_FLAG_CLOSING to be set. In either case, + // we should never re-enter ImmediateClose or SilentClose. + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + set_flag(QUICSESSION_FLAG_SILENT_CLOSE); + set_flag(QUICSESSION_FLAG_CLOSING); + + QuicError err = last_error(); + Debug(this, + "Silent close with %s code %" PRIu64 " (stateless reset? %s)", + err.family_name(), + err.code, + is_stateless_reset() ? "yes" : "no"); + + listener()->OnSessionSilentClose(is_stateless_reset(), err); +} +// 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_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + if (is_in_closing_period()) + return true; + + StopRetransmitTimer(); + UpdateIdleTimer(); + + 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) { + if (nwrite == NGTCP2_ERR_PKT_NUM_EXHAUSTED) { + set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_PKT_NUM_EXHAUSTED); + SilentClose(); + } else { + set_last_error(QUIC_ERROR_SESSION, static_cast(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) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + Debug(this, "Closing stream %" PRId64 " with code %" PRIu64, + stream_id, + app_error_code); + + application_->StreamClose(stream_id, app_error_code); +} + +// Called by ngtcp2 when a stream has been opened. All we do is log +// the activity here. We do not want to actually commit any resources +// until data is received for the stream. This allows us to prevent +// a stream commitment attack. The only exception is shutting the +// stream down explicitly if we are in a graceful close period. +void QuicSession::StreamOpen(int64_t stream_id) { + if (is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)) { + ngtcp2_conn_shutdown_stream( + connection(), + stream_id, + NGTCP2_ERR_CLOSING); + } + Debug(this, "Stream %" PRId64 " opened", stream_id); + return application_->StreamOpen(stream_id); +} + +// 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) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + Debug(this, + "Reset stream %" PRId64 " with code %" PRIu64 + " and final size %" PRIu64, + stream_id, + app_error_code, + final_size); + + BaseObjectPtr 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(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() { + CHECK_NOT_NULL(idle_); + 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); +} + + +// 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(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + + // During the draining period, we must not send any frames at all. + if (is_in_draining_period()) + return true; + + // During the closing period, we are only permitted to send + // CONNECTION_CLOSE frames. + if (is_in_closing_period()) { + return SendConnectionClose(); + } + + // 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: + 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. + SilentClose(); + return false; + default: + set_last_error(QUIC_ERROR_SESSION, static_cast(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->TrackField("state", state_); + tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_); + tracker->TrackField("conn_closebuf", conn_closebuf_); + tracker->TrackField("application", application_); + StatsBase::StatsMemoryInfo(tracker); +} + +// Static function to create a new server QuicSession instance +BaseObjectPtr QuicSession::CreateServer( + QuicSocket* socket, + const QuicSessionConfig& config, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn, + uint32_t options, + QlogMode qlog) { + Local obj; + if (!socket->env() + ->quicserversession_instance_template() + ->NewInstance(socket->env()->context()).ToLocal(&obj)) { + return {}; + } + BaseObjectPtr session = + MakeDetachedBaseObject( + socket, + config, + obj, + rcid, + local_addr, + remote_addr, + dcid, + 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& 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); + + 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(this)), 0); + + connection_.reset(conn); + + crypto_context_->Initialize(); + UpdateDataStats(); + UpdateIdleTimer(); +} + +namespace { +// A pointer to this function is passed to the JavaScript side during +// the client hello and is called by user code when the TLS handshake +// should resume. +void QuicSessionOnClientHelloDone(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->crypto_context()->OnClientHelloDone(); +} + +// This callback is invoked by user code after completing handling +// of the 'OCSPRequest' event. The callback is invoked with two +// possible arguments, both of which are optional +// 1. A replacement SecureContext +// 2. An OCSP response +void QuicSessionOnCertDone(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + + Local cons = env->secure_context_constructor_template(); + crypto::SecureContext* context = nullptr; + if (args[0]->IsObject() && cons->HasInstance(args[0])) + context = Unwrap(args[0].As()); + session->crypto_context()->OnOCSPDone( + BaseObjectPtr(context), + args[1]); +} +} // namespace + +// Recovery stats are used to allow user code to keep track of +// important round-trip timing statistics that are updated through +// the lifetime of a connection. Effectively, these communicate how +// much time (from the perspective of the local peer) is being taken +// to exchange data reliably with the remote peer. +void QuicSession::UpdateRecoveryStats() { + const ngtcp2_rcvry_stat* stat = + ngtcp2_conn_get_rcvry_stat(connection()); + SetStat(&QuicSessionStats::min_rtt, stat->min_rtt); + SetStat(&QuicSessionStats::latest_rtt, stat->latest_rtt); + SetStat(&QuicSessionStats::smoothed_rtt, stat->smoothed_rtt); +} + +// 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_[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] = + static_cast(ngtcp2_conn_get_max_data_left(connection())); + size_t bytes_in_flight = ngtcp2_conn_get_bytes_in_flight(connection()); + state_[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] = + static_cast(bytes_in_flight); + // The max_bytes_in_flight is a highwater mark that can be used + // in performance analysis operations. + if (bytes_in_flight > GetStat(&QuicSessionStats::max_bytes_in_flight)) + SetStat(&QuicSessionStats::max_bytes_in_flight, bytes_in_flight); +} + +// Static method for creating a new client QuicSession instance. +BaseObjectPtr QuicSession::CreateClient( + QuicSocket* socket, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + Local dcid, + PreferredAddressStrategy preferred_address_strategy, + const std::string& alpn, + const std::string& hostname, + uint32_t options, + QlogMode qlog) { + Local obj; + if (!socket->env() + ->quicclientsession_instance_template() + ->NewInstance(socket->env()->context()).ToLocal(&obj)) { + return {}; + } + + BaseObjectPtr session = + MakeDetachedBaseObject( + 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 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(env()); + 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 sbuf( + dcid_value.As()); + 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(this)), 0); + + + connection_.reset(conn); + + crypto_context_->Initialize(); + + CHECK(DeriveAndInstallInitialKey(*this, this->dcid())); + + 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 upon creation of a new client connection +// to initiate the TLS handshake. This is only emitted on the client side. +int QuicSession::OnClientInitial( + ngtcp2_conn* conn, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return NGTCP2_OK(session->crypto_context()->Receive( + NGTCP2_CRYPTO_LEVEL_INITIAL, + 0, nullptr, 0)) ? 0 : NGTCP2_ERR_CALLBACK_FAILURE; +} + +// Triggered by ngtcp2 when an initial handshake packet has been +// received. This is only invoked on server sessions and it is +// the absolute beginning of the communication between a client +// and a server. +int QuicSession::OnReceiveClientInitial( + ngtcp2_conn* conn, + const ngtcp2_cid* dcid, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + if (!session->ReceiveClientInitial(QuicCID(dcid))) { + Debug(session, "Receiving initial client handshake failed"); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +// 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return static_cast( + session->crypto_context()->Receive(crypto_level, offset, data, datalen)); +} + +// Triggered by ngtcp2 when a RETRY packet has been received. This is +// only emitted on the client side (only a server can send a RETRY). +// +// Per the QUIC specification, a RETRY is essentially a mechanism for +// the server to force path validation at the very start of a connection +// at the cost of a single round trip. The RETRY includes a token that +// the client must use in subsequent requests. When received, the client +// MUST restart the TLS handshake and must include the RETRY token in +// all initial packets. If the initial packets contain a valid RETRY +// token, then the server assumes the path to be validated. Fortunately +// ngtcp2 handles the retry token for us, so all we have to do is +// regenerate the initial keying material and restart the handshake +// and we can ignore the retry parameter. +int QuicSession::OnReceiveRetry( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const ngtcp2_pkt_retry* retry, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + if (!session->ReceiveRetry()) { + Debug(session, "Receiving retry token failed"); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +// 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + + QuicCID qcid(cid); + Debug(session, "Updating connection ID %s (has reset token? %s)", + qcid, + token == nullptr ? "No" : "Yes"); + if (token != nullptr) + 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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, + int64_t stream_id, + int fin, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return session->ReceiveStreamData(stream_id, fin, 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->StreamOpen(stream_id); + 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, + size_t datalen, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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, + size_t datalen, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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. + QuicPreferredAddress 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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). +// TODO(@jasnell): In the future, we'll want to explore whether +// we want to handle the different cases of ngtcp2_rand_ctx +int QuicSession::OnRand( + ngtcp2_conn* conn, + uint8_t* dest, + size_t destlen, + ngtcp2_rand_ctx ctx, + void* user_data) { + 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(session)); + return session->GetNewConnectionID(cid, token, cidlen) ? + 0 : NGTCP2_ERR_CALLBACK_FAILURE; +} + +// 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->RemoveConnectionID(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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope 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(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->set_flag(QUICSESSION_FLAG_STATELESS_RESET); + return 0; +} + +void QuicSession::OnQlogWrite(void* user_data, const void* data, size_t len) { + QuicSession* session = static_cast(user_data); + session->listener()->OnQLog(reinterpret_cast(data), len); +} + +const ngtcp2_conn_callbacks QuicSession::callbacks[2] = { + // NGTCP2_CRYPTO_SIDE_CLIENT + { + OnClientInitial, + nullptr, + OnReceiveCryptoData, + OnHandshakeCompleted, + OnVersionNegotiation, + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + OnReceiveStreamData, + OnAckedCryptoOffset, + OnAckedStreamDataOffset, + OnStreamOpen, + OnStreamClose, + OnStatelessReset, + OnReceiveRetry, + OnExtendMaxStreamsBidi, + OnExtendMaxStreamsUni, + OnRand, + OnGetNewConnectionID, + OnRemoveConnectionID, + ngtcp2_crypto_update_key_cb, + OnPathValidation, + OnSelectPreferredAddress, + OnStreamReset, + OnExtendMaxStreamsRemoteBidi, + OnExtendMaxStreamsRemoteUni, + OnExtendMaxStreamData, + OnConnectionIDStatus, + OnHandshakeConfirmed + }, + // NGTCP2_CRYPTO_SIDE_SERVER + { + nullptr, + OnReceiveClientInitial, + 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 + } +}; + +// JavaScript API + +namespace { +void QuicSessionSetSocket(const FunctionCallbackInfo& args) { + QuicSession* session; + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As()); + args.GetReturnValue().Set(session->set_socket(socket)); +} + +// Perform an immediate close on the QuicSession, causing a +// CONNECTION_CLOSE frame to be scheduled and sent and starting +// the closing period for this session. The name "ImmediateClose" +// is a bit of an unfortunate misnomer as the session will not +// be immediately shutdown. The naming is pulled from the QUIC +// spec to indicate a state where the session immediately enters +// the closing period, but the session will not be destroyed +// until either the idle timeout fires or destroy is explicitly +// called. +void QuicSessionClose(const FunctionCallbackInfo& 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->SendConnectionClose(); +} + +// 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& 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& 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& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->ephemeral_key().ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionGetPeerCertificate(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->peer_cert(!args[0]->IsTrue()).ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionGetRemoteAddress( + const FunctionCallbackInfo& 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())); +} + +void QuicSessionGetCertificate( + const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->cert().ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionPing(const FunctionCallbackInfo& 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& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + ProcessEmitWarning( + session->env(), + "Forcing silent close of QuicSession for testing purposes only"); + session->SilentClose(); +} + +// TODO(addaleax): This is a temporary solution for testing and should be +// removed later. +void QuicSessionRemoveFromSocket(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->RemoveFromSocket(); +} + +void QuicSessionUpdateKey(const FunctionCallbackInfo& 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, + ngtcp2_transport_params* params) { + if (value->IsUndefined()) + return false; + CHECK(value->IsArrayBufferView()); + ArrayBufferViewContents sbuf(value.As()); + if (sbuf.length() != sizeof(ngtcp2_transport_params)) + return false; + memcpy(params, sbuf.data(), sizeof(ngtcp2_transport_params)); + return true; +} + +crypto::SSLSessionPointer DecodeSessionTicket(Local value) { + if (value->IsUndefined()) + return {}; + CHECK(value->IsArrayBufferView()); + ArrayBufferViewContents sbuf(value.As()); + return crypto::GetTLSSession(sbuf.data(), sbuf.length()); +} + +void QuicSessionStartHandshake(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->StartHandshake(); +} + +void NewQuicClientSession(const FunctionCallbackInfo& 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(NGTCP2_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, + AUTO_START + }; + + 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)); + CHECK(args[ARG_IDX::AUTO_START]->IsBoolean()); + if (!args[ARG_IDX::SNI]->IsUndefined()) + CHECK(args[ARG_IDX::SNI]->IsString()); + + ASSIGN_OR_RETURN_UNWRAP(&socket, args[ARG_IDX::SOCKET].As()); + ASSIGN_OR_RETURN_UNWRAP(&sc, args[ARG_IDX::SECURE_CONTEXT].As()); + + CHECK(args[ARG_IDX::PREFERRED_ADDRESS_POLICY]->Int32Value( + env->context()).To(&preferred_address_policy)); + switch (preferred_address_policy) { + case QUIC_PREFERRED_ADDRESS_ACCEPT: + 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 session = + QuicSession::CreateClient( + socket, + socket->local_address(), + remote_addr, + BaseObjectPtr(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); + + // Start the TLS handshake if the autoStart option is true + // (which it is by default). + if (args[ARG_IDX::AUTO_START]->BooleanValue(env->isolate())) { + session->StartHandshake(); + // Session was created but was unable to bootstrap properly during + // the start of the TLS handshake. + 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 session) { + env->SetProtoMethod(session, "close", QuicSessionClose); + 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 target, + Local context) { + { + Local class_name = + FIXED_ONE_BYTE_STRING(env->isolate(), "QuicServerSession"); + Local session = FunctionTemplate::New(env->isolate()); + session->SetClassName(class_name); + session->Inherit(AsyncWrap::GetConstructorTemplate(env)); + Local sessiont = session->InstanceTemplate(); + sessiont->SetInternalFieldCount(1); + sessiont->Set(env->owner_symbol(), Null(env->isolate())); + AddMethods(env, session); + env->set_quicserversession_instance_template(sessiont); + } + + { + Local class_name = + FIXED_ONE_BYTE_STRING(env->isolate(), "QuicClientSession"); + Local session = FunctionTemplate::New(env->isolate()); + session->SetClassName(class_name); + session->Inherit(AsyncWrap::GetConstructorTemplate(env)); + Local sessiont = session->InstanceTemplate(); + sessiont->SetInternalFieldCount(1); + sessiont->Set(env->owner_symbol(), Null(env->isolate())); + AddMethods(env, session); + env->SetProtoMethod(session, + "getEphemeralKeyInfo", + QuicSessionGetEphemeralKeyInfo); + env->SetProtoMethod(session, + "setSocket", + QuicSessionSetSocket); + env->SetProtoMethod(session, "startHandshake", QuicSessionStartHandshake); + 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 new file mode 100644 index 00000000000000..63041f684175de --- /dev/null +++ b/src/quic/node_quic_session.h @@ -0,0 +1,1485 @@ +#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 "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_buffer-inl.h" +#include "node_quic_crypto.h" +#include "node_quic_util.h" +#include "node_sockaddr.h" +#include "v8.h" +#include "uv.h" + +#include +#include +#include + +#include +#include +#include + +namespace node { +namespace quic { + +using ConnectionPointer = DeleteFnPtr; + +class QuicApplication; +class QuicPacket; +class QuicSocket; +class QuicStream; + +using QuicHeader = NgHeaderImpl; + +using StreamsMap = std::unordered_map>; + +enum class QlogMode { + kDisabled, + kEnabled +}; + +typedef void(*ConnectionIDStrategy)( + QuicSession* session, + ngtcp2_cid* cid, + size_t cidlen); + +typedef void(*PreferredAddressStrategy)( + QuicSession* session, + const QuicPreferredAddress& 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 : public ngtcp2_settings { + public: + QuicSessionConfig() {} + + explicit QuicSessionConfig(Environment* env) { + Set(env); + } + + QuicSessionConfig(const QuicSessionConfig& config) { + initial_ts = uv_hrtime(); + transport_params = config.transport_params; + qlog = config.qlog; + log_printf = config.log_printf; + token = config.token; + } + + void ResetToDefaults(Environment* env); + + // 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(Environment* env, + const struct sockaddr* preferred_addr = nullptr); + + inline void set_original_connection_id(const QuicCID& ocid); + + // 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 +}; + + +// The QuicSessionState enums are used with the QuicSession's +// private state_ array. This is exposed to JavaScript via an +// aliased buffer and is used to communicate various types of +// state efficiently across the native/JS boundary. +enum QuicSessionState : int { + // Communicates whether a 'keylog' event listener has been + // registered on the JavaScript QuicSession object. The + // value will be either 1 or 0. When set to 1, the native + // code will emit TLS keylog entries to the JavaScript + // side triggering the 'keylog' event once for each line. + IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, + + // Communicates whether a 'clientHello' event listener has + // been registered on the JavaScript QuicServerSession. + // The value will be either 1 or 0. When set to 1, the + // native code will callout to the JavaScript side causing + // the 'clientHello' event to be emitted. This is only + // used on server QuicSession instances. + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, + + // Communicates whether a 'cert' event listener has been + // registered on the JavaScript QuicSession. The value will + // be either 1 or 0. When set to 1, the native code will + // callout to the JavaScript side causing the 'cert' event + // to be emitted. + IDX_QUIC_SESSION_STATE_CERT_ENABLED, + + // Communicates whether a 'pathValidation' event listener + // has been registered on the JavaScript QuicSession. The + // value will be either 1 or 0. When set to 1, the native + // code will callout to the JavaScript side causing the + // 'pathValidation' event to be emitted + IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, + + // Communicates the current max cumulative number of + // bidi and uni streams that may be opened on the session + IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, + + // Communicates the current maxinum number of bytes that + // the local endpoint can send in this connection + // (updated immediately after processing sent/received packets) + IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, + + // Communicates the current total number of bytes in flight + IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, + + // Communicates whether a 'usePreferredAddress' event listener + // has been registered. + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + + IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, + + // Communicates whether a session was closed due to idle timeout + IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, + + // Just the number of session state enums for use when + // creating the AliasedBuffer. + IDX_QUIC_SESSION_STATE_COUNT +}; + +#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(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(RETRY_COUNT, retry_count, "Retry 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") + +#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 + static void ToString(const Base& ptr, Fn&& add_field); +}; + +class QuicSessionListener { + public: + 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 ocsp); + virtual void OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& 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 OnSessionDestroyed(); + virtual void OnSessionClose(QuicError error); + virtual void OnStreamReady(BaseObjectPtr stream); + virtual void OnHandshakeCompleted(); + virtual void OnPathValidation( + ngtcp2_path_validation_result res, + const sockaddr* local, + const sockaddr* remote); + virtual void OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address); + virtual void OnSessionTicket(int size, SSL_SESSION* session); + virtual void OnSessionSilentClose( + bool stateless_reset, + QuicError error); + virtual void OnStreamBlocked(int64_t stream_id); + virtual void OnVersionNegotiation( + uint32_t supported_version, + const uint32_t* versions, + size_t vcnt); + virtual void OnQLog(const uint8_t* data, size_t len); + + QuicSession* session() const { return session_.get(); } + + private: + BaseObjectWeakPtr session_; + QuicSessionListener* previous_listener_ = nullptr; + friend class QuicSession; +}; + +class JSQuicSessionListener : 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 ocsp) override; + void OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& 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 OnSessionDestroyed() override; + void OnSessionClose(QuicError error) override; + void OnStreamReady(BaseObjectPtr 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 OnSessionSilentClose(bool stateless_reset, QuicError error) override; + void OnUsePreferredAddress( + int family, + const QuicPreferredAddress& 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(const uint8_t* data, size_t len) override; + + private: + friend class QuicSession; +}; + +// The QuicCryptoContext class encapsulates all of the crypto/TLS +// handshake details on behalf of a QuicSession. +class QuicCryptoContext : public MemoryRetainer { + public: + 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, size_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 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(); + + inline void OnClientHelloDone(); + + int OnOCSP(); + + void OnOCSPDone( + BaseObjectPtr secure_context, + v8::Local 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 cert() const; + inline v8::MaybeLocal cipher_name() const; + inline v8::MaybeLocal cipher_version() const; + inline v8::MaybeLocal ephemeral_key() const; + inline const char* hello_alpn() const; + inline v8::MaybeLocal hello_ciphers() const; + inline const char* hello_servername() const; + inline v8::MaybeLocal 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; + } + + inline bool set_session(crypto::SSLSessionPointer session); + + inline void set_tls_alert(int err); + + inline bool SetupInitialKey(const QuicCID& dcid); + + ngtcp2_crypto_side side() const { return side_; } + + void WriteHandshake( + ngtcp2_crypto_level level, + const uint8_t* data, + size_t datalen); + + bool InitiateKeyUpdate(); + + int VerifyPeerIdentity(const char* hostname); + + QuicSession* session() const { return session_.get(); } + + void MemoryInfo(MemoryTracker* tracker) const override; + + void handshake_started() { + is_handshake_started_ = true; + } + + bool is_handshake_started() const { return is_handshake_started_; } + + SET_MEMORY_INFO_NAME(QuicCryptoContext) + SET_SELF_SIZE(QuicCryptoContext) + + private: + inline QuicCryptoContext( + QuicSession* session, + BaseObjectPtr secure_context, + ngtcp2_crypto_side side, + uint32_t options); + + bool SetSecrets( + ngtcp2_crypto_level level, + const uint8_t* rx_secret, + const uint8_t* tx_secret, + size_t secretlen); + + BaseObjectWeakPtr session_; + BaseObjectPtr secure_context_; + ngtcp2_crypto_side side_; + crypto::SSLPointer ssl_; + QuicBuffer handshake_[3]; + bool is_handshake_started_ = false; + bool in_tls_callback_ = false; + bool in_key_update_ = false; + bool in_ocsp_request_ = false; + bool in_client_hello_ = false; + bool early_data_ = false; + uint32_t options_; + + v8::Global ocsp_response_; + crypto::BIOPointer bio_trace_; + + class TLSCallbackScope { + public: + explicit TLSCallbackScope(QuicCryptoContext* context) : + context_(context) { + context_->in_tls_callback_ = true; + } + + ~TLSCallbackScope() { + context_->in_tls_callback_ = false; + } + + static bool is_in_callback(QuicCryptoContext* context) { + return context->in_tls_callback_; + } + + private: + QuicCryptoContext* context_; + }; + + class TLSHandshakeScope { + public: + TLSHandshakeScope( + QuicCryptoContext* context, + bool* monitor) : + context_(context), + monitor_(monitor) {} + + ~TLSHandshakeScope() { + if (!is_handshake_suspended()) + return; + + *monitor_ = false; + // 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_->in_ocsp_request_ || context_->in_client_hello_; + } + + + QuicCryptoContext* context_; + bool* monitor_; + }; + + 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; + + virtual bool Initialize() = 0; + virtual bool ReceiveStreamData( + int64_t stream_id, + int fin, + 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) {} + virtual void SetSessionTicketAppData(const SessionTicketAppData& app_data) { + // TODO(@jasnell): Different QUIC applications may wish to set some + // application data in the session ticket (e.g. http/3 would set + // server settings in the application data). For now, doing nothing + // as I'm just adding the basic mechanism. + } + virtual SessionTicketAppData::Status GetSessionTicketAppData( + const SessionTicketAppData& app_data, + SessionTicketAppData::Flag flag) { + // TODO(@jasnell): Different QUIC application may wish to set some + // application data in the session ticket (e.g. http/3 would set + // server settings in the application data). For now, doing nothing + // as I'm just adding the basic mechanism. + 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>& headers, + int64_t push_id = 0); + virtual void StreamClose( + int64_t stream_id, + uint64_t app_error_code); + virtual void StreamOpen(int64_t stream_id); + virtual void StreamReset( + int64_t stream_id, + uint64_t app_error_code); + virtual bool SubmitInformation( + int64_t stream_id, + v8::Local headers) { return false; } + virtual bool SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) { return false; } + virtual bool SubmitTrailers( + int64_t stream_id, + v8::Local headers) { return false; } + virtual BaseObjectPtr SubmitPush( + int64_t stream_id, + v8::Local 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 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 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; + + inline ssize_t WriteVStream( + QuicPathStorage* path, + uint8_t* buf, + ssize_t* ndatalen, + const StreamData& stream_data); + + private: + void MaybeSetFin(const StreamData& stream_data); + BaseObjectWeakPtr session_; + bool needs_init_ = true; + size_t max_header_pairs_ = 0; + size_t max_header_length_ = 0; +}; + +// The QuicSession class is an virtual class that serves as +// the basis for both client and server QuicSession. +// It implements the functionality that is shared for both +// QUIC clients and servers. +// +// QUIC sessions are virtual connections that exchange data +// back and forth between peer endpoints via UDP. Every QuicSession +// has an associated TLS context and all data transfered 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 : public AsyncWrap, + public mem::NgLibMemoryManager, + public StatsBase { + public: + // The default preferred address strategy is to ignore it + static void IgnorePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address); + + static void UsePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address); + + static void Initialize( + Environment* env, + v8::Local target, + v8::Local context); + + static BaseObjectPtr CreateServer( + QuicSocket* socket, + const QuicSessionConfig& config, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn = NGTCP2_ALPN_H3, + uint32_t options = 0, + QlogMode qlog = QlogMode::kDisabled); + + static BaseObjectPtr CreateClient( + QuicSocket* socket, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + v8::Local dcid, + PreferredAddressStrategy preferred_address_strategy = + IgnorePreferredAddressStrategy, + const std::string& alpn = NGTCP2_ALPN_H3, + const std::string& hostname = "", + uint32_t options = 0, + QlogMode qlog = QlogMode::kDisabled); + + static const int kInitialClientBufferLength = 4096; + + // The QuicSession::CryptoContext encapsulates all details of the + // TLS context on behalf of the QuicSession. + 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 wrap, + BaseObjectPtr 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& rcid, + uint32_t options = 0, + PreferredAddressStrategy preferred_address_strategy = + IgnorePreferredAddressStrategy); + + // Server Constructor + QuicSession( + QuicSocket* socket, + const QuicSessionConfig& config, + v8::Local wrap, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn, + uint32_t options, + QlogMode qlog); + + // Client Constructor + QuicSession( + QuicSocket* socket, + v8::Local wrap, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + v8::Local 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; + + // When a client QuicSession is created, if the autoStart + // option is true, the handshake will be immediately started. + // If autoStart is false, the start of the handshake will be + // deferred until the start handshake method is called; + inline void StartHandshake(); + + QuicApplication* application() const { return application_.get(); } + + QuicCryptoContext* crypto_context() const { return crypto_context_.get(); } + + QuicSessionListener* listener() const { return listener_; } + + BaseObjectPtr CreateStream(int64_t id); + BaseObjectPtr FindStream(int64_t id) const; + inline bool HasStream(int64_t id) const; + + inline bool allow_early_data() const; + + // Returns true if StartGracefulClose() has been called and the + // QuicSession is currently in the process of a graceful close. + inline bool is_gracefully_closing() const; + + // Returns true if Destroy() has been called and the + // QuicSession is no longer usable. + inline bool is_destroyed() const; + + inline bool is_stateless_reset() const; + + // Returns true if the QuicSession has entered the + // closing period following a call to ImmediateClose. + // 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 ImmediateClose() is called. + inline void StartGracefulClose(); + + QuicError last_error() const { return last_error_; } + + size_t max_packet_length() const { return max_pktlen_; } + + // 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 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 MaybeTimeout(); + + // 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( + int64_t stream_id, + int fin, + 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 bool SendPacket( + std::unique_ptr 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_; } + + // 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 bi-directional stream, a STOP_SENDING + // frame will be sent. + // + // It is important to note that the QuicStream is + // not destroyed immediately following ShutdownStream. + // The sending QuicSession will not close the stream + // until the RESET_STREAM is acknowledged. + // + // Once the RESET_STREAM is sent, the QuicSession + // should not send any new frames for the stream, + // and all inbound stream frames should be discarded. + // Once ngtcp2 receives the appropriate notification + // that the RESET_STREAM has been acknowledged, the + // stream will be closed. + // + // Once the stream has been closed, it will be + // destroyed and memory will be freed. User code + // can request that a stream be immediately and + // abruptly destroyed without calling ShutdownStream. + // Likewise, an idle timeout may cause the stream + // to be silently destroyed without calling + // ShutdownStream. + void ResetStream( + int64_t stream_id, + uint64_t error_code = NGTCP2_APP_NOERROR); + + 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 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 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 headers); + + inline BaseObjectPtr SubmitPush( + int64_t stream_id, + v8::Local 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); + + // Immediately close the QuicSession. All currently open + // streams are implicitly reset and closed with RESET_STREAM + // and STOP_SENDING frames transmitted as necessary. A + // CONNECTION_CLOSE frame will be sent and the session + // will enter the closing period until either the idle + // timeout period elapses or until the QuicSession is + // explicitly destroyed. During the closing period, + // the only frames that may be transmitted to the peer + // are repeats of the already sent CONNECTION_CLOSE. + // + // The CONNECTION_CLOSE will use the error code set using + // the most recent call to set_last_error() + void ImmediateClose(); + + // Silently, and immediately close the QuicSession. This is + // generally only done during an idle timeout. That is, per + // the QUIC specification, if the session remains idle for + // longer than both the advertised idle timeout and three + // times the current probe timeout (PTO). In such cases, all + // currently open streams are implicitly reset and closed + // without sending corresponding RESET_STREAM and + // STOP_SENDING frames, the connection state is + // discarded, and the QuicSession is destroyed without + // sending a CONNECTION_CLOSE frame. + // + // Silent close may also be used to explicitly destroy + // a QuicSession that has either already entered the + // closing or draining periods; or in response to user + // code requests to forcefully terminate a QuicSession + // without transmitting any additional frames to the + // peer. + void SilentClose(); + + void PushListener(QuicSessionListener* listener); + void RemoveListener(QuicSessionListener* listener); + + 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 QuicPreferredAddress& 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 { + public: + explicit SendSessionScope( + QuicSession* session, + bool wait_for_handshake = false) + : session_(session), + wait_for_handshake_(wait_for_handshake) { + CHECK(session_); + } + + ~SendSessionScope() { + if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(session_.get()) && + (!wait_for_handshake_ || + session_->crypto_context()->is_handshake_started())) + session_->SendPendingData(); + } + + private: + BaseObjectPtr session_; + bool wait_for_handshake_ = 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 Ngtcp2CallbackScope { + public: + explicit Ngtcp2CallbackScope(QuicSession* session) : session_(session) { + CHECK(session_); + CHECK(!InNgtcp2CallbackScope(session)); + session_->set_flag(QUICSESSION_FLAG_NGTCP2_CALLBACK); + } + + ~Ngtcp2CallbackScope() { + session_->set_flag(QUICSESSION_FLAG_NGTCP2_CALLBACK, false); + } + + static bool InNgtcp2CallbackScope(QuicSession* session) { + return session->is_flag_set(QUICSESSION_FLAG_NGTCP2_CALLBACK); + } + + private: + BaseObjectPtr session_; + }; + + 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& 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 dcid, + QlogMode qlog); + + inline void InitApplication(); + + void AckedStreamDataOffset( + int64_t stream_id, + uint64_t offset, + size_t datalen); + + inline void AssociateCID(const QuicCID& cid); + inline void DisassociateCID(const QuicCID& cid); + inline void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data); + void ExtendMaxStreams(bool bidi, uint64_t max_streams); + inline void ExtendMaxStreamsUni(uint64_t max_streams); + inline void ExtendMaxStreamsBidi(uint64_t max_streams); + inline void ExtendMaxStreamsRemoteUni(uint64_t max_streams); + inline void ExtendMaxStreamsRemoteBidi(uint64_t max_streams); + bool GetNewConnectionID(ngtcp2_cid* cid, uint8_t* token, size_t cidlen); + inline void GetConnectionCloseInfo(); + inline void HandshakeCompleted(); + inline void HandshakeConfirmed(); + void PathValidation( + const ngtcp2_path* path, + ngtcp2_path_validation_result res); + bool ReceiveClientInitial(const QuicCID& dcid); + bool ReceivePacket(ngtcp2_path* path, const uint8_t* data, ssize_t nread); + bool ReceiveRetry(); + inline void RemoveConnectionID(const QuicCID& cid); + void ScheduleRetransmit(); + bool SendPacket(std::unique_ptr packet); + inline void set_local_address(const ngtcp2_addr* addr); + void StreamClose(int64_t stream_id, uint64_t app_error_code); + void StreamOpen(int64_t stream_id); + void StreamReset( + int64_t stream_id, + uint64_t final_size, + uint64_t app_error_code); + bool WritePackets(const char* diagnostic_label = nullptr); + void UpdateRecoveryStats(); + void UpdateConnectionID( + int type, + const QuicCID& cid, + const StatelessResetToken& token); + void UpdateDataStats(); + inline void UpdateEndpoint(const ngtcp2_path& path); + + inline void VersionNegotiation(const uint32_t* sv, size_t nsv); + + // static ngtcp2 callbacks + static int OnClientInitial( + ngtcp2_conn* conn, + void* user_data); + static int OnReceiveClientInitial( + ngtcp2_conn* conn, + const ngtcp2_cid* dcid, + void* user_data); + 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, + int64_t stream_id, + int fin, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data, + void* stream_user_data); + static int OnReceiveRetry( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const ngtcp2_pkt_retry* retry, + void* user_data); + static int OnAckedCryptoOffset( + ngtcp2_conn* conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + size_t datalen, + void* user_data); + static int OnAckedStreamDataOffset( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t offset, + size_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( + ngtcp2_conn* conn, + uint8_t* dest, + size_t destlen, + ngtcp2_rand_ctx ctx, + void* user_data); + 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, const void* data, size_t len); + + void UpdateIdleTimer(); + inline void UpdateRetransmitTimer(uint64_t timeout); + inline void StopRetransmitTimer(); + inline void StopIdleTimer(); + bool StartClosingPeriod(); + + enum QuicSessionFlags : uint32_t { + // Initial state when a QuicSession is created but nothing yet done. + QUICSESSION_FLAG_INITIAL = 0x1, + + // Set while the QuicSession is in the process of an Immediate + // or silent close. + QUICSESSION_FLAG_CLOSING = 0x2, + + // Set while the QuicSession is in the process of a graceful close. + QUICSESSION_FLAG_GRACEFUL_CLOSING = 0x4, + + // Set when the QuicSession has been destroyed (but not + // yet freed) + QUICSESSION_FLAG_DESTROYED = 0x8, + + QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS = 0x10, + + // Set while the QuicSession is executing an ngtcp2 callback + QUICSESSION_FLAG_NGTCP2_CALLBACK = 0x100, + + // Set if the QuicSession is in the middle of a silent close + // (that is, a CONNECTION_CLOSE should not be sent) + QUICSESSION_FLAG_SILENT_CLOSE = 0x200, + + QUICSESSION_FLAG_HANDSHAKE_RX = 0x400, + QUICSESSION_FLAG_HANDSHAKE_TX = 0x800, + QUICSESSION_FLAG_HANDSHAKE_KEYS = + QUICSESSION_FLAG_HANDSHAKE_RX | + QUICSESSION_FLAG_HANDSHAKE_TX, + QUICSESSION_FLAG_SESSION_RX = 0x1000, + QUICSESSION_FLAG_SESSION_TX = 0x2000, + QUICSESSION_FLAG_SESSION_KEYS = + QUICSESSION_FLAG_SESSION_RX | + QUICSESSION_FLAG_SESSION_TX, + + // Set if the QuicSession was closed due to stateless reset + QUICSESSION_FLAG_STATELESS_RESET = 0x4000 + }; + + void set_flag(QuicSessionFlags flag, bool on = true) { + if (on) + flags_ |= flag; + else + flags_ &= ~flag; + } + + bool is_flag_set(QuicSessionFlags flag) const { + return (flags_ & flag) == flag; + } + + void IncrementConnectionCloseAttempts() { + if (connection_close_attempts_ < kMaxSizeT) + connection_close_attempts_++; + } + + bool 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; + } + + 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); + + static inline ngtcp2_close_fn SelectCloseFn(uint32_t family) { + return family == QUIC_ERROR_APPLICATION ? + ngtcp2_conn_write_application_close : + ngtcp2_conn_write_connection_close; + } + + // Select the QUIC Application based on the configured ALPN identifier + QuicApplication* SelectApplication(QuicSession* session); + + ngtcp2_mem alloc_info_; + std::unique_ptr crypto_context_; + std::unique_ptr application_; + BaseObjectWeakPtr 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_; + + TimerPointer idle_; + TimerPointer retransmit_; + + QuicCID scid_; + QuicCID rcid_; + QuicCID pscid_; + ngtcp2_transport_params transport_params_; + + std::unique_ptr conn_closebuf_; + + StreamsMap streams_; + + AliasedFloat64Array state_; + + struct RemoteTransportParamsDebug { + QuicSession* session; + explicit RemoteTransportParamsDebug(QuicSession* session_) + : session(session_) {} + std::string ToString() const; + }; + + static const ngtcp2_conn_callbacks callbacks[2]; + + friend class QuicCryptoContext; + friend class QuicSessionListener; + friend class JSQuicSessionListener; +}; + +} // 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 new file mode 100644 index 00000000000000..64ffeebc48148d --- /dev/null +++ b/src/quic/node_quic_socket-inl.h @@ -0,0 +1,227 @@ +#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.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::Create( + const char* diagnostic_label, + size_t len) { + return std::make_unique(diagnostic_label, len); +} + +std::unique_ptr QuicPacket::Copy( + const std::unique_ptr& other) { + return std::make_unique(*other.get()); +} + +void QuicPacket::set_length(size_t len) { + CHECK_LE(len, data_.size()); + data_.resize(len); +} + +int QuicEndpoint::Send( + uv_buf_t* buf, + size_t len, + const sockaddr* addr) { + int ret = static_cast(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 session) { + Debug(this, "Associating stateless reset token %s", token); + token_map_[token] = session; +} + +void QuicSocket::DisassociateStatelessResetToken( + const StatelessResetToken& token) { + Debug(this, "Removing stateless reset token %s", token); + token_map_.erase(token); +} + +// StopListening is called when the QuicSocket is no longer +// accepting new server connections. Typically, this is called +// when the QuicSocket enters a graceful closing state where +// existing sessions are allowed to close naturally but new +// sessions are rejected. +void QuicSocket::StopListening() { + if (is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) { + Debug(this, "Stop listening"); + set_flag(QUICSOCKET_FLAGS_SERVER_LISTENING, false); + // It is important to not call ReceiveStop here as there + // is ongoing traffic being exchanged by the peers. + } +} + +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) { + reset_counts_[addr]++; +} + +void QuicSocket::IncrementSocketAddressCounter(const SocketAddress& addr) { + addr_counts_[addr]++; +} + +void QuicSocket::DecrementSocketAddressCounter(const SocketAddress& addr) { + auto it = addr_counts_.find(addr); + if (it == std::end(addr_counts_)) + return; + it->second--; + // Remove the address if the counter reaches zero again. + if (it->second == 0) { + addr_counts_.erase(addr); + reset_counts_.erase(addr); + } +} + +size_t QuicSocket::GetCurrentSocketAddressCounter(const SocketAddress& addr) { + auto it = addr_counts_.find(addr); + return it == std::end(addr_counts_) ? 0 : it->second; +} + +size_t QuicSocket::GetCurrentStatelessResetCounter(const SocketAddress& addr) { + auto it = reset_counts_.find(addr); + return it == std::end(reset_counts_) ? 0 : it->second; +} + +void QuicSocket::set_server_busy(bool on) { + Debug(this, "Turning Server Busy Response %s", on ? "on" : "off"); + set_flag(QUICSOCKET_FLAGS_SERVER_BUSY, on); + listener_->OnServerBusy(on); +} + +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(c) / 255) < prob; +} + +void QuicSocket::set_diagnostic_packet_loss(double rx, double tx) { + rx_loss_ = rx; + tx_loss_ = tx; +} + +bool QuicSocket::ToggleStatelessReset() { + set_flag( + QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET, + !is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET)); + return !is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); +} + +void QuicSocket::set_validated_address(const SocketAddress& addr) { + if (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU)) { + // Remove the oldest item if we've hit the LRU limit + validated_addrs_.push_back(SocketAddress::Hash()(addr)); + if (validated_addrs_.size() > kMaxValidateAddressLru) + validated_addrs_.pop_front(); + } +} + +bool QuicSocket::is_validated_address(const SocketAddress& addr) const { + if (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU)) { + auto res = std::find(std::begin(validated_addrs_), + std::end(validated_addrs_), + SocketAddress::Hash()(addr)); + return res != std::end(validated_addrs_); + } + return false; +} + +void QuicSocket::AddSession( + const QuicCID& cid, + BaseObjectPtr session) { + sessions_[cid] = session; + IncrementSocketAddressCounter(session->remote_address()); + IncrementStat( + session->is_server() ? + &QuicSocketStats::server_sessions : + &QuicSocketStats::client_sessions); +} + +void QuicSocket::AddEndpoint( + BaseObjectPtr endpoint_, + bool preferred) { + Debug(this, "Adding %sendpoint", preferred ? "preferred " : ""); + if (preferred || endpoints_.empty()) + preferred_endpoint_ = endpoint_; + endpoints_.emplace_back(endpoint_); + if (is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) + endpoint_->ReceiveStart(); +} + +void QuicSocket::SessionReady(BaseObjectPtr session) { + listener_->OnSessionReady(session); +} + +} // 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 new file mode 100644 index 00000000000000..157dee2e8c49c0 --- /dev/null +++ b/src/quic/node_quic_socket.cc @@ -0,0 +1,1178 @@ +#include "node_quic_socket-inl.h" // NOLINT(build/include) +#include "async_wrap-inl.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "memory_tracker-inl.h" +#include "nghttp2/nghttp2.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 + +namespace node { + +using crypto::EntropySource; +using crypto::SecureContext; + +using v8::ArrayBufferView; +using v8::Boolean; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::PropertyAttribute; +using v8::String; +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(&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_(len), + diagnostic_label_(diagnostic_label) { + CHECK_LE(len, NGTCP2_MAX_PKT_SIZE); +} + +QuicPacket::QuicPacket(const QuicPacket& other) : + QuicPacket(other.diagnostic_label_, other.data_.size()) { + memcpy(data_.data(), other.data_.data(), other.data_.size()); +} + +const char* QuicPacket::diagnostic_label() const { + return diagnostic_label_ != nullptr ? + diagnostic_label_ : "unspecified"; +} + +void QuicPacket::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("data", data_); +} + +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 session) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionReady(session); +} + +void QuicSocketListener::OnServerBusy(bool busy) { + if (previous_listener_ != nullptr) + previous_listener_->OnServerBusy(busy); +} + +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 arg = Number::New(env->isolate(), static_cast(code)); + socket()->MakeCallback(env->quic_on_socket_error_function(), 1, &arg); +} + +void JSQuicSocketListener::OnSessionReady(BaseObjectPtr session) { + Environment* env = socket()->env(); + Local arg = session->object(); + Context::Scope context_scope(env->context()); + socket()->MakeCallback(env->quic_on_session_ready_function(), 1, &arg); +} + +void JSQuicSocketListener::OnServerBusy(bool busy) { + Environment* env = socket()->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local arg = Boolean::New(env->isolate(), busy); + socket()->MakeCallback(env->quic_on_socket_server_busy_function(), 1, &arg); +} + +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( + Environment* env, + Local wrap, + QuicSocket* listener, + Local udp_wrap) : + BaseObject(env, wrap), + listener_(listener) { + MakeWeak(); + udp_ = static_cast( + udp_wrap->GetAlignedPointerFromInternalField( + UDPWrapBase::kUDPWrapBaseField)); + CHECK_NOT_NULL(udp_); + udp_->set_listener(this); + strong_ptr_.reset(udp_->GetAsyncWrap()); +} + +void QuicEndpoint::MemoryInfo(MemoryTracker* tracker) const {} + +uv_buf_t QuicEndpoint::OnAlloc(size_t suggested_size) { + return env()->AllocateManaged(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* QuicEndpoint::CreateSendWrap(size_t msg_size) { + return listener_->OnCreateSendWrap(msg_size); +} + +void QuicEndpoint::OnSendDone(ReqWrap* wrap, int status) { + DecrementPendingCallbacks(); + listener_->OnSendDone(wrap, status); + if (!has_pending_callbacks() && waiting_for_callbacks_) + listener_->OnEndpointDone(this); +} + +void QuicEndpoint::OnAfterBind() { + listener_->OnBind(this); +} + +QuicSocket::QuicSocket( + Environment* env, + Local 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(env, wrap, AsyncWrap::PROVIDER_QUICSOCKET), + StatsBase(env, wrap), + alloc_info_(MakeAllocator()), + options_(options), + 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_(NGTCP2_ALPN_H3) { + MakeWeak(); + PushListener(&default_listener_); + + Debug(this, "New QuicSocket created"); + + EntropySource(token_secret_, kTokenSecretLen); + + if (disable_stateless_reset) + set_flag(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); + + // 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_); + + DebugStats(); +} + +template +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 +} + +void QuicSocket::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("endpoints", endpoints_); + tracker->TrackField("sessions", sessions_); + tracker->TrackField("dcid_to_scid", dcid_to_scid_); + tracker->TrackField("addr_counts", addr_counts_); + tracker->TrackField("reset_counts", reset_counts_); + tracker->TrackField("token_map", token_map_); + tracker->TrackField("validated_addrs", validated_addrs_); + StatsBase::StatsMemoryInfo(tracker); + tracker->TrackFieldWithSize( + "current_ngtcp2_memory", + current_ngtcp2_memory_); +} + +void QuicSocket::Listen( + BaseObjectPtr sc, + const sockaddr* preferred_address, + const std::string& alpn, + uint32_t options) { + CHECK(sc); + CHECK(!server_secure_context_); + CHECK(!is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)); + Debug(this, "Starting to listen"); + server_session_config_.Set(env(), preferred_address); + server_secure_context_ = sc; + server_alpn_ = alpn; + server_options_ = options; + set_flag(QUICSOCKET_FLAGS_SERVER_LISTENING); + RecordTimestamp(&QuicSocketStats::listen_at); + ReceiveStart(); +} + +void QuicSocket::OnError(QuicEndpoint* endpoint, ssize_t error) { + Debug(this, "Reading data from UDP socket failed. Error %" PRId64, error); + listener_->OnError(error); +} + +ReqWrap* QuicSocket::OnCreateSendWrap(size_t msg_size) { + HandleScope handle_scope(env()->isolate()); + Local obj; + if (!env()->quicsocketsendwrap_instance_template() + ->NewInstance(env()->context()).ToLocal(&obj)) return nullptr; + return last_created_send_wrap_ = new SendWrap(env(), obj, msg_size); +} + +void QuicSocket::OnEndpointDone(QuicEndpoint* endpoint) { + Debug(this, "Endpoint has no pending callbacks"); + listener_->OnEndpointDone(endpoint); +} + +void QuicSocket::OnBind(QuicEndpoint* endpoint) { + const SocketAddress& local_address = endpoint->local_address(); + bound_endpoints_[local_address] = + BaseObjectWeakPtr(endpoint); + Debug(this, "Endpoint %s bound", local_address); + RecordTimestamp(&QuicSocketStats::bound_at); +} + +BaseObjectPtr QuicSocket::FindSession(const QuicCID& cid) { + BaseObjectPtr 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(is_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; + } + + IncrementStat(&QuicSocketStats::bytes_received, nread); + + const uint8_t* data = reinterpret_cast(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); + + // TODO(@jasnell): It would be fantastic if Debug() could be + // modified to accept objects with a ToString-like capability + // similar to what we can do with TraceEvents... that would + // allow us to pass the QuicCID directly to Debug and have it + // converted to hex only if the category is enabled so we can + // skip committing resources here. + std::string dcid_hex = dcid.ToString(); + Debug(this, "Received a QUIC packet for dcid %s", dcid_hex.c_str()); + + BaseObjectPtr 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_hex.c_str()); + 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 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(is_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(token.data()), + random, + kRandlen); + if (nwrite < static_cast(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. +// +// TODO(@jasnell): 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 should 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) { + std::unique_ptr 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. +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 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 (!is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) { + Debug(this, "QuicSocket is not listening"); + return {}; + } + + switch (ngtcp2_accept(&hd, data, static_cast(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(is_flag_set(QUICSOCKET_FLAGS_SERVER_BUSY)) || + 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_SERVER_BUSY); + 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 (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS) || + hd.tokenlen > 0) { + Debug(this, "Performing explicit address validation"); + if (hd.tokenlen == 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, + 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 session = + QuicSession::CreateServer( + this, + server_session_config_, + dcid, + local_addr, + remote_addr, + scid, + ocid, + version, + server_alpn_, + server_options_, + qlog_); + CHECK(session); + + listener_->OnSessionReady(session); + + return session; +} + +QuicSocket::SendWrap::SendWrap( + Environment* env, + Local req_wrap_obj, + size_t total_length) + : ReqWrap(env, req_wrap_obj, PROVIDER_QUICSOCKET), + total_length_(total_length) { +} + +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 packet, + BaseObjectPtr 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* wrap, int status) { + std::unique_ptr req_wrap(static_cast(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; +} + +// JavaScript API +namespace { +void NewQuicEndpoint(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As()); + CHECK(args[1]->IsObject()); + CHECK_GE(args[1].As()->InternalFieldCount(), + UDPWrapBase::kUDPWrapBaseField); + new QuicEndpoint(env, args.This(), socket, args[1].As()); +} + +void NewQuicSocket(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + 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 buf(args[6].As()); + CHECK_EQ(buf.length(), kTokenSecretLen); + session_reset_secret = buf.data(); + } + + new QuicSocket( + env, + 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& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + CHECK(args[0]->IsObject()); + QuicEndpoint* endpoint; + ASSIGN_OR_RETURN_UNWRAP(&endpoint, args[0].As()); + socket->AddEndpoint( + BaseObjectPtr(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& 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& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + socket->ReceiveStop(); +} + +void QuicSocketListen(const FunctionCallbackInfo& 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(), + 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(&preferred_address_storage); + } + } + + std::string alpn(NGTCP2_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(sc), + preferred_address, + alpn, + options); +} + +void QuicSocketStopListening(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + socket->StopListening(); +} + +void QuicSocketset_server_busy(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + CHECK_EQ(args.Length(), 1); + socket->set_server_busy(args[0]->IsTrue()); +} + +void QuicSocketToggleStatelessReset(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + args.GetReturnValue().Set(socket->ToggleStatelessReset()); +} + +void QuicEndpointWaitForPendingCallbacks( + const FunctionCallbackInfo& args) { + QuicEndpoint* endpoint; + ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.Holder()); + endpoint->WaitForPendingCallbacks(); +} + +} // namespace + +void QuicEndpoint::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicEndpoint"); + Local endpoint = env->NewFunctionTemplate(NewQuicEndpoint); + endpoint->SetClassName(class_name); + endpoint->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(endpoint, + "waitForPendingCallbacks", + QuicEndpointWaitForPendingCallbacks); + endpoint->InstanceTemplate()->Set(env->owner_symbol(), Null(isolate)); + + target->Set( + context, + class_name, + endpoint->GetFunction(context).ToLocalChecked()) + .FromJust(); +} + +void QuicSocket::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicSocket"); + Local socket = env->NewFunctionTemplate(NewQuicSocket); + socket->SetClassName(class_name); + socket->InstanceTemplate()->SetInternalFieldCount(1); + 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); + env->SetProtoMethod(socket, + "setServerBusy", + QuicSocketset_server_busy); + env->SetProtoMethod(socket, + "stopListening", + QuicSocketStopListening); + env->SetProtoMethod(socket, + "toggleStatelessReset", + QuicSocketToggleStatelessReset); + socket->Inherit(HandleWrap::GetConstructorTemplate(env)); + target->Set(context, class_name, + socket->GetFunction(env->context()).ToLocalChecked()).FromJust(); + + // TODO(addaleax): None of these templates actually are constructor templates. + Local sendwrap_template = ObjectTemplate::New(isolate); + sendwrap_template->SetInternalFieldCount(1); + 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 new file mode 100644 index 00000000000000..bb54187b050ca0 --- /dev/null +++ b/src/quic/node_quic_socket.h @@ -0,0 +1,577 @@ +#ifndef SRC_QUIC_NODE_QUIC_SOCKET_H_ +#define SRC_QUIC_NODE_QUIC_SOCKET_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object.h" +#include "node.h" +#include "node_crypto.h" +#include "node_internals.h" +#include "ngtcp2/ngtcp2.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 +#include +#include +#include + +namespace node { + +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::Object; +using v8::Value; + +namespace quic { + +enum QuicSocketOptions : uint32_t { + // When enabled the QuicSocket will validate the address + // using a RETRY packet to the peer. + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS = 0x1, + + // When enabled, and the VALIDATE_ADDRESS option is also + // set, the QuicSocket will use an LRU cache to track + // validated addresses. Address validation will be skipped + // if the address is currently in the cache. + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU = 0x2, +}; + +#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(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 + static void ToString(const Base& ptr, Fn&& add_field); +}; + +class QuicSocket; +class QuicEndpoint; + +// 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 session); + virtual void OnServerBusy(bool busy); + virtual void OnEndpointDone(QuicEndpoint* endpoint); + virtual void OnDestroy(); + + QuicSocket* socket() { return socket_.get(); } + + private: + BaseObjectWeakPtr socket_; + QuicSocketListener* previous_listener_ = nullptr; + friend class QuicSocket; +}; + +class JSQuicSocketListener : public QuicSocketListener { + public: + void OnError(ssize_t code) override; + void OnSessionReady(BaseObjectPtr session) override; + void OnServerBusy(bool busy) override; + void OnEndpointDone(QuicEndpoint* endpoint) override; + void OnDestroy() override; +}; + +// A serialized QuicPacket to be sent by a QuicSocket instance. +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. Generally speaking, a QUIC packet should never + // be larger than the current MTU to avoid IP fragmentation. + // + // The content of a QuicPacket is provided by ngtcp2. 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 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 Create( + const char* diagnostic_label = nullptr, + size_t len = NGTCP2_MAX_PKTLEN_IPV4); + + // 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 Copy( + const std::unique_ptr& other); + + QuicPacket(const char* diagnostic_label, size_t len); + QuicPacket(const QuicPacket& other); + uint8_t* data() { return data_.data(); } + size_t length() const { return data_.size(); } + uv_buf_t buf() const { + return uv_buf_init( + const_cast(reinterpret_cast(data_.data())), + length()); + } + inline void set_length(size_t len); + const char* diagnostic_label() const; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicPacket); + SET_SELF_SIZE(QuicPacket); + + private: + std::vector data_; + 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* OnCreateSendWrap(size_t msg_size) = 0; + virtual void OnSendDone(ReqWrap* 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 : public BaseObject, + public UDPListener { + public: + static void Initialize( + Environment* env, + Local target, + Local context); + + QuicEndpoint( + Environment* env, + Local wrap, + QuicSocket* listener, + Local udp_wrap); + + const SocketAddress& local_address() const { + local_address_ = udp_->GetSockName(); + return local_address_; + } + + // 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* CreateSendWrap(size_t msg_size) override; + + void OnSendDone(ReqWrap* 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() { return pending_callbacks_ > 0; } + inline void WaitForPendingCallbacks(); + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicEndpoint) + SET_SELF_SIZE(QuicEndpoint) + + private: + mutable SocketAddress local_address_; + BaseObjectWeakPtr listener_; + UDPWrapBase* udp_; + BaseObjectPtr strong_ptr_; + size_t pending_callbacks_ = 0; + bool waiting_for_callbacks_ = false; +}; + +// 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, + public StatsBase { + public: + static void Initialize( + Environment* env, + Local target, + Local context); + + QuicSocket( + Environment* env, + Local 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. + const SocketAddress& local_address() { + CHECK(preferred_endpoint_); + return preferred_endpoint_->local_address(); + } + + void MaybeClose(); + + inline void AddSession( + const QuicCID& cid, + BaseObjectPtr session); + inline void AssociateCID( + const QuicCID& cid, + const QuicCID& scid); + inline void DisassociateCID( + const QuicCID& cid); + inline void AssociateStatelessResetToken( + const StatelessResetToken& token, + BaseObjectPtr session); + inline void DisassociateStatelessResetToken( + const StatelessResetToken& token); + void Listen( + BaseObjectPtr context, + const sockaddr* preferred_address = nullptr, + const std::string& alpn = NGTCP2_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 packet, + BaseObjectPtr session = BaseObjectPtr()); + inline void SessionReady(BaseObjectPtr session); + inline void set_server_busy(bool on); + inline void set_diagnostic_packet_loss(double rx = 0.0, double tx = 0.0); + inline void StopListening(); + + // Toggles whether or not stateless reset is enabled or not. + // Returns true if stateless reset is enabled, false if it + // is not. + inline bool ToggleStatelessReset(); + + BaseObjectPtr server_secure_context() const { + return server_secure_context_; + } + + 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* OnCreateSendWrap(size_t msg_size) override; + void OnSendDone(ReqWrap* wrap, int status) override; + void OnBind(QuicEndpoint* endpoint) override; + void OnReceive( + ssize_t nread, + AllocatedBuffer buf, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) override; + void OnError(QuicEndpoint* endpoint, ssize_t error) override; + 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 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 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 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; + + bool is_stateless_reset_disabled() { + return is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); + } + + enum QuicSocketFlags : uint32_t { + QUICSOCKET_FLAGS_NONE = 0x0, + + // Indicates that the QuicSocket has entered a graceful + // closing phase, indicating that no additional + QUICSOCKET_FLAGS_GRACEFUL_CLOSE = 0x1, + QUICSOCKET_FLAGS_WAITING_FOR_CALLBACKS = 0x2, + QUICSOCKET_FLAGS_SERVER_LISTENING = 0x4, + QUICSOCKET_FLAGS_SERVER_BUSY = 0x8, + QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET = 0x10 + }; + + void set_flag(QuicSocketFlags flag, bool on = true) { + if (on) + flags_ |= flag; + else + flags_ &= ~flag; + } + + bool is_flag_set(QuicSocketFlags flag) const { + return flags_ & flag; + } + + void set_option(QuicSocketOptions option, bool on = true) { + if (on) + options_ |= option; + else + options_ &= ~option; + } + + bool is_option_set(QuicSocketOptions option) const { + return options_ & option; + } + + ngtcp2_mem alloc_info_; + + std::vector> endpoints_; + SocketAddress::Map> bound_endpoints_; + BaseObjectWeakPtr preferred_endpoint_; + + uint32_t flags_ = QUICSOCKET_FLAGS_NONE; + uint32_t options_; + uint32_t server_options_; + + 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 server_secure_context_; + std::string server_alpn_; + QuicCID::Map> sessions_; + QuicCID::Map dcid_to_scid_; + + uint8_t token_secret_[kTokenSecretLen]; + uint8_t reset_token_secret_[NGTCP2_STATELESS_RESET_TOKENLEN]; + + // Counts the number of active connections per remote + // address. A custom std::hash specialization for + // sockaddr instances is used. Values are incremented + // when a QuicSession is added to the socket, and + // decremented when the QuicSession is removed. If the + // value reaches the value of max_connections_per_host_, + // attempts to create new connections will be ignored + // until the value falls back below the limit. + SocketAddress::Map addr_counts_; + + // Counts the number of stateless resets sent per + // remote address. + // TODO(@jasnell): this counter persists through the + // lifetime of the QuicSocket, and therefore can become + // a possible risk. Specifically, a malicious peer could + // attempt the local peer to count an increasingly large + // number of remote addresses. Need to mitigate the + // potential risk. + SocketAddress::Map reset_counts_; + + // Counts the number of retry attempts sent per + // remote address. + + StatelessResetToken::Map token_map_; + + // The validated_addrs_ vector is used as an LRU cache for + // validated addresses only when the VALIDATE_ADDRESS_LRU + // option is set. + typedef size_t SocketAddressHash; + std::deque validated_addrs_; + + class SendWrap : public ReqWrap { + public: + SendWrap(Environment* env, + v8::Local req_wrap_obj, + size_t total_length_); + + void set_packet(std::unique_ptr packet) { + packet_ = std::move(packet); + } + QuicPacket* packet() { return packet_.get(); } + void set_session(BaseObjectPtr session) { session_ = session; } + size_t total_length() const { return total_length_; } + + SET_SELF_SIZE(SendWrap); + std::string MemoryInfoName() const override; + void MemoryInfo(MemoryTracker* tracker) const override; + + private: + BaseObjectPtr session_; + std::unique_ptr packet_; + size_t total_length_; + }; + + SendWrap* last_created_send_wrap_ = nullptr; + + 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 new file mode 100644 index 00000000000000..a2097f0d55f0a5 --- /dev/null +++ b/src/quic/node_quic_state.h @@ -0,0 +1,72 @@ +#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 { + +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_PACKET_SIZE, + IDX_QUIC_SESSION_ACK_DELAY_EXPONENT, + IDX_QUIC_SESSION_DISABLE_MIGRATION, + IDX_QUIC_SESSION_MAX_ACK_DELAY, + 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: + explicit QuicState(v8::Isolate* isolate) : + root_buffer( + isolate, + sizeof(quic_state_internal)), + quicsessionconfig_buffer( + isolate, + offsetof(quic_state_internal, quicsessionconfig_buffer), + IDX_QUIC_SESSION_CONFIG_COUNT + 1, + root_buffer), + http3config_buffer( + 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; + + 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 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 new file mode 100644 index 00000000000000..3da0f5fb3b57cf --- /dev/null +++ b/src/quic/node_quic_stream-inl.h @@ -0,0 +1,158 @@ +#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; +} + +bool QuicStream::is_flag_set(int32_t flag) const { + return flags_ & (1 << flag); +} + +void QuicStream::set_flag(int32_t flag, bool on) { + if (on) + flags_ |= (1 << flag); + else + flags_ &= ~(1 << flag); +} + +void QuicStream::set_final_size(uint64_t final_size) { + CHECK_EQ(GetStat(&QuicStreamStats::final_size), 0); + SetStat(&QuicStreamStats::final_size, final_size); +} + +bool QuicStream::is_destroyed() const { + return is_flag_set(QUICSTREAM_FLAG_DESTROYED); +} + +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; +} + +bool QuicStream::is_readable() const { + return was_ever_readable() && !is_flag_set(QUICSTREAM_FLAG_READ_CLOSED); +} + +void QuicStream::set_fin_sent() { + CHECK(!is_writable()); + set_flag(QUICSTREAM_FLAG_FIN_SENT); +} + +bool QuicStream::is_write_finished() const { + return is_flag_set(QUICSTREAM_FLAG_FIN_SENT) && + streambuf_.length() == 0; +} + +bool QuicStream::SubmitInformation(v8::Local headers) { + return session_->SubmitInformation(stream_id_, headers); +} + +bool QuicStream::SubmitHeaders(v8::Local headers, uint32_t flags) { + return session_->SubmitHeaders(stream_id_, headers, flags); +} + +bool QuicStream::SubmitTrailers(v8::Local headers) { + return session_->SubmitTrailers(stream_id_, headers); +} + +BaseObjectPtr QuicStream::SubmitPush( + v8::Local 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); +} + +void QuicStream::ResetStream(uint64_t app_error_code) { + // On calling shutdown, the stream will no longer be + // readable or writable, all any pending data in the + // streambuf_ will be canceled, and all data pending + // to be acknowledged at the ngtcp2 level will be + // abandoned. + BaseObjectPtr ptr(session_); + set_flag(QUICSTREAM_FLAG_READ_CLOSED); + session_->ResetStream(stream_id_, app_error_code); + streambuf_.Cancel(); + streambuf_.End(); +} + +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 new file mode 100644 index 00000000000000..c4f3cdb3991e7c --- /dev/null +++ b/src/quic/node_quic_stream.cc @@ -0,0 +1,513 @@ +#include "node_quic_stream-inl.h" // NOLINT(build/include) +#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 +#include +#include +#include + +namespace node { + +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::String; +using v8::Value; + +namespace quic { + +QuicStream::QuicStream( + QuicSession* sess, + Local 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) { + CHECK_NOT_NULL(sess); + Debug(this, "Created"); + StreamBase::AttachToObject(GetObject()); + 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 +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 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); + 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(get_async_id())) + + ", " + session_->diagnostic_name() + ")"; +} + +void QuicStream::Destroy() { + if (is_destroyed()) + return; + set_flag(QUICSTREAM_FLAG_DESTROYED); + set_flag(QUICSTREAM_FLAG_READ_CLOSED); + streambuf_.End(); + + // 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_.Cancel(); + CHECK_EQ(streambuf_.length(), 0); + + // The QuicSession maintains a map of std::unique_ptrs to + // QuicStream instances. Removing this here will cause + // this QuicStream object to be deconstructed, so the + // QuicStream object will no longer exist after this point. + 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. Any time after this is +// called, a final stream frame will be sent for this QuicStream, +// but it may not be sent right away. +int QuicStream::DoShutdown(ShutdownWrap* req_wrap) { + if (is_destroyed()) + return UV_EPIPE; + + QuicSession::SendSessionScope send_scope(session(), true); + + if (is_writable()) { + Debug(this, "Shutdown writable side"); + RecordTimestamp(&QuicStreamStats::closing_at); + streambuf_.End(); + session()->ResumeStream(stream_id_); + } + + return 1; +} + +int QuicStream::DoWrite( + WriteWrap* req_wrap, + uv_buf_t* bufs, + size_t nbufs, + uv_stream_t* send_handle) { + CHECK_NULL(send_handle); + + // 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(), true); + + Debug(this, "Queuing %" PRIu64 " bytes of data from %d buffers", + length, nbufs); + IncrementStat(&QuicStreamStats::bytes_sent, static_cast(length)); + + BaseObjectPtr 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); + }); + + 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()); + set_flag(QUICSTREAM_FLAG_READ_STARTED); + set_flag(QUICSTREAM_FLAG_READ_PAUSED, false); + 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()); + set_flag(QUICSTREAM_FLAG_READ_PAUSED); + return 0; +} + +void QuicStream::IncrementStats(size_t datalen) { + uint64_t len = static_cast(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::New( + QuicSession* session, + int64_t stream_id, + int64_t push_id) { + Local obj; + if (!session->env() + ->quicserverstream_instance_template() + ->NewInstance(session->env()->context()).ToLocal(&obj)) { + return {}; + } + BaseObjectPtr stream = + MakeDetachedBaseObject( + 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( + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + CHECK(!is_destroyed()); + Debug(this, "Receiving %d bytes. Final? %s. Readable? %s", + datalen, + 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 || fin == 1); + + // 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(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(const_cast(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 = is_flag_set(QUICSTREAM_FLAG_READ_PAUSED); + 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 (fin) { + set_flag(QUICSTREAM_FLAG_FIN); + set_final_size(offset + datalen); + EmitRead(UV_EOF); + } +} + +int QuicStream::DoPull( + bob::Next 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& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + args.GetReturnValue().Set(static_cast(stream->id())); +} + +void OpenUnidirectionalStream(const FunctionCallbackInfo& args) { + CHECK(!args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + + int64_t stream_id; + if (!session->OpenUnidirectionalStream(&stream_id)) + return; + + BaseObjectPtr stream = QuicStream::New(session, stream_id); + args.GetReturnValue().Set(stream->object()); +} + +void OpenBidirectionalStream(const FunctionCallbackInfo& args) { + CHECK(!args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + + int64_t stream_id; + if (!session->OpenBidirectionalStream(&stream_id)) + return; + + BaseObjectPtr stream = QuicStream::New(session, stream_id); + args.GetReturnValue().Set(stream->object()); +} + +void QuicStreamDestroy(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + stream->Destroy(); +} + +void QuicStreamReset(const FunctionCallbackInfo& 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(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& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + args.GetReturnValue().Set(stream->SubmitInformation(args[0].As())); +} + +// 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& 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(), 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& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + args.GetReturnValue().Set(stream->SubmitTrailers(args[0].As())); +} + +// 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& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + BaseObjectPtr push_stream = + stream->SubmitPush(args[0].As()); + if (push_stream) + args.GetReturnValue().Set(push_stream->object()); +} + +} // namespace + +void QuicStream::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicStream"); + Local stream = FunctionTemplate::New(env->isolate()); + stream->SetClassName(class_name); + stream->Inherit(AsyncWrap::GetConstructorTemplate(env)); + StreamBase::AddMethods(env, stream); + Local 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, "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); + target->Set(env->context(), + class_name, + stream->GetFunction(env->context()).ToLocalChecked()).FromJust(); + + 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 new file mode 100644 index 00000000000000..14c4c3c1e56e48 --- /dev/null +++ b/src/quic/node_quic_stream.h @@ -0,0 +1,385 @@ +#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-inl.h" +#include "async_wrap.h" +#include "env.h" +#include "node_http_common.h" +#include "node_quic_util.h" +#include "stream_base-inl.h" +#include "util-inl.h" +#include "v8.h" + +#include +#include + +namespace node { +namespace quic { + +class QuicSession; +class QuicStream; +class QuicApplication; + +using QuicHeader = NgHeaderImpl; + +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(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 + static void ToString(const Base& ptr, Fn&& add_field); +}; + +enum QuicStreamStates : uint32_t { + // QuicStream is fully open. Readable and Writable + QUICSTREAM_FLAG_INITIAL = 0, + + // QuicStream Read State is closed because a final stream frame + // has been received from the peer or the QuicStream is unidirectional + // outbound only (i.e. it was never readable) + QUICSTREAM_FLAG_READ_CLOSED, + + // JavaScript side has switched into flowing mode (Readable side) + QUICSTREAM_FLAG_READ_STARTED, + + // JavaScript side has paused the flow of data (Readable side) + QUICSTREAM_FLAG_READ_PAUSED, + + // QuicStream has received a final stream frame (Readable side) + QUICSTREAM_FLAG_FIN, + + // QuicStream has sent a final stream frame (Writable side) + QUICSTREAM_FLAG_FIN_SENT, + + // QuicStream has been destroyed + QUICSTREAM_FLAG_DESTROYED +}; + +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, + public StreamBase, + public StatsBase { + public: + static void Initialize( + Environment* env, + v8::Local target, + v8::Local context); + + static BaseObjectPtr New( + QuicSession* session, + int64_t stream_id, + int64_t push_id = 0); + + QuicStream( + QuicSession* session, + v8::Local target, + int64_t stream_id, + int64_t push_id = 0); + + ~QuicStream() override; + + std::string diagnostic_name() const override; + + int64_t id() const { return stream_id_; } + 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; + + // The QuicStream has been destroyed and is no longer usable. + inline bool is_destroyed() const; + + // 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 QUICSTREAM_FLAG_READ_CLOSED flag is set or + // - It is a Unidirectional stream originating from the local peer. + inline bool is_readable() const; + + // Records the fact that a final stream frame has been + // serialized and sent to the peer. There still may be + // unacknowledged data in the outbound queue, but no + // additional frames may be sent for the stream other + // than reset stream. + inline void set_fin_sent(); + + // 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 + 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(); + + // 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 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( + int fin, + 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 = 0); + + // Submits informational headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitInformation(v8::Local headers); + + // Submits initial headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitHeaders(v8::Local headers, uint32_t flags); + + // Submits trailing headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitTrailers(v8::Local headers); + + inline BaseObjectPtr SubmitPush(v8::Local headers); + + // Required for StreamBase + bool IsAlive() override; + bool IsClosing() override; + int ReadStart() override; + int ReadStop() override; + int DoShutdown(ShutdownWrap* req_wrap) override; + + AsyncWrap* GetAsyncWrap() override { return this; } + + // Required for MemoryRetainer + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicStream) + SET_SELF_SIZE(QuicStream) + + protected: + int DoPull( + bob::Next next, + int options, + ngtcp2_vec* data, + size_t count, + size_t max_count_hint) override; + + private: + inline bool is_flag_set(int32_t flag) const; + inline void set_flag(int32_t flag, bool on = true); + + // 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 session_; + QuicBuffer streambuf_; + + int64_t stream_id_ = 0; + int64_t push_id_ = 0; + uint32_t flags_ = QUICSTREAM_FLAG_INITIAL; + size_t inbound_consumed_data_while_paused_ = 0; + + std::vector> headers_; + QuicStreamHeadersKind headers_kind_; + size_t current_headers_length_ = 0; + + ListNode stream_queue_; + public: + // Linked List of QuicStream objects + using Queue = ListHead; + 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 new file mode 100644 index 00000000000000..71b5ceab06fbc2 --- /dev/null +++ b/src/quic/node_quic_util-inl.h @@ -0,0 +1,460 @@ +#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 + +namespace node { + +namespace quic { + +QuicPath::QuicPath( + const SocketAddress& local, + const SocketAddress& remote) { + ngtcp2_addr_init( + &this->local, + local.data(), + local.length(), + const_cast(&local)); + ngtcp2_addr_init( + &this->remote, + remote.data(), + remote.length(), + const_cast(&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{}(token->data[n]) + 0x9e3779b9 + + (hash << 6) + (hash >> 2); + } + return hash; +} + +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 dest(ptr_->datalen * 2 + 1); + dest[dest.size() - 1] = '\0'; + size_t written = StringBytes::hex_encode( + reinterpret_cast(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; +} + +Timer::Timer(Environment* env, std::function fn) + : env_(env), + fn_(fn) { + uv_timer_init(env_->event_loop(), &timer_); + timer_.data = this; +} + +void Timer::Stop() { + if (stopped_) + return; + stopped_ = true; + + if (timer_.data == this) { + uv_timer_stop(&timer_); + timer_.data = nullptr; + } +} + +// If the timer is not currently active, interval must be either 0 or greater. +// If the timer is already active, interval is ignored. +void Timer::Update(uint64_t interval) { + if (stopped_) + return; + uv_timer_start(&timer_, OnTimeout, interval, interval); + uv_unref(reinterpret_cast(&timer_)); +} + +void Timer::Free(Timer* timer) { + timer->env_->CloseHandle( + reinterpret_cast(&timer->timer_), + [&](uv_handle_t* timer) { + Timer* t = ContainerOf( + &Timer::timer_, + reinterpret_cast(timer)); + delete t; + }); +} + +void Timer::OnTimeout(uv_timer_t* timer) { + Timer* t = ContainerOf(&Timer::timer_, timer); + t->fn_(); +} + +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 codeArg, + v8::Local familyArg, + int32_t family_) : + family(family_), + code(NGTCP2_NO_ERROR) { + if (codeArg->IsBigInt()) { + code = codeArg.As()->Int64Value(); + } else if (codeArg->IsNumber()) { + double num = 0; + CHECK(codeArg->NumberValue(env->context()).To(&num)); + code = static_cast(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* QuicPreferredAddress::cid() const { + return &paddr_->cid; +} + +const uint8_t* QuicPreferredAddress::stateless_reset_token() const { + return paddr_->stateless_reset_token; +} + +std::string QuicPreferredAddress::preferred_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 QuicPreferredAddress::preferred_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); +} + +int16_t QuicPreferredAddress::preferred_ipv6_port() const { + return paddr_->ipv6_port; +} +int16_t QuicPreferredAddress::preferred_ipv4_port() const { + return paddr_->ipv4_port; +} + +bool QuicPreferredAddress::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 QuicPreferredAddress::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) : token_(token) { + GenerateResetToken(token, secret, cid); +} + +StatelessResetToken::StatelessResetToken( + const uint8_t* secret, + const QuicCID& cid) + : token_(buf_) { + GenerateResetToken(buf_, secret, cid); +} + +std::string StatelessResetToken::ToString() const { + std::vector dest(NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1); + dest[dest.size() - 1] = '\0'; + size_t written = StringBytes::hex_encode( + reinterpret_cast(token_), + 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{}(token.token_[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 +StatsBase::StatsBase( + Environment* env, + v8::Local wrap, + int options) + : stats_buffer_( + env->isolate(), + sizeof(typename T::Stats) / sizeof(uint64_t), + reinterpret_cast(&stats_)) { + static constexpr uint64_t kMax = std::numeric_limits::max(); + stats_.created_at = uv_hrtime(); + + // TODO(@jasnell): The follow are checks instead of handling + // the error. Before this code moves out of experimental, + // these should be change to properly handle the error. + + wrap->DefineOwnProperty( + env->context(), + env->stats_string(), + stats_buffer_.GetJSArray(), + v8::PropertyAttribute::ReadOnly).Check(); + + 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 +void StatsBase::IncrementStat(uint64_t T::Stats::*member, uint64_t amount) { + static constexpr uint64_t kMax = std::numeric_limits::max(); + stats_.*member += std::min(amount, kMax - stats_.*member); +} + +template +void StatsBase::SetStat(uint64_t T::Stats::*member, uint64_t value) { + stats_.*member = value; +} + +template +void StatsBase::RecordTimestamp(uint64_t T::Stats::*member) { + stats_.*member = uv_hrtime(); +} + +template +uint64_t StatsBase::GetStat(uint64_t T::Stats::*member) const { + return stats_.*member; +} + +template +inline void StatsBase::RecordRate(uint64_t 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 +inline void StatsBase::RecordSize(uint64_t val) { + CHECK(size_); + size_->Record(val); +} + +template +inline void StatsBase::RecordAck(uint64_t 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 +void StatsBase::StatsMemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("stats_buffer", stats_buffer_); + tracker->TrackField("rate_histogram", rate_); + tracker->TrackField("size_histogram", size_); + tracker->TrackField("ack_histogram", ack_); +} + +template +void StatsBase::DebugStats() { + StatsDebug stats_debug(static_cast(this)); + Debug(static_cast(this), "Destroyed. %s", stats_debug); +} + +template +std::string StatsBase::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(&T::Stats::created_at)); + T::ToString(*ptr, add_field); + return out; +} + +template +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 new file mode 100644 index 00000000000000..54a5696605f631 --- /dev/null +++ b/src/quic/node_quic_util.h @@ -0,0 +1,392 @@ +#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 +#include + +#include +#include +#include +#include +#include + +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::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_MAX_CONNECTIONS = + std::min(kMaxSizeT, kMaxSafeJsInteger); +constexpr uint64_t DEFAULT_MAX_CONNECTIONS_PER_HOST = 100; +constexpr uint64_t NGTCP2_APP_NOERROR = 0xff00; +constexpr uint64_t MIN_RETRYTOKEN_EXPIRATION = 1; +constexpr uint64_t MAX_RETRYTOKEN_EXPIRATION = 60; +constexpr uint64_t DEFAULT_ACTIVE_CONNECTION_ID_LIMIT = 2; +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 = 10ULL; + +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. +// We currently only support two options in the Node.js implementation, +// but additional options may be added later. +enum SelectPreferredAddressPolicy : int { + // Ignore the server-provided preferred address + QUIC_PREFERRED_ADDRESS_IGNORE, + // Accept the server-provided preferred address + QUIC_PREFERRED_ADDRESS_ACCEPT +}; + +// 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 class StatsBase; + +template +struct StatsTraits { + using Stats = T; + using Base = Q; + + template + 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 +class StatsBase { + public: + // 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 ackwowledgement. 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 wrap, + int options = HistogramOptions::NONE); + + inline ~StatsBase() = default; + + // 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 T::Stats::*member, uint64_t amount = 1); + + // Sets an entirely new value for the given stat field + inline void SetStat(uint64_t T::Stats::*member, uint64_t value); + + // Sets the given stat field to the current uv_hrtime() + inline void RecordTimestamp(uint64_t T::Stats::*member); + + // Gets the current value of the given stat field + inline uint64_t GetStat(uint64_t 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 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 T::Stats::*member); + + inline void StatsMemoryInfo(MemoryTracker* tracker) const; + + inline void DebugStats(); + + private: + typename T::Stats stats_{}; + BaseObjectPtr rate_; + BaseObjectPtr size_; + BaseObjectPtr ack_; + AliasedBigUint64Array stats_buffer_; +}; + +// QuicPreferredAddress 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 QuicPreferredAddress { + public: + QuicPreferredAddress( + Environment* env, + ngtcp2_addr* dest, + const ngtcp2_preferred_addr* paddr) : + env_(env), + dest_(dest), + paddr_(paddr) {} + + inline const ngtcp2_cid* cid() const; + inline std::string preferred_ipv6_address() const; + inline std::string preferred_ipv4_address() const; + inline int16_t preferred_ipv6_port() const; + inline int16_t preferred_ipv4_port() const; + inline const uint8_t* stateless_reset_token() const; + + 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 codeArg, + v8::Local familyArg = v8::Local(), + 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 +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 std::string ToString() const; + + // Copy assignment + QuicCID& operator=(const QuicCID& cid) { + if (this == &cid) return *this; + this->~QuicCID(); + return *new(this) QuicCID(std::move(cid)); + } + + const ngtcp2_cid& operator*() const { return *ptr_; } + const ngtcp2_cid* operator->() const { return ptr_; } + 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(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 + using Map = std::unordered_map; + + private: + ngtcp2_cid cid_{}; + const ngtcp2_cid* ptr_; +}; + +// Simple timer wrapper that is used to implement the internals +// for idle and retransmission timeouts. Call Update to start or +// reset the timer; Stop to halt the timer. +class Timer final : public MemoryRetainer { + public: + inline explicit Timer(Environment* env, std::function fn); + + // Stops the timer with the side effect of the timer no longer being usable. + // It will be cleaned up and the Timer object will be destroyed. + inline void Stop(); + + // If the timer is not currently active, interval must be either 0 or greater. + // If the timer is already active, interval is ignored. + inline void Update(uint64_t interval); + + static inline void Free(Timer* timer); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(Timer) + SET_SELF_SIZE(Timer) + + private: + static inline void OnTimeout(uv_timer_t* timer); + + bool stopped_ = false; + Environment* env_; + std::function fn_; + uv_timer_t timer_; +}; + +using TimerPointer = DeleteFnPtr; + +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 StatelessResetToken( + const uint8_t* token) + : token_(token) {} + + inline std::string ToString() const; + const uint8_t* data() const { return token_; } + + 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 + using Map = + std::unordered_map< + StatelessResetToken, + BaseObjectPtr, + StatelessResetToken::Hash>; + + private: + uint8_t buf_[NGTCP2_STATELESS_RESET_TOKENLEN]{}; + const uint8_t* token_; +}; + +template +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/src/stream_base-inl.h b/src/stream_base-inl.h index 27a9a01c7c2170..3cc44a31772642 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -39,7 +39,8 @@ inline StreamReq* StreamReq::FromObject(v8::Local req_wrap_obj) { inline void StreamReq::Dispose() { object()->SetAlignedPointerInInternalField( StreamReq::kStreamReqField, nullptr); - delete this; + BaseObjectPtr destroy_me{GetAsyncWrap()}; + destroy_me->Detach(); } inline v8::Local StreamReq::object() { diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 277eb6b81bae32..cb03686b4773a9 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -200,6 +200,7 @@ void UDPWrap::Initialize(Local target, Local constants = Object::New(env->isolate()); NODE_DEFINE_CONSTANT(constants, UV_UDP_IPV6ONLY); + NODE_DEFINE_CONSTANT(constants, UV_UDP_REUSEADDR); target->Set(context, env->constants_string(), constants).Check(); diff --git a/src/udp_wrap.h b/src/udp_wrap.h index 6fed1d2dfea810..75a123d8fa793e 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -215,6 +215,11 @@ class UDPWrap final : public HandleWrap, v8::Local current_send_req_wrap_; }; +int sockaddr_for_family(int address_family, + const char* address, + const unsigned short port, + sockaddr_storage* addr); + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/util.h b/src/util.h index 5eaa20b760168b..e33136658c63e0 100644 --- a/src/util.h +++ b/src/util.h @@ -560,6 +560,11 @@ struct MallocedBuffer { size = new_size; } + void Realloc(size_t new_size) { + Truncate(new_size); + data = UncheckedRealloc(data, new_size); + } + inline bool is_empty() const { return data == nullptr; } MallocedBuffer() : data(nullptr), size(0) {} @@ -714,6 +719,11 @@ constexpr size_t arraysize(const T (&)[N]) { return N; } +template +constexpr size_t strsize(const T (&)[N]) { + return N - 1; +} + // Round up a to the next highest multiple of b. template constexpr T RoundUp(T a, T b) { diff --git a/test/cctest/test_quic_buffer.cc b/test/cctest/test_quic_buffer.cc new file mode 100644 index 00000000000000..0e4f697ac3d4f9 --- /dev/null +++ b/test/cctest/test_quic_buffer.cc @@ -0,0 +1,199 @@ +#include "quic/node_quic_buffer-inl.h" +#include "node_bob-inl.h" +#include "util-inl.h" +#include "uv.h" + +#include "gtest/gtest.h" +#include +#include + +using node::quic::QuicBuffer; +using node::quic::QuicBufferChunk; +using node::bob::Status; +using node::bob::Options; +using node::bob::Done; + +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); + CHECK_EQ(0, buffer.length()); + + // We have to move the read head forward in order to consume + buffer.Seek(1); + buffer.Consume(100); + CHECK_EQ(true, done); + CHECK_EQ(0, buffer.length()); +} + +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 + CHECK_EQ(true, done); + CHECK_EQ(0, buffer.length()); +} + +TEST(QuicBuffer, Multiple) { + uv_buf_t bufs[] { + uv_buf_init(const_cast("abcdefghijklmnopqrstuvwxyz"), 26), + uv_buf_init(const_cast("zyxwvutsrqponmlkjihgfedcba"), 26) + }; + + QuicBuffer buf; + bool done = false; + buf.Push(bufs, 2, [&](int status) { done = true; }); + + buf.Seek(2); + CHECK_EQ(buf.remaining(), 50); + CHECK_EQ(buf.length(), 52); + + buf.Consume(25); + CHECK_EQ(buf.length(), 27); + + buf.Consume(25); + CHECK_EQ(buf.length(), 2); + + buf.Consume(2); + CHECK_EQ(0, buf.length()); +} + +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++; + CHECK_EQ(0, status); + delete[] ptr; + }); + buffer.Seek(node::arraysize(bufs)); + + buffer.Consume(25); + CHECK_EQ(75, buffer.length()); + buffer.Consume(25); + CHECK_EQ(50, buffer.length()); + buffer.Consume(25); + CHECK_EQ(25, buffer.length()); + buffer.Consume(25); + CHECK_EQ(0, buffer.length()); + + // The callback was only called once tho + CHECK_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++; + CHECK_EQ(UV_ECANCELED, status); + delete[] ptr; + }); + + buffer.Seek(1); + buffer.Consume(25); + CHECK_EQ(75, buffer.length()); + buffer.Cancel(); + CHECK_EQ(0, buffer.length()); + + // The callback was only called once tho + CHECK_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); + + CHECK_EQ(100, buffer1.length()); + + buffer2 = std::move(buffer1); + CHECK_EQ(0, buffer1.length()); + CHECK_EQ(100, buffer2.length()); +} + +TEST(QuicBuffer, QuicBufferChunk) { + std::unique_ptr chunk = + std::make_unique(100); + memset(chunk->out(), 1, 100); + + QuicBuffer buffer; + buffer.Push(std::move(chunk)); + buffer.End(); + CHECK_EQ(100, buffer.length()); + + auto next = [&]( + int status, + const ngtcp2_vec* data, + size_t count, + Done done) { + CHECK_EQ(status, Status::STATUS_END); + CHECK_EQ(count, 1); + CHECK_NOT_NULL(data); + done(100); + }; + + CHECK_EQ(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); + + CHECK_EQ(buffer.remaining(), 0); + + buffer.Consume(50); + CHECK_EQ(50, buffer.length()); + + buffer.Consume(50); + CHECK_EQ(0, buffer.length()); +} diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc new file mode 100644 index 00000000000000..eb1a9c53319580 --- /dev/null +++ b/test/cctest/test_quic_cid.cc @@ -0,0 +1,31 @@ +#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 +#include + +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/cctest/test_quic_verifyhostnameidentity.cc b/test/cctest/test_quic_verifyhostnameidentity.cc new file mode 100644 index 00000000000000..f611239ac72e4a --- /dev/null +++ b/test/cctest/test_quic_verifyhostnameidentity.cc @@ -0,0 +1,349 @@ + +#include "base_object-inl.h" +#include "quic/node_quic_crypto.h" +#include "quic/node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "util.h" +#include "gtest/gtest.h" + +#include + +#include +#include +#include + +using node::quic::VerifyHostnameIdentity; + +enum altname_type { + TYPE_DNS, + TYPE_IP, + TYPE_URI +}; + +struct altname { + altname_type type; + const char* name; +}; + +void ToAltNamesMap( + const altname* names, + size_t names_len, + std::unordered_multimap* map) { + for (size_t n = 0; n < names_len; n++) { + switch (names[n].type) { + case TYPE_DNS: + map->emplace("dns", names[n].name); + continue; + case TYPE_IP: + map->emplace("ip", names[n].name); + continue; + case TYPE_URI: + map->emplace("uri", names[n].name); + continue; + } + } +} + +TEST(QuicCrypto, BasicCN_1) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("a.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_2) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("A.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_3_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_4) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("a.com."), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_5_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string(".a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_6_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_7_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_8_Fail) { + std::unordered_multimap altnames; + altnames.emplace("uri", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_9) { + std::unordered_multimap altnames; + altnames.emplace("ip", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_10_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "8.8.8.8/24"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_11) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("*.a.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_12_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("ba.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_13_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("\n.a.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_14_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "omg.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_15_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("b*b.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_16_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_17) { + // TODO(@jasnell): This should test multiple CN's. The code is only + // implemented to support one. Need to fix + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("foo.com", std::string("foo.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_18_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_19_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_20) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.co.uk"); + CHECK_EQ( + VerifyHostnameIdentity("a.co.uk", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_21_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_22_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_23) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_24) { + std::unordered_multimap altnames; + altnames.emplace("dns", "A.COM"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_25_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_26) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_27_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("c.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_28) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_29) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a-cb.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_30_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + + +TEST(QuicCrypto, BasicCN_31) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + altnames.emplace("dns", "a.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), 0); +} + + +TEST(QuicCrypto, BasicCN_32) { + std::unordered_multimap altnames; + altnames.emplace("uri", "a.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_33_Fail) { + std::unordered_multimap altnames; + altnames.emplace("uri", "*.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +// // Invalid URI +// { +// host: 'a.b.a.com', cert: { +// subjectaltname: 'URI:http://[a.b.a.com]/', +// subject: {} +// } +// }, + +TEST(QuicCrypto, BasicCN_35_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_36) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.1", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_37_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.2", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_38_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.1", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_39_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("localhost", std::string("localhost"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_40) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity( + "xn--bcher-kva.example.com", + std::string("*.example.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_41_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity( + "xn--bcher-kva.example.com", + std::string("xn--*.example.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} diff --git a/test/common/README.md b/test/common/README.md index 5479a39d8c215c..ba35fa075be996 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -21,6 +21,7 @@ This directory contains modules used to test the Node.js implementation. * [Report module](#report-module) * [tick module](#tick-module) * [tmpdir module](#tmpdir-module) +* [UDP pair helper](#udp-pair-helper) * [WPT module](#wpt-module) ## Benchmark Module @@ -935,6 +936,19 @@ listener to process `'beforeExit'`. If a file needs to be left open until Node.js completes, use a child process and call `refresh()` only in the parent. +## UDP pair helper + +The `common/udppair` module exports a function `makeUDPPair` and a class +`FakeUDPWrap`. + +`FakeUDPWrap` emits `'send'` events when data is to be sent on it, and provides +an `emitReceived()` API for acting as if data has been received on it. + +`makeUDPPair` returns an object `{ clientSide, serverSide }` where each side +is an `FakeUDPWrap` connected to the other side. + +There is no difference between client or server side beyond their names. + ## WPT Module ### `harness` diff --git a/test/common/fixtures.js b/test/common/fixtures.js index 2390ee8284e421..e33ab1d3e1b3e6 100644 --- a/test/common/fixtures.js +++ b/test/common/fixtures.js @@ -20,9 +20,14 @@ function readFixtureKey(name, enc) { return fs.readFileSync(fixturesPath('keys', name), enc); } +function readFixtureKeys(enc, ...names) { + return names.map((name) => readFixtureKey(name, enc)); +} + module.exports = { fixturesDir, path: fixturesPath, readSync: readFixtureSync, - readKey: readFixtureKey + readKey: readFixtureKey, + readKeys: readFixtureKeys, }; diff --git a/test/common/index.js b/test/common/index.js index 28ce841c48cc3f..f888ad2f449d41 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -50,6 +50,7 @@ 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. @@ -674,6 +675,7 @@ const common = { getTTYfd, hasIntl, hasCrypto, + hasQuic, hasMultiLocalhost, invalidArgTypeHelper, isAIX, diff --git a/test/common/quic.js b/test/common/quic.js new file mode 100644 index 00000000000000..6fe121886ad0a2 --- /dev/null +++ b/test/common/quic.js @@ -0,0 +1,38 @@ +/* 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-27'; + +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/common/udppair.js b/test/common/udppair.js new file mode 100644 index 00000000000000..0178d722fde938 --- /dev/null +++ b/test/common/udppair.js @@ -0,0 +1,100 @@ +/* eslint-disable node-core/require-common-first, node-core/required-modules */ +'use strict'; +const { internalBinding } = require('internal/test/binding'); +const { JSUDPWrap } = internalBinding('js_udp_wrap'); +const EventEmitter = require('events'); + +class FakeUDPWrap extends EventEmitter { + constructor() { + super(); + + this._handle = new JSUDPWrap(); + + this._handle.onreadstart = () => this._startReading(); + this._handle.onreadstop = () => this._stopReading(); + this._handle.onwrite = + (wrap, buffers, addr) => this._write(wrap, buffers, addr); + this._handle.getsockname = (obj) => { + Object.assign(obj, { address: '127.0.0.1', family: 'IPv4', port: 1337 }); + return 0; + }; + + this.reading = false; + this.bufferedReceived = []; + this.emitBufferedImmediate = null; + } + + _emitBuffered = () => { + if (!this.reading) return; + if (this.bufferedReceived.length > 0) { + this.emitReceived(this.bufferedReceived.shift()); + this.emitBufferedImmediate = setImmediate(this._emitBuffered); + } else { + this.emit('wantRead'); + } + }; + + _startReading() { + this.reading = true; + this.emitBufferedImmediate = setImmediate(this._emitBuffered); + } + + _stopReading() { + this.reading = false; + clearImmediate(this.emitBufferedImmediate); + } + + _write(wrap, buffers, addr) { + this.emit('send', { buffers, addr }); + setImmediate(() => this._handle.onSendDone(wrap, 0)); + } + + afterBind() { + this._handle.onAfterBind(); + } + + emitReceived(info) { + if (!this.reading) { + this.bufferedReceived.push(info); + return; + } + + const { + buffers, + addr: { + family = 4, + address = '127.0.0.1', + port = 1337, + }, + flags = 0 + } = info; + + let familyInt; + switch (family) { + case 'IPv4': familyInt = 4; break; + case 'IPv6': familyInt = 6; break; + default: throw new Error('bad family'); + } + + for (const buffer of buffers) { + this._handle.emitReceived(buffer, familyInt, address, port, flags); + } + } +} + +function makeUDPPair() { + const serverSide = new FakeUDPWrap(); + const clientSide = new FakeUDPWrap(); + + serverSide.on('send', + (chk) => setImmediate(() => clientSide.emitReceived(chk))); + clientSide.on('send', + (chk) => setImmediate(() => serverSide.emitReceived(chk))); + + return { serverSide, clientSide }; +} + +module.exports = { + FakeUDPWrap, + makeUDPPair +}; diff --git a/test/parallel/test-module-cjs-helpers.js b/test/parallel/test-module-cjs-helpers.js index 12de65598e54e1..cadb5f99362cda 100644 --- a/test/parallel/test-module-cjs-helpers.js +++ b/test/parallel/test-module-cjs-helpers.js @@ -5,5 +5,5 @@ require('../common'); const assert = require('assert'); const { builtinLibs } = require('internal/modules/cjs/helpers'); -const expectedLibs = process.features.inspector ? 34 : 33; +const expectedLibs = process.features.inspector ? 35 : 34; assert.strictEqual(builtinLibs.length, expectedLibs); diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 14484293dc4621..f0aac8deda15c3 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -2,8 +2,18 @@ const common = require('../common'); const assert = require('assert'); -const expected_keys = ['ares', 'brotli', 'modules', 'node', - 'uv', 'v8', 'zlib', 'nghttp2', 'napi', 'llhttp']; +const expected_keys = [ + 'ares', + 'brotli', + 'modules', + 'node', + 'uv', + 'v8', + 'zlib', + 'nghttp2', + 'napi', + 'llhttp' +]; if (common.hasCrypto) { expected_keys.push('openssl'); @@ -16,6 +26,11 @@ 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 new file mode 100644 index 00000000000000..3d5a5b581fbc24 --- /dev/null +++ b/test/parallel/test-quic-binding.js @@ -0,0 +1,39 @@ +// 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), 'ff00001b'); +assert.strictEqual(quic.constants.NGTCP2_ALPN_H3, '\u0005h3-27'); + +// 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-client-connect-multiple-parallel.js b/test/parallel/test-quic-client-connect-multiple-parallel.js new file mode 100644 index 00000000000000..43282a13f31bb0 --- /dev/null +++ b/test/parallel/test-quic-client-connect-multiple-parallel.js @@ -0,0 +1,57 @@ +// 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 quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +(async function() { + const servers = []; + for (let i = 0; i < 3; i++) { + const server = quic.createSocket(); + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); + })); + + server.on('close', common.mustCall()); + + servers.push(server); + } + + await Promise.all(servers.map((server) => once(server, 'ready'))); + + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + let done = 0; + for (const server of servers) { + const req = 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(); + if (++done === servers.length) client.close(); + })); + })); + + req.on('close', common.mustCall()); + } +})().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 new file mode 100644 index 00000000000000..51c8289f3fe363 --- /dev/null +++ b/test/parallel/test-quic-client-connect-multiple-sequential.js @@ -0,0 +1,56 @@ +// 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 quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const { once } = require('events'); + +(async function() { + const servers = []; + for (let i = 0; i < 3; i++) { + const server = quic.createSocket(); + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); + })); + + server.on('close', common.mustCall()); + + servers.push(server); + } + + await Promise.all(servers.map((server) => once(server, 'ready'))); + + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + for (const server of servers) { + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + const [ stream ] = await once(req, 'stream'); + stream.resume(); + await once(stream, 'end'); + + server.close(); + req.close(); + await once(req, 'close'); + } + + client.close(); + + await once(client, 'close'); +})().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 new file mode 100644 index 00000000000000..6518c8a2795f9e --- /dev/null +++ b/test/parallel/test-quic-client-empty-preferred-address.js @@ -0,0 +1,57 @@ +// 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 `accpet`, 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 { createSocket } = require('quic'); +const { once } = require('events'); + +(async () => { + const server = createSocket(); + + let client; + const options = { key, cert, ca, alpn: 'zzz' }; + server.listen(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 once(server, 'ready'); + + client = createSocket({ client: options }); + + const clientSession = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + preferredAddressPolicy: 'accept', + }); + + await once(clientSession, 'secure'); + + const stream = 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 new file mode 100644 index 00000000000000..0615894ffeda0c --- /dev/null +++ b/test/parallel/test-quic-client-server.js @@ -0,0 +1,372 @@ +// 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 { Buffer } = require('buffer'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const fs = require('fs'); +const { + key, + cert, + ca, + debug, +} = require('../common/quic'); + +const filedata = fs.readFileSync(__filename, { encoding: 'utf8' }); + +const { createSocket } = require('quic'); + +const kStatelessResetToken = + Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); + +let client; + +const server = createSocket({ + validateAddress: true, + statelessResetSecret: kStatelessResetToken +}); + +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 countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ + key, + cert, + ca, + requestCert: true, + rejectUnauthorized: false, + alpn: kALPN, +}); +server.on('session', common.mustCall((session) => { + 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('usePreferredAddress', common.mustNotCall()); + + session.on('clientHello', common.mustCall( + (alpn, servername, ciphers, cb) => { + assert.strictEqual(alpn, kALPN); + assert.strictEqual(servername, kServerName); + assert.strictEqual(ciphers.length, 4); + cb(); + })); + + session.on('OCSPRequest', common.mustCall( + (servername, context, cb) => { + debug('QuicServerSession received a OCSP request'); + assert.strictEqual(servername, kServerName); + + // This will be a SecureContext. By default it will + // be the SecureContext used to create the QuicSession. + // If the user wishes to do something with it, it can, + // but if it wishes to pass in a new SecureContext, + // it can pass it in as the second argument to the + // callback below. + assert(context); + debug('QuicServerSession Certificate: ', context.getCertificate()); + debug('QuicServerSession Issuer: ', context.getIssuer()); + + // The callback can be invoked asynchronously + setImmediate(() => { + // The first argument is a potential error, + // in which case the session will be destroyed + // immediately. + // The second is an optional new SecureContext + // The third is the ocsp response. + // All arguments are optional + cb(null, null, Buffer.from('hello')); + }); + })); + + 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 = session.openStream({ halfOpen: true }); + assert(uni.unidirectional); + assert(!uni.bidirectional); + assert(uni.serverInitiated); + assert(!uni.clientInitiated); + assert(!uni.pending); + uni.write(unidata[0], common.mustCall()); + uni.end(unidata[1], common.mustCall()); + uni.on('finish', common.mustCall()); + uni.on('end', common.mustCall()); + uni.on('data', common.mustNotCall()); + uni.on('close', common.mustCall(() => { + assert.strictEqual(uni.finalSize, 0n); + })); + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + })); + + 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.bidirectional); + assert(!stream.unidirectional); + assert(stream.clientInitiated); + assert(!stream.serverInitiated); + assert(!stream.pending); + + const file = fs.createReadStream(__filename); + let data = ''; + file.pipe(stream); + 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, 'bigint'); + assert.strictEqual(typeof stream.bytesReceived, 'bigint'); + assert.strictEqual(typeof stream.bytesSent, 'bigint'); + assert.strictEqual(typeof stream.maxExtendedOffset, 'bigint'); + assert.strictEqual(stream.finalSize, BigInt(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_QUICSESSION_DESTROYED', + name: 'Error' + }; + assert.throws(() => session.ping(), { ...err }); + assert.throws(() => session.openStream(), { + ...err, + message: 'Cannot call openStream after a QuicSession has been destroyed' + }); + assert.throws(() => session.updateKey(), { + ...err, + message: 'Cannot call updateKey after a QuicSession has been destroyed' + }); + })); +})); + +server.on('ready', common.mustCall(() => { + 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]; + + client = createSocket({ client: { key, cert, ca, alpn: kALPN } + }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + debug(' Bound duration', + client.boundDuration); + debug(' Bytes Sent/Received: %d/%d', + client.bytesSent, + client.bytesReceived); + debug(' Packets Sent/Received: %d/%d', + client.packetsSent, + client.packetsReceived); + debug(' Sessions:', client.clientSessions); + })); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: kServerName, + requestOCSP: true, + }); + + assert.strictEqual(req.servername, kServerName); + + req.on('usePreferredAddress', common.mustNotCall()); + + req.on('OCSPResponse', common.mustCall((response) => { + debug(`QuicClientSession OCSP response: "${response.toString()}"`); + assert.strictEqual(response.toString(), 'hello'); + })); + + 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((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' + }); + + { + 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 file = fs.createReadStream(__filename); + const stream = req.openStream(); + file.pipe(stream); + let data = ''; + stream.resume(); + stream.setEncoding('utf8'); + stream.on('blocked', 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); + assert.strictEqual(stream.finalSize, BigInt(filedata.length)); + countdown.dec(); + })); + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + let data = ''; + 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, 26n); + 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); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', () => { + debug('Server closing. Duration', server.duration); + debug(' Bound duration:', + server.boundDuration); + debug(' Listen duration:', + server.listenDuration); + debug(' Bytes Sent/Received: %d/%d', + server.bytesSent, + server.bytesReceived); + debug(' Packets Sent/Received: %d/%d', + server.packetsSent, + server.packetsReceived); + debug(' Sessions:', server.serverSessions); +}); diff --git a/test/parallel/test-quic-errors-quicsession-openstream.js b/test/parallel/test-quic-errors-quicsession-openstream.js new file mode 100644 index 00000000000000..02325e6cca6151 --- /dev/null +++ b/test/parallel/test-quic-errors-quicsession-openstream.js @@ -0,0 +1,84 @@ +// 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 { createHook } = require('async_hooks'); +const assert = require('assert'); +const quic = require('quic'); + +// Ensure that no QUICSTREAM instances are created during the test +createHook({ + init(id, type) { + assert.notStrictEqual(type, 'QUICSTREAM'); + } +}).enable(); + +const Countdown = require('../common/countdown'); +const { key, cert, ca } = require('../common/quic'); + +const options = { key, cert, ca, alpn: 'zzz', maxStreamsUni: 0 }; +const server = quic.createSocket({ server: options }); +const client = quic.createSocket({ client: options }); + +const countdown = new Countdown(1, () => { + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + session.on('stream', common.mustNotCall()); +})); + +server.on('ready', common.mustCall(() => { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + ['z', 1, {}, [], null, Infinity, 1n].forEach((i) => { + assert.throws( + () => req.openStream({ halfOpen: i }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + ['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => req.openStream({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + [-1, Number.MAX_SAFE_INTEGER + 1].forEach((highWaterMark) => { + assert.throws(() => req.openStream({ highWaterMark }), { + code: 'ERR_OUT_OF_RANGE' + }); + }); + + ['a', 1n, [], {}, false].forEach((highWaterMark) => { + assert.throws(() => req.openStream({ highWaterMark }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + req.on('ready', common.mustCall()); + req.on('secure', common.mustCall()); + + // Unidirectional streams are not allowed. openStream will succeeed + // but the stream will be destroyed immediately. The underlying + // QuicStream C++ handle will not be created. + req.openStream({ + halfOpen: true, + highWaterMark: 10, + defaultEncoding: 'utf16le' + }).on('error', common.expectsError({ + code: 'ERR_QUICSTREAM_OPEN_FAILED' + })).on('error', common.mustCall(() => countdown.dec())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-errors-quicsocket-connect.js b/test/parallel/test-quic-errors-quicsocket-connect.js new file mode 100644 index 00000000000000..2ba330a60f819a --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-connect.js @@ -0,0 +1,243 @@ +// 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 { createSocket } = require('quic'); + +// 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 = createSocket(); + +// Test invalid minDHSize options argument +['test', 1n, {}, [], false].forEach((minDHSize) => { + assert.throws(() => client.connect({ minDHSize }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid port argument option +[-1, 'test', 1n, {}, [], NaN, false, 65536].forEach((port) => { + assert.throws(() => client.connect({ port }), { + code: 'ERR_SOCKET_BAD_PORT' + }); +}); + +// Test invalid address argument option +[-1, 10, 1n, {}, [], true].forEach((address) => { + assert.throws(() => client.connect({ address }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test servername can't be IP address argument option +[ + '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' +].forEach((servername) => { + assert.throws(() => client.connect({ servername }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + +[-1, 10, 1n, {}, [], true].forEach((servername) => { + assert.throws(() => client.connect({ servername }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid remoteTransportParams argument option +[-1, 'test', 1n, {}, []].forEach((remoteTransportParams) => { + assert.throws(() => client.connect({ remoteTransportParams }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid sessionTicket argument option +[-1, 'test', 1n, {}, []].forEach((sessionTicket) => { + assert.throws(() => client.connect({ sessionTicket }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid alpn argument option +[-1, 10, 1n, {}, [], true].forEach((alpn) => { + assert.throws(() => client.connect({ alpn }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[ + 'idleTimeout', + 'activeConnectionIdLimit', + 'maxAckDelay', + 'maxData', + 'maxPacketSize', + 'maxStreamDataBidiLocal', + 'maxStreamDataBidiRemote', + 'maxStreamDataUni', + 'maxStreamsBidi', + 'maxStreamsUni', + 'highWaterMark', +].forEach((prop) => { + assert.throws(() => client.connect({ [prop]: -1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => client.connect({ [prop]: Number.MAX_SAFE_INTEGER + 1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => client.connect({ [prop]: val }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); +}); + +// activeConnectionIdLimit must be between 2 and 8, inclusive +[1, 9].forEach((activeConnectionIdLimit) => { + assert.throws(() => client.connect({ activeConnectionIdLimit }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +['a', 1n, 1, [], {}].forEach((ipv6Only) => { + assert.throws(() => client.connect({ ipv6Only }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, [], {}].forEach((preferredAddressPolicy) => { + assert.throws(() => client.connect({ preferredAddressPolicy }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, 'test', [], {}].forEach((qlog) => { + assert.throws(() => client.connect({ qlog }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, 'test', [], {}].forEach((requestOCSP) => { + assert.throws(() => client.connect({ requestOCSP }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, [], {}, 'aaa'].forEach((type) => { + assert.throws(() => client.connect({ type }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + + +[ + 'qpackMaxTableCapacity', + 'qpackBlockedStreams', + 'maxHeaderListSize', + 'maxPushes', +].forEach((prop) => { + assert.throws(() => client.connect({ h3: { [prop]: -1 } }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => client.connect({ h3: { [prop]: Number.MAX_SAFE_INTEGER + 1 } }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => client.connect({ h3: { [prop]: val } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); +}); + +['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => client.connect({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + + +// Test that connect cannot be called after QuicSocket is closed. +client.close(); +assert.throws(() => client.connect(), { + code: 'ERR_QUICSOCKET_DESTROYED' +}); + +// TODO(@jasnell): Test additional options: +// +// Client QuicSession Related: +// +// [x] idleTimeout - must be a number greater than zero +// [x] ipv6Only - must be a boolean +// [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] maxPacketSize - 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 new file mode 100644 index 00000000000000..447f7c8ea692ef --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-create.js @@ -0,0 +1,187 @@ +// 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 { createSocket } = require('quic'); + +// Test invalid QuicSocket options argument +[1, 'test', false, 1n, null].forEach((i) => { + assert.throws(() => createSocket(i), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket port argument option +[-1, 'test', 1n, {}, [], NaN, false].forEach((port) => { + assert.throws(() => createSocket({ endpoint: { port } }), { + code: 'ERR_SOCKET_BAD_PORT' + }); +}); + +// Test invalid QuicSocket addressargument option +[-1, 10, 1n, {}, [], NaN, false].forEach((address) => { + assert.throws(() => createSocket({ endpoint: { address } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket type argument option +[1, false, 1n, {}, null, NaN].forEach((type) => { + assert.throws(() => createSocket({ endpoint: { type } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket ipv6Only argument option +[1, NaN, 1n, null, {}, []].forEach((ipv6Only) => { + assert.throws(() => createSocket({ endpoint: { ipv6Only } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket reuseAddr argument option +[1, NaN, 1n, null, {}, []].forEach((reuseAddr) => { + assert.throws(() => createSocket({ endpoint: { reuseAddr } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket lookup argument option +[1, 1n, {}, [], 'test', true].forEach((lookup) => { + assert.throws(() => createSocket({ lookup }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket validateAddress argument option +[1, NaN, 1n, null, {}, []].forEach((validateAddress) => { + assert.throws(() => createSocket({ validateAddress }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket validateAddressLRU argument option +[1, NaN, 1n, null, {}, []].forEach((validateAddressLRU) => { + assert.throws(() => createSocket({ validateAddressLRU }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket autoClose argument option +[1, NaN, 1n, null, {}, []].forEach((autoClose) => { + assert.throws(() => createSocket({ autoClose }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket qlog argument option +[1, NaN, 1n, null, {}, []].forEach((qlog) => { + assert.throws(() => createSocket({ qlog }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + + +// Test invalid QuicSocket retryTokenTimeout option +[0, 61, NaN].forEach((retryTokenTimeout) => { + assert.throws(() => createSocket({ retryTokenTimeout }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket retryTokenTimeout option +['test', null, 1n, {}, [], false].forEach((retryTokenTimeout) => { + assert.throws(() => createSocket({ retryTokenTimeout }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnectionsPerHost) => { + assert.throws(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxConnectionsPerHost) => { + assert.throws(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxConnections option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnections) => { + assert.throws(() => createSocket({ maxConnections }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxConnections) => { + assert.throws(() => createSocket({ maxConnections }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxStatelessResetsPerHost option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxStatelessResetsPerHost) => { + assert.throws(() => createSocket({ maxStatelessResetsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxStatelessResetsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxStatelessResetsPerHost) => { + assert.throws(() => createSocket({ maxStatelessResetsPerHost }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, 'test'].forEach((options) => { + assert.throws(() => createSocket({ endpoint: options }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + assert.throws(() => createSocket({ client: options }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + assert.throws(() => createSocket({ 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 new file mode 100644 index 00000000000000..330d49e7b1341c --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-listen.js @@ -0,0 +1,179 @@ +// 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 { createSocket } = require('quic'); + +// Test invalid callback function +{ + const server = createSocket(); + [1, 1n].forEach((cb) => { + assert.throws(() => server.listen({}, cb), { + code: 'ERR_INVALID_CALLBACK' + }); + }); +} + +// Test QuicSocket is already listening +{ + const server = createSocket(); + server.listen(); + assert.throws(() => server.listen(), { + code: 'ERR_QUICSOCKET_LISTENING' + }); + server.close(); +} + +// Test QuicSocket listen after destroy error +{ + const server = createSocket(); + server.close(); + assert.throws(() => server.listen(), { + code: 'ERR_QUICSOCKET_DESTROYED' + }); +} + +{ + // Test incorrect ALPN + const server = createSocket(); + [1, 1n, true, {}, [], null].forEach((alpn) => { + assert.throws(() => server.listen({ alpn }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + // Test invalid idle timeout + [ + 'idleTimeout', + 'activeConnectionIdLimit', + 'maxAckDelay', + 'maxData', + 'maxPacketSize', + 'maxStreamDataBidiLocal', + 'maxStreamDataBidiRemote', + 'maxStreamDataUni', + 'maxStreamsBidi', + 'maxStreamsUni', + 'highWaterMark', + ].forEach((prop) => { + assert.throws(() => server.listen({ [prop]: -1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => server.listen({ [prop]: Number.MAX_SAFE_INTEGER + 1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => server.listen({ [prop]: val }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + }); + + [1, 1n, 'test', {}, []].forEach((rejectUnauthorized) => { + assert.throws(() => server.listen({ rejectUnauthorized }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', {}, []].forEach((requestCert) => { + assert.throws(() => server.listen({ requestCert }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', {}, []].forEach((requestCert) => { + assert.throws(() => server.listen({ requestCert }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', false].forEach((preferredAddress) => { + assert.throws(() => server.listen({ preferredAddress }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + + [1, 1n, null, false, {}, []].forEach((address) => { + assert.throws(() => server.listen({ preferredAddress: { address } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [-1].forEach((port) => { + assert.throws(() => server.listen({ preferredAddress: { port } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 'test', false, null, {}, []].forEach((type) => { + assert.throws(() => server.listen({ preferredAddress: { type } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + }); + + [1, 1n, false, [], {}, null].forEach((ciphers) => { + assert.throws(() => server.listen({ ciphers }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, false, [], {}, null].forEach((groups) => { + assert.throws(() => server.listen({ groups }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + ['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => server.listen({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + // 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); +} + + +// Options to check +// * [x] alpn +// * [x] idleTimeout +// * [x] activeConnectionIdLimit +// * [x] maxAckDelay +// * [x] maxData +// * [x] maxPacketSize +// * [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 new file mode 100644 index 00000000000000..8c7638b55747db --- /dev/null +++ b/test/parallel/test-quic-http3-client-server.js @@ -0,0 +1,157 @@ +// 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 { createSocket } = require('quic'); + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +const kServerName = 'agent2'; // Intentionally the wrong servername + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ + key, + cert, + ca, + alpn: kHttp3Alpn, +}); +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()); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kHttp3Alpn } + }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + h3: { maxPushes: 10 } + }); + debug('QuicClientSession Created'); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession handshake completed'); + + const file = fs.createReadStream(__filename); + const stream = 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); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-http3-push.js b/test/parallel/test-quic-http3-push.js new file mode 100644 index 00000000000000..d91e98a7ee6ebd --- /dev/null +++ b/test/parallel/test-quic-http3-push.js @@ -0,0 +1,157 @@ +// 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 { createSocket } = require('quic'); + +let client; +const server = createSocket(); + +const countdown = new Countdown(2, () => { + server.close(); + client.close(); +}); + +const options = { key, cert, ca, alpn: kHttp3Alpn }; + +server.listen(options); + +server.on('session', common.mustCall((session) => { + + 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()); + 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()); +})); + +server.on('ready', common.mustCall(() => { + client = createSocket({ client: options }); + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + maxStreamsUni: 10, + h3: { maxPushes: 10 } + }); + + 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()); + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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); + })); + + 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()); + + 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(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-http3-trailers.js b/test/parallel/test-quic-http3-trailers.js new file mode 100644 index 00000000000000..58d84098d9b175 --- /dev/null +++ b/test/parallel/test-quic-http3-trailers.js @@ -0,0 +1,109 @@ +// 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 { createSocket } = require('quic'); + +let client; +const server = createSocket(); + +const countdown = new Countdown(1, () => { + server.close(); + client.close(); +}); + +const options = { key, cert, ca, alpn: kHttp3Alpn }; + +server.listen(options); + +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()); +})); + +server.on('ready', common.mustCall(() => { + client = createSocket({ client: options }); + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + maxStreamsUni: 10, + h3: { maxPushes: 10 } + }); + + req.on('close', common.mustCall()); + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-idle-timeout.js b/test/parallel/test-quic-idle-timeout.js new file mode 100644 index 00000000000000..af38c511f55531 --- /dev/null +++ b/test/parallel/test-quic-idle-timeout.js @@ -0,0 +1,73 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); +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 = createSocket({ server: options }); + const client = createSocket({ client: options }); + + server.listen(); + server.on('session', common.mustCall()); + + await once(server, 'ready'); + + const session = 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 = createSocket({ server: options }); + const client = createSocket({ client: options }); + + server.listen({ idleTimeout }); + + 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 once(server, 'ready'); + + const session = 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 new file mode 100644 index 00000000000000..d54200d658c303 --- /dev/null +++ b/test/parallel/test-quic-ipv6only.js @@ -0,0 +1,128 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); + +if (!common.hasIPv6) + common.skip('missing ipv6'); + +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +const kALPN = 'zzz'; + +// Setting `type` to `udp4` while setting `ipv6Only` to `true` is possible +// and it will throw an error. +{ + const server = createSocket({ endpoint: { type: 'udp4', ipv6Only: true } }); + + server.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'EINVAL'); + assert.strictEqual(err.message, 'bind EINVAL 0.0.0.0'); + })); + + server.listen({ key, cert, ca, alpn: kALPN }); +} + +// Connecting ipv6 server by "127.0.0.1" should work when `ipv6Only` +// is set to `false`. +(async () => { + const server = createSocket({ endpoint: { type: 'udp6', ipv6Only: false } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + + server.on('session', common.mustCall((serverSession) => { + serverSession.on('stream', common.mustCall()); + })); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + await once(session, 'secure'); + + const stream = session.openStream({ halfOpen: true }); + stream.end('hello'); + + await once(stream, 'close'); + + client.close(); + server.close(); + + await Promise.allSettled([ + once(client, 'close'), + once(server, 'close') + ]); +})().then(common.mustCall()); + +// When the `ipv6Only` set to `true`, a client cann't connect to it +// through "127.0.0.1". +(async () => { + const server = createSocket({ endpoint: { type: 'udp6', ipv6Only: true } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + server.on('session', common.mustNotCall()); + + await once(server, 'ready'); + + const session = 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 () => { + const server = createSocket({ endpoint: { type: 'udp6' } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + server.on('session', common.mustNotCall()); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + type: 'udp6', + idleTimeout: common.platformTimeout(1), + }); + + session.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_QUICCLIENTSESSION_FAILED'); + client.close(); + server.close(); + })); + + session.on('secure', common.mustNotCall()); + session.on('close', common.mustCall()); + + await Promise.allSettled([ + once(client, 'close'), + once(server, 'close') + ]); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-keylog.js b/test/parallel/test-quic-keylog.js new file mode 100644 index 00000000000000..ef0702f5297897 --- /dev/null +++ b/test/parallel/test-quic-keylog.js @@ -0,0 +1,67 @@ +// 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 { createSocket } = require('quic'); + +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' }; + +(async () => { + const server = createSocket({ server: options }); + const client = createSocket({ client: options }); + + const kServerKeylogs = Array.from(kKeylogs); + const kClientKeylogs = Array.from(kKeylogs); + + server.listen(); + + server.on('session', common.mustCall((session) => { + session.on('keylog', common.mustCall((line) => { + assert.match(line.toString(), kServerKeylogs.shift()); + }, kServerKeylogs.length)); + })); + + await once(server, 'ready'); + + const req = 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 new file mode 100644 index 00000000000000..206442646710c9 --- /dev/null +++ b/test/parallel/test-quic-maxconnectionsperhost.js @@ -0,0 +1,86 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { createSocket } = require('quic'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); +const { key, cert, ca } = require('../common/quic'); +const kServerName = 'agent2'; +const kALPN = 'zzz'; + +// 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(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); + }); +} + +// Test that new client sessions will be closed when it exceeds +// maxConnectionsPerHost. +{ + const kMaxConnectionsPerHost = 5; + const kIdleTimeout = 0; + + let client; + let server; + + const countdown = new Countdown(kMaxConnectionsPerHost + 1, () => { + client.close(); + server.close(); + }); + + function connect() { + return client.connect({ + key, + cert, + ca, + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + servername: kServerName, + alpn: kALPN, + idleTimeout: kIdleTimeout, + }); + } + + server = createSocket({ maxConnectionsPerHost: kMaxConnectionsPerHost }); + + server.listen({ key, cert, ca, alpn: kALPN, idleTimeout: kIdleTimeout }); + + server.on('session', common.mustCall(() => {}, kMaxConnectionsPerHost)); + + server.on('close', common.mustCall(() => { + assert.strictEqual(server.serverBusyCount, 1n); + })); + + server.on('ready', common.mustCall(() => { + client = createSocket(); + + const sessions = []; + + for (let i = 0; i < kMaxConnectionsPerHost; i += 1) { + const req = connect(); + req.on('error', common.mustNotCall()); + req.on('close', common.mustCall(() => countdown.dec())); + sessions.push(req); + } + + const extra = connect(); + extra.on('error', console.log); + extra.on('close', common.mustCall(() => { + countdown.dec(); + // Shutdown the remaining open sessions. + setImmediate(common.mustCall(() => { + for (const req of sessions) + req.close(); + })); + })); + + })); +} diff --git a/test/parallel/test-quic-process-cleanup.js b/test/parallel/test-quic-process-cleanup.js new file mode 100644 index 00000000000000..85e9320a77f5ba --- /dev/null +++ b/test/parallel/test-quic-process-cleanup.js @@ -0,0 +1,57 @@ +// Flags: --no-warnings +'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 quic = require('quic'); +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 server = quic.createSocket({ server: options }); + +server.listen(); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: options }); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustCall(() => { + if (workerData.removeFromSocket) + req.removeFromSocket(); + process.exit(); // Exits the worker thread + })); + + req.on('close', common.mustNotCall()); +})); + +server.on('close', common.mustNotCall()); diff --git a/test/parallel/test-quic-qlog.js b/test/parallel/test-quic-qlog.js new file mode 100644 index 00000000000000..916416c33fab73 --- /dev/null +++ b/test/parallel/test-quic-qlog.js @@ -0,0 +1,66 @@ +// 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 quic = require('quic'); +const { kUDPHandleForTesting } = require('internal/quic/core'); + +const { key, cert, ca } = require('../common/quic'); + +const { serverSide, clientSide } = makeUDPPair(); + +const server = quic.createSocket({ + validateAddress: true, + endpoint: { [kUDPHandleForTesting]: serverSide._handle }, + qlog: true +}); + +serverSide.afterBind(); +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + gatherQlog(session, 'server'); + + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: clientSide._handle }, + client: { key, cert, ca, alpn: 'meow' }, + qlog: true + }); + clientSide.afterBind(); + + const req = 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(); + })); + })); +})); + +function gatherQlog(session, id) { + let log = ''; + session.on('qlog', (chunk) => log += chunk); + session.on('close', common.mustCall(() => { + const { qlog_version, traces } = JSON.parse(log); + assert.strictEqual(typeof qlog_version, 'string'); + assert.strictEqual(typeof traces[0].events, 'object'); + })); +} diff --git a/test/parallel/test-quic-quicendpoint-address.js b/test/parallel/test-quic-quicendpoint-address.js new file mode 100644 index 00000000000000..17c8f6991c5e12 --- /dev/null +++ b/test/parallel/test-quic-quicendpoint-address.js @@ -0,0 +1,87 @@ +// 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 { createSocket } = require('quic'); + +async function Test1(options, address) { + const server = createSocket(options); + assert.strictEqual(server.endpoints.length, 1); + assert.strictEqual(server.endpoints[0].bound, false); + assert.deepStrictEqual({}, server.endpoints[0].address); + + server.listen({ key, cert, ca, alpn: 'zzz' }); + + await once(server, 'ready'); + assert.strictEqual(server.endpoints.length, 1); + const endpoint = server.endpoints[0]; + assert.strictEqual(endpoint.bound, true); + assert.strictEqual(endpoint.destroyed, false); + assert.strictEqual(typeof endpoint.address.port, 'number'); + assert.strictEqual(endpoint.address.address, address); + server.close(); + assert.strictEqual(endpoint.destroyed, true); +} + +async function Test2() { + // Creates a server with multiple endpoints (one on udp4 and udp6) + const server = createSocket({ 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' }); + + 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-quicession-server-openstream-pending.js b/test/parallel/test-quic-quicession-server-openstream-pending.js new file mode 100644 index 00000000000000..6207834cab0d8a --- /dev/null +++ b/test/parallel/test-quic-quicession-server-openstream-pending.js @@ -0,0 +1,59 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that opening a stream works even if the session isn’t ready yet. + +const assert = require('assert'); +const quic = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); +const options = { key, cert, ca, alpn: 'meow' }; + +(async () => { + const server = quic.createSocket({ server: options }); + const client = quic.createSocket({ client: options }); + + server.listen(); + + server.on('session', common.mustCall((session) => { + // The server can create a stream immediately without waiting + // for the secure event... however, the data will not actually + // be transmitted until the handshake is completed. + const stream = session.openStream({ halfOpen: true }); + stream.on('close', common.mustCall()); + stream.on('error', console.log); + stream.end('hello'); + + session.on('stream', common.mustNotCall()); + })); + + await once(server, 'ready'); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + const [ stream ] = await once(req, 'stream'); + + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall()); + + await once(stream, 'close'); + + assert.strictEqual(data, 'hello'); + + server.close(); + client.close(); + + await Promise.all([ + once(server, 'close'), + once(client, 'close') + ]); + +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-openstream-pending.js b/test/parallel/test-quic-quicsession-openstream-pending.js new file mode 100644 index 00000000000000..6b406a396e1bba --- /dev/null +++ b/test/parallel/test-quic-quicsession-openstream-pending.js @@ -0,0 +1,64 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that opening a stream works even if the session isn’t ready yet. + +const assert = require('assert'); +const quic = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); +const options = { key, cert, ca, alpn: 'meow' }; + +(async () => { + const server = quic.createSocket({ server: options }); + const client = quic.createSocket({ client: options }); + + server.listen(); + + server.on('session', common.mustCall((session) => { + session.on('stream', common.mustCall(async (stream) => { + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + await once(stream, 'end'); + assert.strictEqual(data, 'Hello!'); + })); + })); + + await once(server, 'ready'); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + // In this case, the QuicStream is usable but corked + // until the underlying internal QuicStream handle + // has been created, which will not happen until + // after the TLS handshake has been completed. + const stream = req.openStream({ halfOpen: true }); + stream.end('Hello!'); + stream.on('error', common.mustNotCall()); + stream.resume(); + assert(!req.allowEarlyData); + assert(!req.handshakeComplete); + assert(stream.pending); + + await once(stream, 'ready'); + + assert(req.handshakeComplete); + assert(!stream.pending); + + await once(stream, 'close'); + + server.close(); + client.close(); + + await Promise.all([ + once(server, 'close'), + once(client, 'close') + ]); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-resume.js b/test/parallel/test-quic-quicsession-resume.js new file mode 100644 index 00000000000000..b1af4663b0ab54 --- /dev/null +++ b/test/parallel/test-quic-quicsession-resume.js @@ -0,0 +1,100 @@ +'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 { createSocket } = require('quic'); + +const options = { key, cert, ca, alpn: 'zzz' }; + +const server = createSocket({ server: options }); +const client = createSocket({ client: options }); + +const countdown = new Countdown(2, () => { + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + assert(session.usingEarlyData); + })); + + session.on('stream', common.mustCall((stream) => { + stream.resume(); + })); +}, 2)); + +server.on('ready', common.mustCall(() => { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + const stream = req.openStream({ halfOpen: true }); + stream.end('hello'); + stream.resume(); + stream.on('close', () => countdown.dec()); + + 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')); + + // Destroy this initial client session... + req.destroy(); + + // Wait a tick then start a new one. + setImmediate(newSession, ticket, params); + }, 1)); + + function newSession(sessionTicket, remoteTransportParams) { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + sessionTicket, + remoteTransportParams, + autoStart: false, + }); + + assert(req.allowEarlyData); + + const stream = req.openStream({ halfOpen: true }); + stream.end('hello'); + stream.on('error', common.mustNotCall()); + stream.on('close', common.mustCall(() => countdown.dec())); + + req.startHandshake(); + + // TODO(@jasnell): There's a slight bug in here in that + // calling end() will uncork the stream, causing data to + // be flushed to the C++ layer, which will trigger a + // SendPendingData that will start the handshake. That + // has the effect of short circuiting the intent of + // manual startHandshake(), which makes it not use 0RTT + // for the stream data. + + req.on('secure', common.mustCall(() => { + // 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); + })); + } +})); diff --git a/test/parallel/test-quic-quicsession-send-fd.js b/test/parallel/test-quic-quicsession-send-fd.js new file mode 100644 index 00000000000000..2d208dea850729 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-fd.js @@ -0,0 +1,86 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const quic = require('quic'); +const fs = require('fs'); + +const { key, cert, ca } = require('../common/quic'); + +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 }); + } + } +} + +for (const { variant, offset, length } of variants) { + const server = quic.createSocket(); + let fd; + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: false }); + + stream.on('data', common.mustNotCall()); + stream.on('finish', common.mustCall()); + stream.on('close', common.mustCall()); + stream.on('end', 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()); + })); + + server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + 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); + + stream.end(); + client.close(); + server.close(); + if (fd !== undefined) { + if (fd.close) fd.close().then(common.mustCall()); + else fs.closeSync(fd); + } + })); + })); + + req.on('close', common.mustCall()); + })); + + server.on('close', common.mustCall()); +} 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 new file mode 100644 index 00000000000000..5e923204fe93d3 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-close-before-open.js @@ -0,0 +1,46 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const quic = require('quic'); +const fs = require('fs'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +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 new file mode 100644 index 00000000000000..142f76538a6ded --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-open-error-handled.js @@ -0,0 +1,49 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const path = require('path'); +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +server.on('close', 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 new file mode 100644 index 00000000000000..ac0720ad155ca0 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-open-error.js @@ -0,0 +1,49 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const path = require('path'); +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = 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(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-server-destroy-early.js b/test/parallel/test-quic-quicsession-server-destroy-early.js new file mode 100644 index 00000000000000..95ac20b000b96f --- /dev/null +++ b/test/parallel/test-quic-quicsession-server-destroy-early.js @@ -0,0 +1,81 @@ +// 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 fs = require('fs'); +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 { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +const kServerPort = process.env.NODE_DEBUG_KEYLOG ? 5678 : 0; +const kClientPort = process.env.NODE_DEBUG_KEYLOG ? 5679 : 0; + +const kServerName = 'agent2'; // Intentionally the wrong servername +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +server.listen({ key, cert, ca, alpn: kALPN }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + if (process.env.NODE_DEBUG_KEYLOG) { + const kl = fs.createWriteStream(process.env.NODE_DEBUG_KEYLOG); + session.on('keylog', kl.write.bind(kl)); + } + + session.on('close', common.mustCall(() => { + client.close(); + server.close(); + + assert.throws(() => server.close(), { + code: 'ERR_QUICSOCKET_DESTROYED', + name: 'Error', + message: 'Cannot call close after a QuicSocket has been destroyed' + }); + })); + session.on('stream', common.mustNotCall()); + + // Prematurely destroy the session without waiting for the + // handshake to complete. + session.destroy(); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kALPN } + }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + })); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + }); + + req.on('secure', common.mustNotCall()); + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-close.js b/test/parallel/test-quic-quicsocket-close.js new file mode 100644 index 00000000000000..c287e958a62067 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-close.js @@ -0,0 +1,19 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); + +{ + const socket = createSocket(); + socket.close(common.mustCall()); + socket.on('close', common.mustCall()); + assert.throws(() => socket.close(), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: 'Cannot call close after a QuicSocket has been destroyed' + }); +} diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js new file mode 100644 index 00000000000000..9ec9312ce431c5 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js @@ -0,0 +1,109 @@ +// Flags: --no-warnings +'use strict'; + +// Tests that stream data is successfully transmitted under +// packet loss conditions on the receiving end. + +// TODO(@jasnell): We need an equivalent test that checks +// transmission end random packet loss. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug +} = require('../common/quic'); +// TODO(@jasnell): There's currently a bug in pipeline when piping +// a duplex back into to itself. +// const { pipeline } = require('stream'); + +const { createSocket } = require('quic'); + +const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const options = { key, cert, ca, alpn: 'echo' }; + +const client = createSocket({ client: options }); +const server = createSocket({ 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(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + stream.on('data', (chunk) => stream.write(chunk)); + stream.on('end', () => stream.end()); + // TODO(@jasnell): There's currently a bug in pipeline when piping + // a duplex back into to itself. + // pipeline(stream, stream, common.mustCall((err) => { + // assert(!err); + // })); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = 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(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); +})); diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js new file mode 100644 index 00000000000000..c5453ac6b83966 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js @@ -0,0 +1,109 @@ +// Flags: --no-warnings +'use strict'; + +// Tests that stream data is successfully transmitted under +// packet loss conditions on the receiving end. + +// TODO(@jasnell): We need an equivalent test that checks +// transmission end random packet loss. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug +} = require('../common/quic'); +// TODO(@jasnell): There's currently a bug in pipeline when piping +// a duplex back into to itself. +// const { pipeline } = require('stream'); + +const { createSocket } = require('quic'); + +const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const options = { key, cert, ca, alpn: 'echo' }; + +const client = createSocket({ client: options }); +const server = createSocket({ server: options }); + +// Both client and server will drop transmitted 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(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + stream.on('data', (chunk) => stream.write(chunk)); + stream.on('end', () => stream.end()); + // TODO(@jasnell): There's currently a bug in pipeline when piping + // a duplex back into to itself. + // pipeline(stream, stream, common.mustCall((err) => { + // assert(!err); + // })); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = 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(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); +})); diff --git a/test/parallel/test-quic-quicsocket-serverbusy.js b/test/parallel/test-quic-quicsocket-serverbusy.js new file mode 100644 index 00000000000000..d53b32190642fb --- /dev/null +++ b/test/parallel/test-quic-quicsocket-serverbusy.js @@ -0,0 +1,62 @@ +// 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 { + key, + cert, + ca, + debug, + kServerPort, + kClientPort +} = require('../common/quic'); + +const { createSocket } = require('quic'); +const options = { key, cert, ca, alpn: 'zzz' }; + +let client; +const server = createSocket({ + endpoint: { port: kServerPort }, + server: options +}); + +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.setServerBusy(); +server.listen(); + +server.on('close', common.mustCall()); +server.on('listening', common.mustCall()); +server.on('session', common.mustNotCall()); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: options + }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustNotCall()); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/parallel/test-quic-quicsocket.js b/test/parallel/test-quic-quicsocket.js new file mode 100644 index 00000000000000..5a5cf57505e72e --- /dev/null +++ b/test/parallel/test-quic-quicsocket.js @@ -0,0 +1,154 @@ +// 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 { createSocket } = require('quic'); + +const socket = createSocket(); +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, 'bigint'); +assert.strictEqual(typeof socket.boundDuration, 'bigint'); +assert.strictEqual(typeof socket.listenDuration, 'bigint'); +assert.strictEqual(typeof socket.bytesReceived, 'bigint'); +assert.strictEqual(socket.bytesReceived, 0n); +assert.strictEqual(socket.bytesSent, 0n); +assert.strictEqual(socket.packetsReceived, 0n); +assert.strictEqual(socket.packetsSent, 0n); +assert.strictEqual(socket.serverSessions, 0n); +assert.strictEqual(socket.clientSessions, 0n); + +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((args) => { + assert.throws(() => socket.setServerBusy(args), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +socket.listen({ alpn: 'zzz' }); +assert(socket.pending); + +socket.on('ready', common.mustCall(() => { + assert(endpoint.bound); + + // QuicSocket is already listening. + assert.throws(() => socket.listen(), { + code: 'ERR_QUICSOCKET_LISTENING' + }); + + 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); +})); + +socket.on('close', common.mustCall(() => { + [ + 'ref', + 'unref', + 'setTTL', + 'setMulticastTTL', + 'setBroadcast', + 'setMulticastLoopback', + 'setMulticastInterface', + 'addMembership', + 'dropMembership' + ].forEach((op) => { + assert.throws(() => endpoint[op](), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: `Cannot call ${op} after a QuicSocket has been destroyed` + }); + }); + + [ + 'setServerBusy', + ].forEach((op) => { + assert.throws(() => socket[op](), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: `Cannot call ${op} after a QuicSocket has been destroyed` + }); + }); +})); diff --git a/test/parallel/test-quic-quicstream-close-early.js b/test/parallel/test-quic-quicstream-close-early.js new file mode 100644 index 00000000000000..358e8705e4c733 --- /dev/null +++ b/test/parallel/test-quic-quicstream-close-early.js @@ -0,0 +1,122 @@ +// 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, + debug, + kServerPort, + kClientPort, + setupKeylog +} = require('../common/quic'); + +const { createSocket } = require('quic'); + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +const kServerName = 'agent1'; +const kALPN = 'zzz'; + +const countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + setupKeylog(session); + + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const uni = session.openStream({ halfOpen: true }); + + uni.write('hi', common.expectsError()); + + uni.close(3); + + uni.on('error', common.mustCall(() => { + assert.strictEqual(uni.aborted, true); + })); + + uni.on('data', common.mustNotCall()); + uni.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed on server', + uni.id); + })); + + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + })); + + session.on('stream', common.mustNotCall()); + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kALPN } + }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + + stream.write('hello', common.expectsError()); + stream.write('there', common.expectsError()); + + stream.close(1); + + stream.on('error', common.mustCall(() => { + assert.strictEqual(stream.aborted, true); + })); + + stream.on('end', common.mustNotCall()); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + stream.on('abort', common.mustNotCall()); + stream.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(), 'hi'); + })); + stream.on('end', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d ended on client', + stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed on client', + stream.id); + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-destroy.js b/test/parallel/test-quic-quicstream-destroy.js new file mode 100644 index 00000000000000..ca5c508320454a --- /dev/null +++ b/test/parallel/test-quic-quicstream-destroy.js @@ -0,0 +1,75 @@ +// 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 { + debug, + key, + cert, + ca +} = require('../common/quic'); + +const { createSocket } = require('quic'); + +const options = { key, cert, ca, alpn: 'zzz' }; + +const server = createSocket({ server: options }); + +server.listen(); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + stream.destroy(); + stream.on('close', common.mustCall()); + stream.on('error', common.mustNotCall()); + assert(stream.destroyed); + })); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const client = createSocket({ client: options }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + })); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + stream.write('foo'); + // Do not explicitly end the stream here. + + stream.on('finish', common.mustNotCall()); + stream.on('data', common.mustNotCall()); + stream.on('end', common.mustCall()); + + stream.on('close', common.mustCall(() => { + debug('Stream closed on client side'); + assert(stream.destroyed); + client.close(); + server.close(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-identifiers.js b/test/parallel/test-quic-quicstream-identifiers.js new file mode 100644 index 00000000000000..dd3443c0b4211a --- /dev/null +++ b/test/parallel/test-quic-quicstream-identifiers.js @@ -0,0 +1,156 @@ +// 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 { debug, key, cert } = require('../common/quic'); + +const { createSocket } = require('quic'); +const options = { key, cert, alpn: 'zzz' }; + +let client; +const server = createSocket({ server: options }); + +const countdown = new Countdown(4, () => { + debug('Countdown expired. Closing sockets'); + server.close(); + client.close(); +}); + +const closeHandler = common.mustCall(() => countdown.dec(), 4); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession created'); + session.on('secure', common.mustCall(() => { + debug('QuicServerSession TLS Handshake Completed.'); + + ([3, 1n, [], {}, null, 'meow']).forEach((halfOpen) => { + assert.throws(() => session.openStream({ halfOpen }), { + code: 'ERR_INVALID_ARG_TYPE', + }); + }); + + const uni = session.openStream({ halfOpen: true }); + uni.end('test'); + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + + const bidi = session.openStream(); + bidi.end('test'); + bidi.resume(); + bidi.on('end', common.mustCall()); + debug('Bidirectional, Server-initiated stream %d opened', bidi.id); + + 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: + debug('Bidirectional, Client-initiated stream %d received', stream.id); + assert(stream.bidirectional); + assert(!stream.unidirectional); + stream.end('test'); + break; + case 2: + debug('Unidirectional, Client-initiated stream %d receieved', + stream.id); + assert(stream.unidirectional); + assert(!stream.bidirectional); + break; + } + stream.resume(); + stream.on('end', common.mustCall()); + }, 2)); +})); + +server.on('ready', common.mustCall(() => { + debug('Server listening on port %d', server.endpoints[0].address.port); + client = createSocket({ client: options }); + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Completed'); + const bidi = req.openStream(); + bidi.end('test'); + bidi.resume(); + bidi.on('close', closeHandler); + assert.strictEqual(bidi.id, 0); + debug('Bidirectional, Client-initiated stream %d opened', bidi.id); + + assert(bidi.clientInitiated); + assert(bidi.bidirectional); + assert(!bidi.serverInitiated); + assert(!bidi.unidirectional); + + const uni = req.openStream({ halfOpen: true }); + uni.end('test'); + uni.on('close', closeHandler); + assert.strictEqual(uni.id, 2); + debug('Unidirectional, Client-initiated stream %d opened', uni.id); + + 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: + debug('Bidirectional, Server-initiated stream %d received', stream.id); + assert(!stream.unidirectional); + assert(stream.bidirectional); + stream.end(); + break; + case 3: + debug('Unidirectional, Server-initiated stream %d received', stream.id); + assert(stream.unidirectional); + assert(!stream.bidirectional); + } + stream.resume(); + stream.on('end', common.mustCall()); + stream.on('close', closeHandler); + }, 2)); + +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-simple-client-migrate.js b/test/parallel/test-quic-simple-client-migrate.js new file mode 100644 index 00000000000000..b25fd43716f8e6 --- /dev/null +++ b/test/parallel/test-quic-simple-client-migrate.js @@ -0,0 +1,108 @@ +// 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, + debug, +} = require('../common/quic'); + +const { createSocket } = require('quic'); +const { pipeline } = require('stream'); + +let req; +let client; +let client2; +const server = createSocket(); + +const options = { key, cert, ca, alpn: 'zzz' }; +const countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + req.close(); + server.close(); + client2.close(); +}); + +server.listen(options); + +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.mustCall()); + + session.openStream({ halfOpen: true }).end('Hello from the server'); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + client = createSocket({ client: options }); + client2 = createSocket({ client: options }); + + req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + client.on('close', common.mustCall()); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Complete'); + + let data = ''; + + const stream = req.openStream(); + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'Hello from the client'); + 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(); + })); + // 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(() => { + req.setSocket(client2, (err) => { + assert(!err); + client.close(); + stream.end('from the client'); + }); + }), common.platformTimeout(100)); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'Hello from the server'); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed', stream.id); + countdown.dec(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-statelessreset.js b/test/parallel/test-quic-statelessreset.js new file mode 100644 index 00000000000000..c1f4a28f108337 --- /dev/null +++ b/test/parallel/test-quic-statelessreset.js @@ -0,0 +1,76 @@ +// 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 { createSocket } = require('quic'); + +const kStatelessResetToken = + Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); + +let client; + +const server = createSocket({ statelessResetSecret: kStatelessResetToken }); + +server.listen({ key, cert, ca, alpn: 'zzz' }); + +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()); +})); + +server.on('close', common.mustCall(() => { + // Verify stats recording + assert.strictEqual(server.statelessResetCount, 1n); +})); + +server.on('ready', common.mustCall(() => { + const endpoint = server.endpoints[0]; + + client = createSocket({ client: { key, cert, ca, alpn: 'zzz' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + }); + + req.on('secure', common.mustCall(() => { + const stream = req.openStream(); + stream.end('hello'); + stream.resume(); + stream.on('close', common.mustCall()); + })); + + req.on('close', common.mustCall(() => { + assert.strictEqual(req.statelessReset, true); + server.close(); + client.close(); + })); + +})); diff --git a/test/parallel/test-quic-with-fake-udp.js b/test/parallel/test-quic-with-fake-udp.js new file mode 100644 index 00000000000000..b96fe681d6a006 --- /dev/null +++ b/test/parallel/test-quic-with-fake-udp.js @@ -0,0 +1,61 @@ +// 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 quic = require('quic'); +const { kUDPHandleForTesting } = require('internal/quic/core'); + +const { key, cert, ca } = require('../common/quic'); + +const { serverSide, clientSide } = makeUDPPair(); + +const server = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: serverSide._handle } +}); + +serverSide.afterBind(); +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = 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()); + })); + + session.on('close', common.mustNotCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: clientSide._handle }, + client: { key, cert, ca, alpn: 'meow' } + }); + clientSide.afterBind(); + + const req = 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()); +})); + +server.on('close', common.mustNotCall()); diff --git a/test/parallel/test-validators.js b/test/parallel/test-validators.js index 1c36cc17ec046b..814c201a2aafa0 100644 --- a/test/parallel/test-validators.js +++ b/test/parallel/test-validators.js @@ -3,11 +3,14 @@ require('../common'); const assert = require('assert'); +const { Buffer } = require('buffer'); const { validateArray, validateBoolean, + validateBuffer, validateInteger, validateObject, + validateString, } = require('internal/validators'); const { MAX_SAFE_INTEGER, MIN_SAFE_INTEGER } = Number; const outOfRangeError = { @@ -23,6 +26,31 @@ const invalidArgValueError = { name: 'TypeError', }; +{ + // validateString tests + validateString('hi', 'foo'); + + [1, false, [], {}, NaN, 1n, null, undefined].forEach((i) => { + assert.throws(() => validateString(i, 'foo'), invalidArgTypeError); + }); + + validateString(undefined, 'foo', { allowUndefined: true }); +} + +{ + // validateBuffer tests + validateBuffer(Buffer.from('hi'), 'foo'); + validateBuffer(Buffer.alloc(10), 'foo'); + validateBuffer(new Uint8Array(10), 'foo'); + validateBuffer(new DataView((new Uint8Array(10)).buffer)); + + [1, false, '', {}, [], 1n, null, undefined].forEach((i) => { + assert.throws(() => validateBuffer(i, 'foo'), invalidArgTypeError); + }); + + validateBuffer(undefined, 'foo', { allowUndefined: true }); +} + { // validateInteger tests. @@ -37,8 +65,15 @@ const invalidArgValueError = { }, outOfRangeError); // validateInteger() works with unsafe integers. - validateInteger(MAX_SAFE_INTEGER + 1, 'foo', 0, MAX_SAFE_INTEGER + 1); - validateInteger(MIN_SAFE_INTEGER - 1, 'foo', MIN_SAFE_INTEGER - 1); + validateInteger( + MAX_SAFE_INTEGER + 1, + 'foo', + { min: 0, max: MAX_SAFE_INTEGER + 1 }); + validateInteger( + MIN_SAFE_INTEGER - 1, + 'foo', + { min: MIN_SAFE_INTEGER - 1 }); + validateInteger(undefined, 'foo', { allowUndefined: true }); } { @@ -69,6 +104,8 @@ const invalidArgValueError = { validateBoolean(val, 'foo'); }, invalidArgTypeError); }); + + validateBoolean(undefined, 'foo', { allowUndefined: true }); } { diff --git a/test/pummel/test-heapdump-quic.js b/test/pummel/test-heapdump-quic.js new file mode 100644 index 00000000000000..68526751915988 --- /dev/null +++ b/test/pummel/test-heapdump-quic.js @@ -0,0 +1,152 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const quic = require('quic'); + +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 = quic.createSocket({ 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 = quic.createSocket({ + 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 ab1f2110a3debc..17eb781e5d4b97 100644 --- a/test/sequential/test-async-wrap-getasyncid.js +++ b/test/sequential/test-async-wrap-getasyncid.js @@ -45,6 +45,13 @@ const { getSystemErrorName } = require('util'); delete providers.STREAMPIPE; 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.JSUDPWRAP; if (!common.isMainThread) delete providers.INSPECTORJSBINDING; delete providers.KEYPAIRGENREQUEST; diff --git a/test/sequential/test-quic-preferred-address-ipv6.js b/test/sequential/test-quic-preferred-address-ipv6.js new file mode 100644 index 00000000000000..bfad714c6e9684 --- /dev/null +++ b/test/sequential/test-quic-preferred-address-ipv6.js @@ -0,0 +1,94 @@ +// 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 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 { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +let client; + +const server = createSocket({ endpoint: { type: 'udp6' } }); + +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN, preferredAddress: { + port: common.PORT, + address: '::', + type: 'udp6', +} }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + session.on('stream', common.mustCall((stream) => { + stream.end('hello world'); + stream.resume(); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + })); +})); + +server.on('ready', common.mustCall(() => { + 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]; + + client = createSocket({ endpoint: { type: 'udp6' }, client: { + key, + cert, + ca, + alpn: kALPN, + preferredAddressPolicy: 'accept' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + type: 'udp6', + }); + + req.on('ready', common.mustCall(() => { + req.on('usePreferredAddress', common.mustCall(({ address, port, type }) => { + assert.strictEqual(address, '::'); + assert.strictEqual(port, common.PORT); + assert.strictEqual(type, 'udp6'); + })); + })); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + stream.end('hello world'); + stream.resume(); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/sequential/test-quic-preferred-address.js b/test/sequential/test-quic-preferred-address.js new file mode 100644 index 00000000000000..b030494a34e534 --- /dev/null +++ b/test/sequential/test-quic-preferred-address.js @@ -0,0 +1,93 @@ +// 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 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 { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +let client; + +const server = createSocket(); + +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN, preferredAddress: { + port: common.PORT, + address: '0.0.0.0', + type: 'udp4', +} }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + session.on('stream', common.mustCall((stream) => { + stream.end('hello world'); + stream.resume(); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + })); +})); + +server.on('ready', common.mustCall(() => { + 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]; + + client = createSocket({ client: { + key, + cert, + ca, + alpn: kALPN, + preferredAddressPolicy: 'accept' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + }); + + req.on('ready', common.mustCall(() => { + req.on('usePreferredAddress', common.mustCall(({ address, port, type }) => { + assert.strictEqual(address, '0.0.0.0'); + assert.strictEqual(port, common.PORT); + assert.strictEqual(type, 'udp4'); + })); + })); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + stream.end('hello world'); + stream.resume(); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 02b59d37ffd278..cb117e42c7b2a8 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -15,10 +15,29 @@ const jsPrimitives = { const jsGlobalObjectsUrl = `${jsDocPrefix}Reference/Global_Objects/`; const jsGlobalTypes = [ - 'Array', 'ArrayBuffer', 'ArrayBufferView', 'DataView', 'Date', 'Error', - 'EvalError', 'Function', 'Map', 'Object', 'Promise', 'RangeError', - 'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', - 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', 'WebAssembly.Instance', + 'Array', + 'ArrayBuffer', + 'ArrayBufferView', + 'BigInt', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Function', + 'Map', + 'Object', + 'Promise', + 'RangeError', + 'ReferenceError', + 'RegExp', + 'Set', + 'SharedArrayBuffer', + 'SyntaxError', + 'TypeError', + 'TypedArray', + 'URIError', + 'Uint8Array', + 'WebAssembly.Instance', ]; const customTypesMap = { @@ -123,6 +142,10 @@ const customTypesMap = { 'perf_hooks.html#perf_hooks_class_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_quic_createsocket_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 51c5b4d0c6a66d..d27f018e91c328 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -88,6 +88,12 @@ addlicense "gtest" "test/cctest/gtest" "$(cat ${rootdir}/test/cctest/gtest/LICEN # 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 561eb535f2965e..dec19a86cc07a1 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -68,6 +68,7 @@ set openssl_no_asm= set doc= set extra_msbuild_args= set exit_code=0 +set experimental_quic= :next-arg if "%1"=="" goto args-done @@ -144,6 +145,7 @@ 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 @@ -196,6 +198,7 @@ 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 not exist "%~dp0deps\icu" goto no-depsicu @@ -703,7 +706,7 @@ del .used_configure_flags goto exit :help -echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/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] [vs2017/vs2019] [download-all] [lint/lint-ci/lint-js/lint-js-ci/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 vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/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] [vs2017/vs2019] [download-all] [lint/lint-ci/lint-js/lint-js-ci/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 Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build