diff options
| author | Alexander Smirnov <[email protected]> | 2024-11-29 13:32:04 +0000 | 
|---|---|---|
| committer | Alexander Smirnov <[email protected]> | 2024-11-29 13:32:04 +0000 | 
| commit | 03fc6a84cd47911dfcc873669a69da6cbcc308d1 (patch) | |
| tree | 650bf49d8971dbf6bafa9dd0488b8305a5579092 /contrib | |
| parent | 991917c6cbd9969fcb3741d24d0fa0d2e0cfaba0 (diff) | |
| parent | 56a560baa86b52c66ce622414579975930421950 (diff) | |
Merge branch 'rightlib' into mergelibs-241129-1330
Diffstat (limited to 'contrib')
227 files changed, 77735 insertions, 234 deletions
diff --git a/contrib/libs/curl/.yandex_meta/__init__.py b/contrib/libs/curl/.yandex_meta/__init__.py index f0181761de6..219d30179f2 100644 --- a/contrib/libs/curl/.yandex_meta/__init__.py +++ b/contrib/libs/curl/.yandex_meta/__init__.py @@ -83,6 +83,9 @@ def post_install(self):          # add ifaddrs implementation if needed          m.PEERDIR.add("contrib/libs/libc_compat") +        m.PEERDIR.add("contrib/libs/openssl") +        m.PEERDIR.add("contrib/libs/ngtcp2") +        m.PEERDIR.add("contrib/libs/nghttp3")          # make c-ares dependency conditional,          # but leave ADDINCL in place to make CONFIGURE work @@ -178,9 +181,9 @@ curl = GNUMakeNixProject(          "nspr.h",          "netinet/in6.h",          "nettle/", -        "nghttp3/", -        "ngtcp2.h", -        "ngtcp2/", +        "ngtcp2/ngtcp2_crypto_boringssl.h", +        "ngtcp2/ngtcp2_crypto_gnutls.h", +        "ngtcp2/ngtcp2_crypto_wolfssl.h",          "nwconio.h",          # NB: openssl/core_names.h appeared in OpenSSL 3.0, while we have only 1.1.1l at the time          "openssl/core_names.h", diff --git a/contrib/libs/curl/.yandex_meta/override.nix b/contrib/libs/curl/.yandex_meta/override.nix index c836e6b03f9..3fdf2c31e51 100644 --- a/contrib/libs/curl/.yandex_meta/override.nix +++ b/contrib/libs/curl/.yandex_meta/override.nix @@ -1,4 +1,14 @@ -pkgs: attrs: with pkgs; rec { +pkgs: attrs: with pkgs; + +let +  ngtcp2 = pkgs.ngtcp2.overrideAttrs (finalAttrs: previousAttrs: rec { +    version = "1.8.1"; +    src = fetchurl { +      url = "https://github.com/ngtcp2/ngtcp2/releases/download/v${version}/ngtcp2-${version}.tar.xz"; +      hash = "sha256-rIRKees/FT5Mzc/szt9CxXqzUruKuS7IrF00F6ec+xE="; +    }; +  }); +in rec {    version = "8.5.0";    versionWithUnderscores = "${lib.replaceStrings ["."] ["_"] version}"; @@ -13,6 +23,9 @@ pkgs: attrs: with pkgs; rec {      c-ares      zlib      zstd +    quictls +    nghttp3 +    ngtcp2    ];    configureFlags = [ @@ -20,15 +33,15 @@ pkgs: attrs: with pkgs; rec {      "--disable-ldap"      "--disable-ldaps"      "--enable-ares" -    "--with-openssl=${openssl.dev}" +    "--with-openssl"      "--with-ca-fallback"      "--with-zstd=${zstd.dev}"      "--with-brotli=${brotli.dev}" +    "--with-nghttp3" +    "--with-ngtcp2"      "--without-gnutls"      "--without-libidn2"      "--without-librtmp" -    "--without-nghttp3" -    "--without-ngtcp2"      "--without-wolfssl"    ]; diff --git a/contrib/libs/curl/lib/curl_config-linux.h b/contrib/libs/curl/lib/curl_config-linux.h index 22debd1fcda..e10307c7716 100644 --- a/contrib/libs/curl/lib/curl_config-linux.h +++ b/contrib/libs/curl/lib/curl_config-linux.h @@ -470,13 +470,13 @@  #define HAVE_NGHTTP2_NGHTTP2_H 1  /* Define to 1 if you have the <nghttp3/nghttp3.h> header file. */ -/* #undef HAVE_NGHTTP3_NGHTTP3_H */ +#define HAVE_NGHTTP3_NGHTTP3_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2_crypto.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_CRYPTO_H */ +#define HAVE_NGTCP2_NGTCP2_CRYPTO_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_H */ +#define HAVE_NGTCP2_NGTCP2_H 1  /* if you have an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE     */ @@ -886,16 +886,16 @@  #define USE_NGHTTP2 1  /* if nghttp3 is in use */ -/* #undef USE_NGHTTP3 */ +#define USE_NGHTTP3 1  /* if ngtcp2 is in use */ -/* #undef USE_NGTCP2 */ +#define USE_NGTCP2 1  /* if ngtcp2_crypto_gnutls is in use */  /* #undef USE_NGTCP2_CRYPTO_GNUTLS */  /* if ngtcp2_crypto_quictls is in use */ -/* #undef USE_NGTCP2_CRYPTO_QUICTLS */ +#define USE_NGTCP2_CRYPTO_QUICTLS 1  /* if ngtcp2_crypto_wolfssl is in use */  /* #undef USE_NGTCP2_CRYPTO_WOLFSSL */ diff --git a/contrib/libs/curl/lib/curl_config-osx.h b/contrib/libs/curl/lib/curl_config-osx.h index f6b6041cafb..599c2a40032 100644 --- a/contrib/libs/curl/lib/curl_config-osx.h +++ b/contrib/libs/curl/lib/curl_config-osx.h @@ -488,13 +488,13 @@  #define HAVE_NGHTTP2_NGHTTP2_H 1  /* Define to 1 if you have the <nghttp3/nghttp3.h> header file. */ -/* #undef HAVE_NGHTTP3_NGHTTP3_H */ +#define HAVE_NGHTTP3_NGHTTP3_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2_crypto.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_CRYPTO_H */ +#define HAVE_NGTCP2_NGTCP2_CRYPTO_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_H */ +#define HAVE_NGTCP2_NGTCP2_H 1  /* if you have an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE     */ @@ -990,14 +990,17 @@  #define USE_NGHTTP2 1  /* if nghttp3 is in use */ -/* #undef USE_NGHTTP3 */ +#define USE_NGHTTP3 1  /* if ngtcp2 is in use */ -/* #undef USE_NGTCP2 */ +#define USE_NGTCP2 1  /* if ngtcp2_crypto_openssl is in use */  /* #undef USE_NGTCP2_CRYPTO_OPENSSL */ +/* if ngtcp2_crypto_quictls is in use */ +#define USE_NGTCP2_CRYPTO_QUICTLS 1 +  /* if NSS is enabled */  /* #undef USE_NSS */ diff --git a/contrib/libs/curl/lib/curl_config-win.h b/contrib/libs/curl/lib/curl_config-win.h index 89b070743b7..c680859da97 100644 --- a/contrib/libs/curl/lib/curl_config-win.h +++ b/contrib/libs/curl/lib/curl_config-win.h @@ -474,13 +474,13 @@  #define HAVE_NGHTTP2_NGHTTP2_H 1  /* Define to 1 if you have the <nghttp3/nghttp3.h> header file. */ -/* #undef HAVE_NGHTTP3_NGHTTP3_H */ +#define HAVE_NGHTTP3_NGHTTP3_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2_crypto.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_CRYPTO_H */ +#define HAVE_NGTCP2_NGTCP2_CRYPTO_H 1  /* Define to 1 if you have the <ngtcp2/ngtcp2.h> header file. */ -/* #undef HAVE_NGTCP2_NGTCP2_H */ +#define HAVE_NGTCP2_NGTCP2_H 1  /* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */  /* #undef HAVE_OLD_GSSMIT */ @@ -1003,14 +1003,17 @@  /* #undef USE_NGHTTP2 */  /* if nghttp3 is in use */ -/* #undef USE_NGHTTP3 */ +#define USE_NGHTTP3 1  /* if ngtcp2 is in use */ -/* #undef USE_NGTCP2 */ +#define USE_NGTCP2 1  /* if ngtcp2_crypto_openssl is in use */  /* #undef USE_NGTCP2_CRYPTO_OPENSSL */ +/* if ngtcp2_crypto_quictls is in use */ +#define USE_NGTCP2_CRYPTO_QUICTLS 1 +  /* if NSS is enabled */  /* #undef USE_NSS */ diff --git a/contrib/libs/curl/lib/vquic/curl_ngtcp2.c b/contrib/libs/curl/lib/vquic/curl_ngtcp2.c index 416c8fb3ecb..2aefc7fa601 100644 --- a/contrib/libs/curl/lib/vquic/curl_ngtcp2.c +++ b/contrib/libs/curl/lib/vquic/curl_ngtcp2.c @@ -25,15 +25,15 @@  #include "curl_setup.h"  #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) -#error #include <ngtcp2/ngtcp2.h> -#error #include <nghttp3/nghttp3.h> +#include <ngtcp2/ngtcp2.h> +#include <nghttp3/nghttp3.h>  #ifdef USE_OPENSSL  #include <openssl/err.h>  #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)  #error #include <ngtcp2/ngtcp2_crypto_boringssl.h>  #else -#error #include <ngtcp2/ngtcp2_crypto_quictls.h> +#include <ngtcp2/ngtcp2_crypto_quictls.h>  #endif  #include "vtls/openssl.h"  #elif defined(USE_GNUTLS) diff --git a/contrib/libs/curl/lib/vquic/curl_ngtcp2.h b/contrib/libs/curl/lib/vquic/curl_ngtcp2.h index baa4d41527d..e65c51919d6 100644 --- a/contrib/libs/curl/lib/vquic/curl_ngtcp2.h +++ b/contrib/libs/curl/lib/vquic/curl_ngtcp2.h @@ -32,8 +32,8 @@  #include <netinet/udp.h>  #endif -#error #include <ngtcp2/ngtcp2_crypto.h> -#error #include <nghttp3/nghttp3.h> +#include <ngtcp2/ngtcp2_crypto.h> +#include <nghttp3/nghttp3.h>  #ifdef USE_OPENSSL  #include <openssl/ssl.h>  #elif defined(USE_WOLFSSL) diff --git a/contrib/libs/curl/ya.make b/contrib/libs/curl/ya.make index 5f55a73471a..f66b9ec82c7 100644 --- a/contrib/libs/curl/ya.make +++ b/contrib/libs/curl/ya.make @@ -20,6 +20,8 @@ PEERDIR(      contrib/libs/brotli/enc      contrib/libs/libc_compat      contrib/libs/nghttp2 +    contrib/libs/nghttp3 +    contrib/libs/ngtcp2      contrib/libs/openssl      contrib/libs/zlib      contrib/libs/zstd diff --git a/contrib/libs/nghttp3/.yandex_meta/__init__.py b/contrib/libs/nghttp3/.yandex_meta/__init__.py new file mode 100644 index 00000000000..f75bcde11ee --- /dev/null +++ b/contrib/libs/nghttp3/.yandex_meta/__init__.py @@ -0,0 +1,34 @@ +import os +from devtools.yamaker.project import CMakeNinjaNixProject + + +def post_install(self): +    includeFilePath = os.path.join(self.dstdir, "lib/includes/nghttp3/nghttp3.h") + +    with open(includeFilePath, "r") as file: +        filedata = file.read() + +    filedata = filedata.replace("<nghttp3/version.h>", "\"version.h\"") + +    with open(includeFilePath, "w") as file: +        file.write(filedata) + +    # Add PEERDIR to contrib/libs/nghttp2 and remove sfparse.c from SRCS +    # because nghttp2 already has sfparse.c. +    # Using sfparse.c from nghttp3 may cause a linker error in projects +    # that use nghttp2 and nghttp3 when building with the flag -all_load +    with self.yamakes["."] as m: +        m.PEERDIR.add("contrib/libs/nghttp2") +        m.SRCS.remove("lib/sfparse/sfparse.c") + + +nghttp3 = CMakeNinjaNixProject( +    license="MIT", +    flags=["-DENABLE_LIB_ONLY=1"], +    owners=["g:devtools-contrib", "g:yandex-io"], +    nixattr="nghttp3", +    arcdir="contrib/libs/nghttp3", +    post_install=post_install, +    platform_dispatchers=["config.h"], +    addincl_global={".": {"./lib/includes"}}, +) diff --git a/contrib/libs/nghttp3/.yandex_meta/devtools.copyrights.report b/contrib/libs/nghttp3/.yandex_meta/devtools.copyrights.report new file mode 100644 index 00000000000..fb16c9775b8 --- /dev/null +++ b/contrib/libs/nghttp3/.yandex_meta/devtools.copyrights.report @@ -0,0 +1,348 @@ +# File format ($ symbol means the beginning of a line): +# +# $ # this message +# $ # ======================= +# $     # comments (all commentaries should starts with some number of spaces and # symbol) +# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits +# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files +# $ # user comments +# $ +# ${action} {license id} {license text hash} +# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make +# ${all_file_action} filename +# $ # user commentaries (many lines) +# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify) +# ${action} {license spdx} {license text hash} +# $BELONGS ./ya/make/file/relative/path/3/ya.make +# ${all_file_action} filename +# $    #    user commentaries +# $ generated description +# $ ... +# +# You can modify action, all_file_action and add commentaries +# Available actions: +# keep - keep license in contrib and use in credits +# skip - skip license +# remove - remove all files with this license +# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file +# +# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory) +# We suppose that that files can contain some license info +# Available all file actions: +# FILE_IGNORE - ignore file (do nothing) +# FILE_INCLUDE - include all file data into licenses text file +# ======================= + +KEEP     COPYRIGHT_SERVICE_LABEL 0badd8dd1e0fa79a05753bbdaf7b9220 +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2018 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_ksl.c [4:5] +        lib/nghttp3_ksl.h [4:5] +        lib/nghttp3_vec.c [4:5] +        lib/nghttp3_vec.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL 1050d39974838d9e4a3ebfa807befa85 +BELONGS ya.make +    License text: +         * Copyright (c) 2008-2009 Bjoern Hoehrmann <[email protected]> +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/sfparse/sfparse.c [792:792] + +KEEP     COPYRIGHT_SERVICE_LABEL 1c2050c8044ca0184cd622399091074c +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2015 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_http.c [4:5] +        lib/nghttp3_http.h [4:5] +        lib/sfparse/sfparse.c [4:6] +        lib/sfparse/sfparse.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL 21d419b06e057fd073eece04d054aff1 +BELONGS ya.make +    License text: +        /* Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]> +         * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/sfparse/sfparse.c [789:790] + +KEEP     COPYRIGHT_SERVICE_LABEL 3782c9399af9718cb85783105b7784ae +BELONGS ya.make +    License text: +           Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +        Software Foundation, Inc. +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        INSTALL [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL 45c763ee42f330d97a4b988bd6ff227d +BELONGS ya.make +    License text: +         * Copyright (c) 2022 nghttp3 contributors +         * Copyright (c) 2022 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_balloc.c [4:5] +        lib/nghttp3_balloc.h [4:5] +        lib/nghttp3_objalloc.c [4:5] +        lib/nghttp3_objalloc.h [4:5] +        lib/nghttp3_opl.c [4:5] +        lib/nghttp3_opl.h [4:5] +        lib/nghttp3_unreachable.c [4:5] +        lib/nghttp3_unreachable.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL 58664e40c9f8424ff9aaf888f8b2814d +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2016 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/nghttp3/version.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL ac6c2cf84aa4a7b445ce3d5b1ef80906 +BELONGS ya.make +    License text: +         * Copyright (c) 2018 nghttp3 contributors +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2017 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/nghttp3/nghttp3.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL af63e11b9643762985255709a4a9bb2b +BELONGS ya.make +    License text: +        Copyright (c) 2023 sfparse contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/sfparse/COPYING [3:3] +        lib/sfparse/sfparse.c [4:6] +        lib/sfparse/sfparse.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL b9bce4a8047145ca716cda7c1ba12739 +BELONGS ya.make +    License text: +         * Copyright (c) 2018 nghttp3 contributors +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2017 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/nghttp3/nghttp3.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL bc545ae6368aee638646db325e41a41c +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2017 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/nghttp3/nghttp3.h [4:6] +        lib/nghttp3_buf.c [4:5] +        lib/nghttp3_buf.h [4:5] +        lib/nghttp3_conv.c [4:5] +        lib/nghttp3_conv.h [4:5] +        lib/nghttp3_gaptr.c [4:5] +        lib/nghttp3_gaptr.h [4:5] +        lib/nghttp3_idtr.c [4:5] +        lib/nghttp3_idtr.h [4:5] +        lib/nghttp3_macro.h [4:5] +        lib/nghttp3_map.c [4:6] +        lib/nghttp3_map.h [4:6] +        lib/nghttp3_mem.c [4:6] +        lib/nghttp3_mem.h [4:6] +        lib/nghttp3_pq.c [4:6] +        lib/nghttp3_pq.h [4:6] +        lib/nghttp3_range.c [4:5] +        lib/nghttp3_range.h [4:5] +        lib/nghttp3_ringbuf.c [4:5] +        lib/nghttp3_ringbuf.h [4:5] +        lib/nghttp3_str.c [4:6] +        lib/nghttp3_str.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL c063623f476671a53060cbaa3eda1e6b +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2013 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_frame.c [4:5] +        lib/nghttp3_frame.h [4:5] +        lib/nghttp3_qpack.c [4:5] +        lib/nghttp3_qpack.h [4:5] +        lib/nghttp3_qpack_huffman.c [4:5] +        lib/nghttp3_qpack_huffman.h [4:5] +        lib/nghttp3_qpack_huffman_data.c [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL c6a4928114aae275ca6bc55aac9b378e +BELONGS ya.make +    License text: +        Copyright (c) 2019 nghttp3 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        COPYING [3:3] +        README.rst [73:73] +        lib/includes/nghttp3/version.h [4:5] +        lib/nghttp3_buf.c [4:5] +        lib/nghttp3_buf.h [4:5] +        lib/nghttp3_conn.c [4:4] +        lib/nghttp3_conn.h [4:4] +        lib/nghttp3_conv.c [4:5] +        lib/nghttp3_conv.h [4:5] +        lib/nghttp3_debug.c [4:5] +        lib/nghttp3_debug.h [4:5] +        lib/nghttp3_err.c [4:4] +        lib/nghttp3_err.h [4:4] +        lib/nghttp3_frame.c [4:5] +        lib/nghttp3_frame.h [4:5] +        lib/nghttp3_gaptr.c [4:5] +        lib/nghttp3_gaptr.h [4:5] +        lib/nghttp3_http.c [4:5] +        lib/nghttp3_http.h [4:5] +        lib/nghttp3_idtr.c [4:5] +        lib/nghttp3_idtr.h [4:5] +        lib/nghttp3_ksl.c [4:5] +        lib/nghttp3_ksl.h [4:5] +        lib/nghttp3_macro.h [4:5] +        lib/nghttp3_map.c [4:6] +        lib/nghttp3_map.h [4:6] +        lib/nghttp3_mem.c [4:6] +        lib/nghttp3_mem.h [4:6] +        lib/nghttp3_pq.c [4:6] +        lib/nghttp3_pq.h [4:6] +        lib/nghttp3_qpack.c [4:5] +        lib/nghttp3_qpack.h [4:5] +        lib/nghttp3_qpack_huffman.c [4:5] +        lib/nghttp3_qpack_huffman.h [4:5] +        lib/nghttp3_qpack_huffman_data.c [4:5] +        lib/nghttp3_range.c [4:5] +        lib/nghttp3_range.h [4:5] +        lib/nghttp3_rcbuf.c [4:5] +        lib/nghttp3_rcbuf.h [4:5] +        lib/nghttp3_ringbuf.c [4:5] +        lib/nghttp3_ringbuf.h [4:5] +        lib/nghttp3_str.c [4:6] +        lib/nghttp3_str.h [4:6] +        lib/nghttp3_stream.c [4:4] +        lib/nghttp3_stream.h [4:4] +        lib/nghttp3_tnode.c [4:4] +        lib/nghttp3_tnode.h [4:4] +        lib/nghttp3_vec.c [4:5] +        lib/nghttp3_vec.h [4:5] +        lib/nghttp3_version.c [4:4] +        lib/sfparse/sfparse.c [4:6] +        lib/sfparse/sfparse.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL c812bfe524d8bc74ad8cf40a45902020 +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2012 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_map.c [4:6] +        lib/nghttp3_map.h [4:6] +        lib/nghttp3_pq.c [4:6] +        lib/nghttp3_pq.h [4:6] +        lib/nghttp3_str.c [4:6] +        lib/nghttp3_str.h [4:6] + +KEEP     COPYRIGHT_SERVICE_LABEL cb27ea2f3cf1cca44a3fdc2d80ec9197 +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2016 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_debug.c [4:5] +        lib/nghttp3_debug.h [4:5] +        lib/nghttp3_rcbuf.c [4:5] +        lib/nghttp3_rcbuf.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL d89f9db9c9889910b44184610d7947e1 +BELONGS ya.make +    License text: +         * Copyright (c) 2022 nghttp3 contributors +         * Copyright (c) 2022 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_balloc.c [4:5] +        lib/nghttp3_balloc.h [4:5] +        lib/nghttp3_objalloc.c [4:5] +        lib/nghttp3_objalloc.h [4:5] +        lib/nghttp3_opl.c [4:5] +        lib/nghttp3_opl.h [4:5] +        lib/nghttp3_unreachable.c [4:5] +        lib/nghttp3_unreachable.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL d8ca50c11a8cd311a31e6462c4b42555 +BELONGS ya.make +    License text: +         * Copyright (c) 2019 nghttp3 contributors +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2014 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/nghttp3_mem.c [4:6] +        lib/nghttp3_mem.h [4:6] diff --git a/contrib/libs/nghttp3/.yandex_meta/devtools.licenses.report b/contrib/libs/nghttp3/.yandex_meta/devtools.licenses.report new file mode 100644 index 00000000000..ac350881686 --- /dev/null +++ b/contrib/libs/nghttp3/.yandex_meta/devtools.licenses.report @@ -0,0 +1,167 @@ +# File format ($ symbol means the beginning of a line): +# +# $ # this message +# $ # ======================= +# $     # comments (all commentaries should starts with some number of spaces and # symbol) +# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits +# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files +# $ # user comments +# $ +# ${action} {license id} {license text hash} +# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make +# ${all_file_action} filename +# $ # user commentaries (many lines) +# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify) +# ${action} {license spdx} {license text hash} +# $BELONGS ./ya/make/file/relative/path/3/ya.make +# ${all_file_action} filename +# $    #    user commentaries +# $ generated description +# $ ... +# +# You can modify action, all_file_action and add commentaries +# Available actions: +# keep - keep license in contrib and use in credits +# skip - skip license +# remove - remove all files with this license +# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file +# +# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory) +# We suppose that that files can contain some license info +# Available all file actions: +# FILE_IGNORE - ignore file (do nothing) +# FILE_INCLUDE - include all file data into licenses text file +# ======================= + +KEEP     MIT                  14395d49cccf7dd9c262788c7c3eb609 +BELONGS ya.make +FILE_INCLUDE AUTHORS found in files: COPYING at line 19, lib/sfparse/COPYING at line 19 +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        COPYING [5:22] +        lib/sfparse/COPYING [5:22] + +KEEP     MIT                  8c6b397cbef46628bea401075e15b1d5 +BELONGS ya.make +    License text: +        The MIT License +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : REFERENCE +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        COPYING [1:1] +        lib/sfparse/COPYING [1:1] + +KEEP     MIT                  a00cb0c2918046e6e350bbe37dc87fff +BELONGS ya.make +    License text: +        License +        ------- +        The MIT License +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : NOTICE +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        README.rst [68:71] + +KEEP     MIT                  a3a8f7feced3937b87cd090ba748e24b +BELONGS ya.make +FILE_INCLUDE AUTHORS found in files: lib/includes/nghttp3/nghttp3.h at line 22, lib/includes/nghttp3/version.h at line 21, lib/nghttp3_balloc.c at line 21, lib/nghttp3_balloc.h at line 21, lib/nghttp3_buf.c at line 21, lib/nghttp3_buf.h at line 21, lib/nghttp3_conn.c at line 20, lib/nghttp3_conn.h at line 20, lib/nghttp3_conv.c at line 21, lib/nghttp3_conv.h at line 21, lib/nghttp3_debug.c at line 21, lib/nghttp3_debug.h at line 21, lib/nghttp3_err.c at line 20, lib/nghttp3_err.h at line 20, lib/nghttp3_frame.c at line 21, lib/nghttp3_frame.h at line 21, lib/nghttp3_gaptr.c at line 21, lib/nghttp3_gaptr.h at line 21, lib/nghttp3_http.c at line 21, lib/nghttp3_http.h at line 21, lib/nghttp3_idtr.c at line 21, lib/nghttp3_idtr.h at line 21, lib/nghttp3_ksl.c at line 21, lib/nghttp3_ksl.h at line 21, lib/nghttp3_macro.h at line 21, lib/nghttp3_map.c at line 22, lib/nghttp3_map.h at line 22, lib/nghttp3_mem.c at line 22, lib/nghttp3_mem.h at line 22, lib/nghttp3_objalloc.c at line 21, lib/nghttp3_objalloc.h at line 21, lib/nghttp3_opl.c at line 21, lib/nghttp3_opl.h at line 21, lib/nghttp3_pq.c at line 22, lib/nghttp3_pq.h at line 22, lib/nghttp3_qpack.c at line 21, lib/nghttp3_qpack.h at line 21, lib/nghttp3_qpack_huffman.c at line 21, lib/nghttp3_qpack_huffman.h at line 21, lib/nghttp3_qpack_huffman_data.c at line 21, lib/nghttp3_range.c at line 21, lib/nghttp3_range.h at line 21, lib/nghttp3_rcbuf.c at line 21, lib/nghttp3_rcbuf.h at line 21, lib/nghttp3_ringbuf.c at line 21, lib/nghttp3_ringbuf.h at line 21, lib/nghttp3_str.c at line 22, lib/nghttp3_str.h at line 22, lib/nghttp3_stream.c at line 20, lib/nghttp3_stream.h at line 20, lib/nghttp3_tnode.c at line 20, lib/nghttp3_tnode.h at line 20, lib/nghttp3_unreachable.c at line 21, lib/nghttp3_unreachable.h at line 21, lib/nghttp3_vec.c at line 21, lib/nghttp3_vec.h at line 21, lib/nghttp3_version.c at line 20, lib/sfparse/sfparse.c at line 22, lib/sfparse/sfparse.h at line 22 +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        lib/includes/nghttp3/nghttp3.h [8:25] +        lib/includes/nghttp3/version.h [7:24] +        lib/nghttp3_balloc.c [7:24] +        lib/nghttp3_balloc.h [7:24] +        lib/nghttp3_buf.c [7:24] +        lib/nghttp3_buf.h [7:24] +        lib/nghttp3_conn.c [6:23] +        lib/nghttp3_conn.h [6:23] +        lib/nghttp3_conv.c [7:24] +        lib/nghttp3_conv.h [7:24] +        lib/nghttp3_debug.c [7:24] +        lib/nghttp3_debug.h [7:24] +        lib/nghttp3_err.c [6:23] +        lib/nghttp3_err.h [6:23] +        lib/nghttp3_frame.c [7:24] +        lib/nghttp3_frame.h [7:24] +        lib/nghttp3_gaptr.c [7:24] +        lib/nghttp3_gaptr.h [7:24] +        lib/nghttp3_http.c [7:24] +        lib/nghttp3_http.h [7:24] +        lib/nghttp3_idtr.c [7:24] +        lib/nghttp3_idtr.h [7:24] +        lib/nghttp3_ksl.c [7:24] +        lib/nghttp3_ksl.h [7:24] +        lib/nghttp3_macro.h [7:24] +        lib/nghttp3_map.c [8:25] +        lib/nghttp3_map.h [8:25] +        lib/nghttp3_mem.c [8:25] +        lib/nghttp3_mem.h [8:25] +        lib/nghttp3_objalloc.c [7:24] +        lib/nghttp3_objalloc.h [7:24] +        lib/nghttp3_opl.c [7:24] +        lib/nghttp3_opl.h [7:24] +        lib/nghttp3_pq.c [8:25] +        lib/nghttp3_pq.h [8:25] +        lib/nghttp3_qpack.c [7:24] +        lib/nghttp3_qpack.h [7:24] +        lib/nghttp3_qpack_huffman.c [7:24] +        lib/nghttp3_qpack_huffman.h [7:24] +        lib/nghttp3_qpack_huffman_data.c [7:24] +        lib/nghttp3_range.c [7:24] +        lib/nghttp3_range.h [7:24] +        lib/nghttp3_rcbuf.c [7:24] +        lib/nghttp3_rcbuf.h [7:24] +        lib/nghttp3_ringbuf.c [7:24] +        lib/nghttp3_ringbuf.h [7:24] +        lib/nghttp3_str.c [8:25] +        lib/nghttp3_str.h [8:25] +        lib/nghttp3_stream.c [6:23] +        lib/nghttp3_stream.h [6:23] +        lib/nghttp3_tnode.c [6:23] +        lib/nghttp3_tnode.h [6:23] +        lib/nghttp3_unreachable.c [7:24] +        lib/nghttp3_unreachable.h [7:24] +        lib/nghttp3_vec.c [7:24] +        lib/nghttp3_vec.h [7:24] +        lib/nghttp3_version.c [6:23] +        lib/sfparse/sfparse.c [8:25] +        lib/sfparse/sfparse.h [8:25] + +KEEP     FSFAP                d02cc4799cbd521d2aa8c3ff19e655f6 +BELONGS ya.make +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: FSFAP +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html, https://spdx.org/licenses/FSFAP +    Files with this license: +        INSTALL [7:10] + +KEEP     MIT                  f759889bc387afe17a80ab852d7af858 +BELONGS ya.make +FILE_INCLUDE AUTHORS found in files: lib/sfparse/sfparse.c at line 808 +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        lib/sfparse/sfparse.c [794:812] diff --git a/contrib/libs/nghttp3/.yandex_meta/licenses.list.txt b/contrib/libs/nghttp3/.yandex_meta/licenses.list.txt new file mode 100644 index 00000000000..8f0e93ab063 --- /dev/null +++ b/contrib/libs/nghttp3/.yandex_meta/licenses.list.txt @@ -0,0 +1,184 @@ +====================COPYRIGHT==================== +   Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. + + +====================COPYRIGHT==================== + * Copyright (c) 2008-2009 Bjoern Hoehrmann <[email protected]> + + +====================COPYRIGHT==================== + * Copyright (c) 2018 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 ngtcp2 contributors + + +====================COPYRIGHT==================== +/* Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]> + * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + + +====================COPYRIGHT==================== +Copyright (c) 2019 nghttp3 contributors + + +====================COPYRIGHT==================== +Copyright (c) 2023 sfparse contributors + + +====================FSFAP==================== +   Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved.  This file is offered as-is, +without warranty of any kind. + + +====================File: AUTHORS==================== +Alexis La Goutte +Amir Livneh +Bruno S Marques +Bryan Call +Cheng Zhao +Daniel Bevenius +Daniel Stenberg +Deel +Dimitris Apostolou +Don +Don Olmstead +Force Charlie +James M Snell +Javier Blazquez +Li Xinwei +Marek Ludha +Nishant Nori +Ondřej Koláček +Peter Wu +Tal Regev +Tatsuhiro Tsujikawa +Tim Gates +Toni Uhlig +Valère Plantevin +Viktor Szakats +Your Name +lhuang04 + + +====================MIT==================== + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + + +====================MIT==================== + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +====================MIT==================== +License +------- + +The MIT License + + +====================MIT==================== +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +====================MIT==================== +The MIT License diff --git a/contrib/libs/nghttp3/.yandex_meta/override.nix b/contrib/libs/nghttp3/.yandex_meta/override.nix new file mode 100644 index 00000000000..5bec6bee159 --- /dev/null +++ b/contrib/libs/nghttp3/.yandex_meta/override.nix @@ -0,0 +1,15 @@ +pkgs: attrs: with pkgs; with attrs; rec { +    pname = "nghttp3"; +    version = "1.6.0"; + +    nativeBuildInputs = [ +      cmake pkg-config autoconf libtool automake +    ]; + +    buildInputs = [ ]; + +    src = fetchurl { +      url = "https://github.com/ngtcp2/nghttp3/releases/download/v${version}/nghttp3-${version}.tar.xz"; +      hash = "sha256-6qkBlUvElANNNzjvGRMN5pOH1qPaApBExg2drpF5Ko0="; +    }; +} diff --git a/contrib/libs/nghttp3/AUTHORS b/contrib/libs/nghttp3/AUTHORS new file mode 100644 index 00000000000..9bdc2113a96 --- /dev/null +++ b/contrib/libs/nghttp3/AUTHORS @@ -0,0 +1,27 @@ +Alexis La Goutte +Amir Livneh +Bruno S Marques +Bryan Call +Cheng Zhao +Daniel Bevenius +Daniel Stenberg +Deel +Dimitris Apostolou +Don +Don Olmstead +Force Charlie +James M Snell +Javier Blazquez +Li Xinwei +Marek Ludha +Nishant Nori +Ondřej Koláček +Peter Wu +Tal Regev +Tatsuhiro Tsujikawa +Tim Gates +Toni Uhlig +Valère Plantevin +Viktor Szakats +Your Name +lhuang04 diff --git a/contrib/libs/nghttp3/COPYING b/contrib/libs/nghttp3/COPYING new file mode 100644 index 00000000000..37562ea58cd --- /dev/null +++ b/contrib/libs/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/contrib/libs/nghttp3/ChangeLog b/contrib/libs/nghttp3/ChangeLog new file mode 100644 index 00000000000..84280d2a913 --- /dev/null +++ b/contrib/libs/nghttp3/ChangeLog @@ -0,0 +1,416 @@ +commit e79890583f1ba8bb4d58d7456043a7e65205b34d +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-05 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-05 + +    Bump package and library versions + +commit 54cfefe9c32387f1085766f60520bc458f7f374d +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-05 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-05 + +    Update AUTHORS + +commit 107aef87ce82eacab564b10eb1eead752bde1507 +Merge: 6e1a15d 0758422 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-03 +Commit:     GitHub <[email protected]> +CommitDate: 2024-10-03 + +    Merge pull request #267 from ngtcp2/check-header-value-avx2 +     +    Optimize nghttp3_check_header_value + +commit 6e1a15d42d8484425bf29744aaeef6631c8873f5 +Merge: ade24b7 94fce2e +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-02 +Commit:     GitHub <[email protected]> +CommitDate: 2024-10-02 + +    Merge pull request #268 from ngtcp2/cmake-remove-trailing-ws +     +    cmake: Remove trailing spaces + +commit 94fce2ec75fa9eb27144590ece9006da5541db9c +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-02 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-02 + +    cmake: Remove trailing spaces + +commit 07584226aa11a92f78072a4925d3eac81e4bd19c +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-01 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-01 + +    Optimize nghttp3_check_header_value +     +    Optimize nghttp3_check_header_value with AVX2 instructions if +    available. + +commit ade24b7f2da755485149ebca0340264b99abae8d +Merge: bdfd99f f808f05 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-01 +Commit:     GitHub <[email protected]> +CommitDate: 2024-10-01 + +    Merge pull request #266 from ngtcp2/revert-265 +     +    Revert "Optimize nghttp3_check_header_value" + +commit f808f05439ad903034e30d05ee7f0023ad12fb48 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-01 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-01 + +    Revert "Optimize nghttp3_check_header_value" +     +    This reverts commit 21c3e2ffaeda7d2f69f9718945c79b846e6443d2. + +commit bdfd99f2f41805c4a9513e523019c9e3c9b1d17d +Merge: d602bd1 21c3e2f +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-30 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-30 + +    Merge pull request #265 from ngtcp2/check-header-value-sse42 +     +    Optimize nghttp3_check_header_value + +commit d602bd1ab20b49066614779dc272996c184abce7 +Merge: 68bc239 8fd4bd8 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-30 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-30 + +    Merge pull request #264 from ngtcp2/limit-huffman-decode +     +    Do huffman encoding only when the reduction is more than 25% + +commit 21c3e2ffaeda7d2f69f9718945c79b846e6443d2 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-29 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-30 + +    Optimize nghttp3_check_header_value +     +    Optimize nghttp3_check_header_value with SSE4.2 instructions if +    available. + +commit 8fd4bd833e764f5b0d46f7b8ed2ae2708cafbfd1 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-29 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-29 + +    Do huffman encoding only when the reduction is more than 25% +     +    Do huffman encoding only when the reduction is more than 25% because +    huffman decoding is quite costly for relatively large field values. + +commit 68bc239d34c1ee43647993c722e3db8bdbc1238f +Merge: 0889971 76c42e2 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-29 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-29 + +    Merge pull request #263 from ngtcp2/qpack-opcode-switch +     +    qpack: Switch on opcode + +commit 76c42e277f6ff2062a9311e2fd11653c28f0e97b +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-27 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-27 + +    qpack: Switch on opcode + +commit 0889971045eb3f2199c3763599227a0a256a018e +Merge: 02ea26c 4ff70b1 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-25 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-25 + +    Merge pull request #262 from ngtcp2/simplify-http-on-req-resp-header +     +    Simplify http_request_on_header and http_response_on_header + +commit 4ff70b1254296800dc8387862e49b51091ce23b9 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-25 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-25 + +    Simplify http_request_on_header and http_response_on_header + +commit 02ea26c95bcd40b06734ad01e77033f9be4e30c5 +Merge: ccbc5dd 1199810 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-25 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-25 + +    Merge pull request #261 from ngtcp2/refactor-http-on-header +     +    Refactor http on header + +commit 1199810f2bf48279759e86edabfd99b2cc3b165f +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-24 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-25 + +    Add gennmchartbl.py + +commit 3e53cbbd1b1785187d896085480582906d69a00e +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-24 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-25 + +    Refactor nghttp3_http_on_header +     +    Rearrange switch statements so that token switch is done just once. +    Apply some optimisations by removing unneeded lines of code and +    conditionals. + +commit ccbc5ddd25a2bca46e120131f84aa6f59c4ba93d +Merge: 3e14e6f 8ff5d34 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-19 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-19 + +    Merge pull request #260 from ngtcp2/ispow2 +     +    ispow2: Better fallback implementation + +commit 8ff5d342097a164780e9b0388e82a7c6e78773ae +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-19 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-19 + +    ispow2: Better fallback implementation + +commit 3e14e6f020c4105d8358189e767a8c570f9612b0 +Merge: a872b6c c6669de +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-14 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-14 + +    Merge pull request #259 from ngtcp2/clang-format-fuzz +     +    clang-format files under fuzz + +commit c6669deacd8bbe39e842f8d415bf2caf9d460e22 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-14 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-14 + +    clang-format files under fuzz + +commit a872b6ca8a1fef513e8f4b2d50ebc0dd4906879e +Merge: b970523 8511829 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-14 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-14 + +    Merge pull request #258 from ngtcp2/macro-comments +     +    Consistent macro comments + +commit 85118298ee2051508809f48bba06a997b62c63e5 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-14 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-14 + +    Consistent macro comments +     +    Make macro comments consistent throughout the codebase.  Fixup +    HAVE_BE64TOH and HAVE_BSWAP_64 cmakedefine to match the semantics of +    autotools.  Fix nghttp3_ntohl64 and nghttp3_htonl64 for bigendian in +    examples. + +commit b9705237a948c2ee7ea239b2392b13db7c85ceca +Merge: 31a1543 c75f960 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-11 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-11 + +    Merge pull request #256 from ngtcp2/optimize-read-varint +     +    Optimize nghttp3_read_varint + +commit c75f960a12d4c9914987f328b3cf98661202164a +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-11 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-11 + +    Optimize nghttp3_read_varint + +commit 31a1543276ecb7fcab2b16b8ba2655e478e5d2b4 +Merge: 7473d2f a0e5fb0 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-10 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-10 + +    Merge pull request #255 from ngtcp2/map-update +     +    nghttp3_map: Port ngtcp2 changes + +commit a0e5fb0bc3f11faf493ce0e50294e38b43b37aa4 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-10 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-10 + +    nghttp3_map: Port ngtcp2 changes + +commit 7473d2f0aa6a2edd15be3e06fcecdac576a9fab0 +Merge: 99ba689 8a8d45c +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-04 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-04 + +    Merge pull request #254 from ngtcp2/amend-253 +     +    Fix potential overflow + +commit 8a8d45cb734eb8087773988cafee3a15906e7f52 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-04 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-04 + +    Fix potential overflow + +commit 99ba6896a219cdd85c29e20068c7a3b946d4c57f +Merge: 2c4a381 b8c6cca +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-04 +Commit:     GitHub <[email protected]> +CommitDate: 2024-09-04 + +    Merge pull request #253 from ngtcp2/qpack-buf-reserve +     +    Refactor qpack reserve_buf + +commit b8c6cca46afac0ca60e927377cc6831de76e76f1 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-09-04 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-09-04 + +    Refactor qpack reserve_buf +     +    Previously, reserve_buf_small and reserve_buf were doing the same +    thing.  Remove them and call reserve_buf_internal directly.  Optimize +    the code to round up to the next highest power of 2. + +commit 2c4a3816bbeab45e7c9092aecc915b54a3c88ccb +Merge: e9f34d9 f4d323a +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-31 +Commit:     GitHub <[email protected]> +CommitDate: 2024-08-31 + +    Merge pull request #252 from fcharlie/arm-intrinsics +     +    Fix ARM64 __popcnt intrinsics + +commit f4d323ad0a2d5ac8a6445cb7b06e3efa95d19f39 +Author:     Force Charlie <[email protected]> +AuthorDate: 2024-08-30 +Commit:     Force Charlie <[email protected]> +CommitDate: 2024-08-30 + +    Fix ARM64 __popcnt intrinsics + +commit e9f34d95a81b2792605f7d8c78dd30c9112d3d04 +Merge: 32a66a3 a9e8cd5 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-30 +Commit:     GitHub <[email protected]> +CommitDate: 2024-08-30 + +    Merge pull request #250 from talregev/TalR/fix_include_export +     +    Fix include export + +commit a9e8cd5d9ac82b9e4385cdfdec0d0e231b22f662 +Author:     Tal Regev <[email protected]> +AuthorDate: 2024-08-30 +Commit:     Tal Regev <[email protected]> +CommitDate: 2024-08-30 + +    Fix include export + +commit 32a66a3fe442835568072a0f0d78ad1a54bb882a +Merge: 8067396 c7bd7f3 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-28 +Commit:     GitHub <[email protected]> +CommitDate: 2024-08-28 + +    Merge pull request #248 from talregev/TalR/cmake_export +     +    Fix cmake export + +commit c7bd7f3666babf7ed4cd411120031d78c53810e8 +Author:     Tal Regev <[email protected]> +AuthorDate: 2024-08-28 +Commit:     Tal Regev <[email protected]> +CommitDate: 2024-08-28 + +    Fix cmake export + +commit 806739676fcf0ba61f94c23e4b35567f697ee013 +Merge: 3c7c7f2 96737c0 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-28 +Commit:     GitHub <[email protected]> +CommitDate: 2024-08-28 + +    Merge pull request #249 from ngtcp2/change-clang-format-options +     +    Change clang-format options + +commit 96737c00a942d0911f59fa7265954b08f0e09114 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-28 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-08-28 + +    Change clang-format options + +commit 3c7c7f29b1eba52363fe01907d796b96b38a0ee8 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-08-24 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-08-24 + +    Bump package version diff --git a/contrib/libs/nghttp3/INSTALL b/contrib/libs/nghttp3/INSTALL new file mode 100755 index 00000000000..e82fd21de2e --- /dev/null +++ b/contrib/libs/nghttp3/INSTALL @@ -0,0 +1,368 @@ +Installation Instructions +************************* + +   Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. + +   Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved.  This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + +   Briefly, the shell command './configure && make && make install' +should configure, build, and install this package.  The following +more-detailed instructions are generic; see the 'README' file for +instructions specific to this package.  Some packages provide this +'INSTALL' file but do not implement all of the features documented +below.  The lack of an optional feature in a given package is not +necessarily a bug.  More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + +   The 'configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation.  It uses +those values to create a 'Makefile' in each directory of the package. +It may also create one or more '.h' files containing system-dependent +definitions.  Finally, it creates a shell script 'config.status' that +you can run in the future to recreate the current configuration, and a +file 'config.log' containing compiler output (useful mainly for +debugging 'configure'). + +   It can also use an optional file (typically called 'config.cache' and +enabled with '--cache-file=config.cache' or simply '-C') that saves the +results of its tests to speed up reconfiguring.  Caching is disabled by +default to prevent problems with accidental use of stale cache files. + +   If you need to do unusual things to compile the package, please try +to figure out how 'configure' could check whether to do them, and mail +diffs or instructions to the address given in the 'README' so they can +be considered for the next release.  If you are using the cache, and at +some point 'config.cache' contains results you don't want to keep, you +may remove or edit it. + +   The file 'configure.ac' (or 'configure.in') is used to create +'configure' by a program called 'autoconf'.  You need 'configure.ac' if +you want to change it or regenerate 'configure' using a newer version of +'autoconf'. + +   The simplest way to compile this package is: + +  1. 'cd' to the directory containing the package's source code and type +     './configure' to configure the package for your system. + +     Running 'configure' might take a while.  While running, it prints +     some messages telling which features it is checking for. + +  2. Type 'make' to compile the package. + +  3. Optionally, type 'make check' to run any self-tests that come with +     the package, generally using the just-built uninstalled binaries. + +  4. Type 'make install' to install the programs and any data files and +     documentation.  When installing into a prefix owned by root, it is +     recommended that the package be configured and built as a regular +     user, and only the 'make install' phase executed with root +     privileges. + +  5. Optionally, type 'make installcheck' to repeat any self-tests, but +     this time using the binaries in their final installed location. +     This target does not install anything.  Running this target as a +     regular user, particularly if the prior 'make install' required +     root privileges, verifies that the installation completed +     correctly. + +  6. You can remove the program binaries and object files from the +     source code directory by typing 'make clean'.  To also remove the +     files that 'configure' created (so you can compile the package for +     a different kind of computer), type 'make distclean'.  There is +     also a 'make maintainer-clean' target, but that is intended mainly +     for the package's developers.  If you use it, you may have to get +     all sorts of other programs in order to regenerate files that came +     with the distribution. + +  7. Often, you can also type 'make uninstall' to remove the installed +     files again.  In practice, not all packages have tested that +     uninstallation works correctly, even though it is required by the +     GNU Coding Standards. + +  8. Some packages, particularly those that use Automake, provide 'make +     distcheck', which can by used by developers to test that all other +     targets like 'make install' and 'make uninstall' work correctly. +     This target is generally not run by end users. + +Compilers and Options +===================== + +   Some systems require unusual options for compilation or linking that +the 'configure' script does not know about.  Run './configure --help' +for details on some of the pertinent environment variables. + +   You can give 'configure' initial values for configuration parameters +by setting variables in the command line or in the environment.  Here is +an example: + +     ./configure CC=c99 CFLAGS=-g LIBS=-lposix + +   *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +   You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory.  To do this, you can use GNU 'make'.  'cd' to the +directory where you want the object files and executables to go and run +the 'configure' script.  'configure' automatically checks for the source +code in the directory that 'configure' is in and in '..'.  This is known +as a "VPATH" build. + +   With a non-GNU 'make', it is safer to compile the package for one +architecture at a time in the source code directory.  After you have +installed the package for one architecture, use 'make distclean' before +reconfiguring for another architecture. + +   On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple '-arch' options to the +compiler but only a single '-arch' option to the preprocessor.  Like +this: + +     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ +                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ +                 CPP="gcc -E" CXXCPP="g++ -E" + +   This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the 'lipo' tool if you have problems. + +Installation Names +================== + +   By default, 'make install' installs the package's commands under +'/usr/local/bin', include files under '/usr/local/include', etc.  You +can specify an installation prefix other than '/usr/local' by giving +'configure' the option '--prefix=PREFIX', where PREFIX must be an +absolute file name. + +   You can specify separate installation prefixes for +architecture-specific files and architecture-independent files.  If you +pass the option '--exec-prefix=PREFIX' to 'configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + +   In addition, if you use an unusual directory layout you can give +options like '--bindir=DIR' to specify different values for particular +kinds of files.  Run 'configure --help' for a list of the directories +you can set and what kinds of files go in them.  In general, the default +for these options is expressed in terms of '${prefix}', so that +specifying just '--prefix' will affect all of the other directory +specifications that were not explicitly provided. + +   The most portable way to affect installation locations is to pass the +correct locations to 'configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +'make install' command line to change installation locations without +having to reconfigure or recompile. + +   The first method involves providing an override variable for each +affected directory.  For example, 'make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +'${prefix}'.  Any directories that were specified during 'configure', +but not in terms of '${prefix}', must each be overridden at install time +for the entire installation to be relocated.  The approach of makefile +variable overrides for each directory variable is required by the GNU +Coding Standards, and ideally causes no recompilation.  However, some +platforms have known limitations with the semantics of shared libraries +that end up requiring recompilation when using this method, particularly +noticeable in packages that use GNU Libtool. + +   The second method involves providing the 'DESTDIR' variable.  For +example, 'make install DESTDIR=/alternate/directory' will prepend +'/alternate/directory' before all installation names.  The approach of +'DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters.  On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of '${prefix}' +at 'configure' time. + +Optional Features +================= + +   If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving 'configure' the +option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. + +   Some packages pay attention to '--enable-FEATURE' options to +'configure', where FEATURE indicates an optional part of the package. +They may also pay attention to '--with-PACKAGE' options, where PACKAGE +is something like 'gnu-as' or 'x' (for the X Window System).  The +'README' should mention any '--enable-' and '--with-' options that the +package recognizes. + +   For packages that use the X Window System, 'configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the 'configure' options '--x-includes=DIR' and +'--x-libraries=DIR' to specify their locations. + +   Some packages offer the ability to configure how verbose the +execution of 'make' will be.  For these packages, running './configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with 'make V=1'; while running './configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with 'make V=0'. + +Particular systems +================== + +   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU CC +is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + +     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + +   HP-UX 'make' updates targets which have the same timestamps as their +prerequisites, which makes it generally unusable when shipped generated +files such as 'configure' are involved.  Use GNU 'make' instead. + +   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its '<wchar.h>' header file.  The option '-nodtk' can be used as a +workaround.  If GNU CC is not installed, it is therefore recommended to +try + +     ./configure CC="cc" + +and if that doesn't work, try + +     ./configure CC="cc -nodtk" + +   On Solaris, don't put '/usr/ucb' early in your 'PATH'.  This +directory contains several dysfunctional programs; working variants of +these programs are available in '/usr/bin'.  So, if you need '/usr/ucb' +in your 'PATH', put it _after_ '/usr/bin'. + +   On Haiku, software installed for all users goes in '/boot/common', +not '/usr/local'.  It is recommended to use the following options: + +     ./configure --prefix=/boot/common + +Specifying the System Type +========================== + +   There may be some features 'configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on.  Usually, assuming the package is built to be run on the +_same_ architectures, 'configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +'--build=TYPE' option.  TYPE can either be a short name for the system +type, such as 'sun4', or a canonical name which has the form: + +     CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + +     OS +     KERNEL-OS + +   See the file 'config.sub' for the possible values of each field.  If +'config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + +   If you are _building_ compiler tools for cross-compiling, you should +use the option '--target=TYPE' to select the type of system they will +produce code for. + +   If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with '--host=TYPE'. + +Sharing Defaults +================ + +   If you want to set default values for 'configure' scripts to share, +you can create a site shell script called 'config.site' that gives +default values for variables like 'CC', 'cache_file', and 'prefix'. +'configure' looks for 'PREFIX/share/config.site' if it exists, then +'PREFIX/etc/config.site' if it exists.  Or, you can set the +'CONFIG_SITE' environment variable to the location of the site script. +A warning: not all 'configure' scripts look for a site script. + +Defining Variables +================== + +   Variables not defined in a site shell script can be set in the +environment passed to 'configure'.  However, some packages may run +configure again during the build, and the customized values of these +variables may be lost.  In order to avoid this problem, you should set +them in the 'configure' command line, using 'VAR=value'.  For example: + +     ./configure CC=/usr/local2/bin/gcc + +causes the specified 'gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an +Autoconf limitation.  Until the limitation is lifted, you can use this +workaround: + +     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +'configure' Invocation +====================== + +   'configure' recognizes the following options to control how it +operates. + +'--help' +'-h' +     Print a summary of all of the options to 'configure', and exit. + +'--help=short' +'--help=recursive' +     Print a summary of the options unique to this package's +     'configure', and exit.  The 'short' variant lists options used only +     in the top level, while the 'recursive' variant lists options also +     present in any nested packages. + +'--version' +'-V' +     Print the version of Autoconf used to generate the 'configure' +     script, and exit. + +'--cache-file=FILE' +     Enable the cache: use and save the results of the tests in FILE, +     traditionally 'config.cache'.  FILE defaults to '/dev/null' to +     disable caching. + +'--config-cache' +'-C' +     Alias for '--cache-file=config.cache'. + +'--quiet' +'--silent' +'-q' +     Do not print messages saying which checks are being made.  To +     suppress all normal output, redirect it to '/dev/null' (any error +     messages will still be shown). + +'--srcdir=DIR' +     Look for the package's source code in directory DIR.  Usually +     'configure' can determine that directory automatically. + +'--prefix=DIR' +     Use DIR as the installation prefix.  *note Installation Names:: for +     more details, including other options available for fine-tuning the +     installation locations. + +'--no-create' +'-n' +     Run the configure checks, but stop before creating any output +     files. + +'configure' also accepts some other, not widely useful, options.  Run +'configure --help' for more details. diff --git a/contrib/libs/nghttp3/README b/contrib/libs/nghttp3/README new file mode 100644 index 00000000000..5ccc0ea36b5 --- /dev/null +++ b/contrib/libs/nghttp3/README @@ -0,0 +1 @@ +See README.rst diff --git a/contrib/libs/nghttp3/README.rst b/contrib/libs/nghttp3/README.rst new file mode 100644 index 00000000000..950c7c59f72 --- /dev/null +++ b/contrib/libs/nghttp3/README.rst @@ -0,0 +1,73 @@ +nghttp3 +======= + +nghttp3 is an implementation of `RFC 9114 +<https://datatracker.ietf.org/doc/html/rfc9114>`_ HTTP/3 mapping over +QUIC and `RFC 9204 <https://datatracker.ietf.org/doc/html/rfc9204>`_ +QPACK in C. + +It does not depend on any particular QUIC transport implementation. + +Documentation +------------- + +`Online documentation <https://nghttp2.org/nghttp3/>`_ is available. + +Build from git +--------------- + +.. code-block:: shell + +   $ git clone https://github.com/ngtcp2/nghttp3 +   $ cd nghttp3 +   $ git submodule update --init +   $ autoreconf -i +   $ ./configure +   $ make -j$(nproc) check + +HTTP/3 +------ + +This library implements `RFC 9114 +<https://datatracker.ietf.org/doc/html/rfc9114>`_ HTTP/3.  It does not +support server push. + +The following extensions have been implemented: + +- `Extensible Prioritization Scheme for HTTP +  <https://datatracker.ietf.org/doc/html/rfc9218>`_ +- `Bootstrapping WebSockets with HTTP/3 +  <https://datatracker.ietf.org/doc/html/rfc9220>`_ + +It can also send and receive `SETTINGS_H3_DATAGRAM` from `HTTP +Datagrams and the Capsule Protocol +<https://datatracker.ietf.org/doc/html/rfc9297>`_. + +QPACK +----- + +This library implements `RFC 9204 +<https://datatracker.ietf.org/doc/html/rfc9204>`_ QPACK.  It supports +dynamic table. + +Optimizations +------------- + +This library optionally uses AVX2, if available, to optimize its +performance.  To compile with AVX2, add ``-mavx2`` to CFLAGS.  Note +that by default, CFLAGS is set to ``-g -O2``.  When specifying CFLAGS, +include them as well (e.g., ``-g -O2 -mavx2``). + +Examples +-------- + +- client: https://github.com/ngtcp2/ngtcp2/blob/main/examples/client.cc +- server: https://github.com/ngtcp2/ngtcp2/blob/main/examples/server.cc +- curl: https://github.com/curl/curl/blob/master/lib/vquic/curl_ngtcp2.c + +License +------- + +The MIT License + +Copyright (c) 2019 nghttp3 contributors diff --git a/contrib/libs/nghttp3/config-ios.h b/contrib/libs/nghttp3/config-ios.h new file mode 100644 index 00000000000..6cbc4ce682e --- /dev/null +++ b/contrib/libs/nghttp3/config-ios.h @@ -0,0 +1,2 @@ +#include "config-linux.h" +#include "config-osx.h" diff --git a/contrib/libs/nghttp3/config-linux.h b/contrib/libs/nghttp3/config-linux.h new file mode 100644 index 00000000000..8f805085262 --- /dev/null +++ b/contrib/libs/nghttp3/config-linux.h @@ -0,0 +1,31 @@ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef ssize_t */ + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <sys/endian.h> header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the <endian.h> header file. */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the <byteswap.h> header file. */ +#define HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have the `be64toh' function, otherwise 0. */ +#define HAVE_DECL_BE64TOH 1 + +/* Define to 1 if you have the `bswap_64' function, otherwise 0. */ +#define HAVE_DECL_BSWAP_64 1 + +/* Define WORDS_BIGENDIAN to 1 if target architecture is big +   endian. */ +/* #undef WORDS_BIGENDIAN */ diff --git a/contrib/libs/nghttp3/config-osx.h b/contrib/libs/nghttp3/config-osx.h new file mode 100644 index 00000000000..8dd585cd9d8 --- /dev/null +++ b/contrib/libs/nghttp3/config-osx.h @@ -0,0 +1,9 @@ +#include "config-linux.h" + +#undef HAVE_ENDIAN_H + +#undef HAVE_BYTESWAP_H + +#undef HAVE_DECL_BE64TOH + +#undef HAVE_DECL_BSWAP_64 diff --git a/contrib/libs/nghttp3/config-win.h b/contrib/libs/nghttp3/config-win.h new file mode 100644 index 00000000000..1f524ea23b4 --- /dev/null +++ b/contrib/libs/nghttp3/config-win.h @@ -0,0 +1,13 @@ +#include "config-linux.h" + +#undef HAVE_ARPA_INET_H + +#undef HAVE_UNISTD_H + +#undef HAVE_ENDIAN_H + +#undef HAVE_BYTESWAP_H + +#undef HAVE_DECL_BE64TOH + +#undef HAVE_DECL_BSWAP_64 diff --git a/contrib/libs/nghttp3/config.h b/contrib/libs/nghttp3/config.h new file mode 100644 index 00000000000..0f06551d66f --- /dev/null +++ b/contrib/libs/nghttp3/config.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined(__IOS__) +#   include "config-ios.h" +#elif defined(__APPLE__) +#   include "config-osx.h" +#elif defined(_MSC_VER) +#   include "config-win.h" +#else +#   include "config-linux.h" +#endif diff --git a/contrib/libs/nghttp3/lib/includes/nghttp3/nghttp3.h b/contrib/libs/nghttp3/lib/includes/nghttp3/nghttp3.h new file mode 100644 index 00000000000..89d57a515b5 --- /dev/null +++ b/contrib/libs/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -0,0 +1,2941 @@ +/* + * 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 /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +#include <stdlib.h> +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 +   compliant.  See compiler macros and version number in +   https://sourceforge.net/p/predef/wiki/Compilers/ */ +#  include <stdint.h> +#else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ +#  include <inttypes.h> +#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ +#include <sys/types.h> +#include <stdarg.h> +#include <stddef.h> + +#include "version.h" + +#if 1 +#  define NGHTTP3_EXTERN +#elif defined(WIN32) +#  ifdef BUILDING_NGHTTP3 +#    define NGHTTP3_EXTERN __declspec(dllexport) +#  else /* !defined(BUILDING_NGHTTP3) */ +#    define NGHTTP3_EXTERN __declspec(dllimport) +#  endif /* !defined(BUILDING_NGHTTP3) */ +#else    /* !(defined(NGHTTP3_STATICLIB) || defined(WIN32)) */ +#  ifdef BUILDING_NGHTTP3 +#    define NGHTTP3_EXTERN __attribute__((visibility("default"))) +#  else /* !defined(BUILDING_NGHTTP3) */ +#    define NGHTTP3_EXTERN +#  endif /* !defined(BUILDING_NGHTTP3) */ +#endif   /* !(defined(NGHTTP3_STATICLIB) || defined(WIN32)) */ + +#ifdef _MSC_VER +#  define NGHTTP3_ALIGN(N) __declspec(align(N)) +#else /* !defined(_MSC_VER) */ +#  define NGHTTP3_ALIGN(N) __attribute__((aligned(N))) +#endif /* !defined(_MSC_VER) */ + +/** + * @typedef + * + * :type:`nghttp3_ssize` is signed counterpart of size_t. + */ +typedef ptrdiff_t nghttp3_ssize; + +/** + * @macro + * + * :macro:`NGHTTP3_ALPN_H3` is a serialized form of HTTP/3 ALPN + * protocol identifier this library supports.  Notice that the first + * byte is the length of the following protocol identifier. + */ +#define NGHTTP3_ALPN_H3 "\x2h3" + +/** + * @macrosection + * + * nghttp3 library error codes + */ + +/** + * @macro + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` indicates that a passed + * argument is invalid. + */ +#define NGHTTP3_ERR_INVALID_ARGUMENT -101 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` indicates that a requested + * operation is not allowed at the current connection state. + */ +#define NGHTTP3_ERR_INVALID_STATE -102 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_WOULDBLOCK` indicates that an operation might + * block. + */ +#define NGHTTP3_ERR_WOULDBLOCK -103 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_STREAM_IN_USE` indicates that a stream ID is + * already in use. + */ +#define NGHTTP3_ERR_STREAM_IN_USE -104 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_HEADER` indicates that an HTTP + * header field is malformed. + */ +#define NGHTTP3_ERR_MALFORMED_HTTP_HEADER -105 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_REMOVE_HTTP_HEADER` indicates that an HTTP + * header field is discarded. + */ +#define NGHTTP3_ERR_REMOVE_HTTP_HEADER -106 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING` indicates that HTTP + * messaging is malformed. + */ +#define NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING -107 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_FATAL` indicates that a fatal error is + * occurred during QPACK processing, and it cannot be recoverable. + */ +#define NGHTTP3_ERR_QPACK_FATAL -108 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` indicates that a header + * field is too large to process. + */ +#define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -109 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` indicates that a stream is + * not found. + */ +#define NGHTTP3_ERR_STREAM_NOT_FOUND -110 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is + * closing state. + */ +#define NGHTTP3_ERR_CONN_CLOSING -111 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_STREAM_DATA_OVERFLOW` indicates that the length + * of stream data is too long, and causes overflow. + */ +#define NGHTTP3_ERR_STREAM_DATA_OVERFLOW -112 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` indicates that a + * QPACK decompression failed. + */ +#define NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED -401 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR` indicates that an + * error occurred while reading QPACK encoder stream. + */ +#define NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR -402 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` indicates that an + * error occurred while reading QPACK decoder stream. + */ +#define NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR -403 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_FRAME_UNEXPECTED` indicates that an + * unexpected HTTP/3 frame is received. + */ +#define NGHTTP3_ERR_H3_FRAME_UNEXPECTED -601 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_FRAME_ERROR` indicates that an HTTP/3 frame + * is malformed. + */ +#define NGHTTP3_ERR_H3_FRAME_ERROR -602 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_MISSING_SETTINGS` indicates that an HTTP/3 + * SETTINGS frame is missing. + */ +#define NGHTTP3_ERR_H3_MISSING_SETTINGS -603 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_INTERNAL_ERROR` indicates an internal error. + */ +#define NGHTTP3_ERR_H3_INTERNAL_ERROR -604 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` indicates that a + * critical stream is closed. + */ +#define NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM -605 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR` indicates a general + * protocol error.  This is typically a catch-all error. + */ +#define NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR -606 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_ID_ERROR` indicates that an ID related error + * occurred. + */ +#define NGHTTP3_ERR_H3_ID_ERROR -607 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_SETTINGS_ERROR` indicates that an HTTP/3 + * SETTINGS frame is malformed. + */ +#define NGHTTP3_ERR_H3_SETTINGS_ERROR -608 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_STREAM_CREATION_ERROR` indicates that a + * remote endpoint attempts to create a new stream which is not + * allowed. + */ +#define NGHTTP3_ERR_H3_STREAM_CREATION_ERROR -609 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_FATAL` indicates that error codes less than + * this value is fatal error.  When this error is returned, an + * endpoint should drop connection immediately. + */ +#define NGHTTP3_ERR_FATAL -900 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_NOMEM` indicates out of memory. + */ +#define NGHTTP3_ERR_NOMEM -901 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` indicates that user defined + * callback function failed. + */ +#define NGHTTP3_ERR_CALLBACK_FAILURE -902 + +/** + * @macrosection + * + * HTTP/3 application error code + */ + +/** + * @macro + * + * :macro:`NGHTTP3_H3_NO_ERROR` is HTTP/3 application error code + * ``H3_NO_ERROR``. + */ +#define NGHTTP3_H3_NO_ERROR 0x0100 +/** + * @macro + * + * :macro:`NGHTTP3_H3_GENERAL_PROTOCOL_ERROR` is HTTP/3 application + * error code ``H3_GENERAL_PROTOCOL_ERROR``. + */ +#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101 +/** + * @macro + * + * :macro:`NGHTTP3_H3_INTERNAL_ERROR` is HTTP/3 application error code + * ``H3_INTERNAL_ERROR``. + */ +#define NGHTTP3_H3_INTERNAL_ERROR 0x0102 +/** + * @macro + * + * :macro:`NGHTTP3_H3_STREAM_CREATION_ERROR` is HTTP/3 application + * error code ``H3_STREAM_CREATION_ERROR``. + */ +#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103 +/** + * @macro + * + * :macro:`NGHTTP3_H3_CLOSED_CRITICAL_STREAM` is HTTP/3 application + * error code ``H3_CLOSED_CRITICAL_STREAM``. + */ +#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104 +/** + * @macro + * + * :macro:`NGHTTP3_H3_FRAME_UNEXPECTED` is HTTP/3 application error + * code ``H3_FRAME_UNEXPECTED``. + */ +#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105 +/** + * @macro + * + * :macro:`NGHTTP3_H3_FRAME_ERROR` is HTTP/3 application error code + * ``H3_FRAME_ERROR``. + */ +#define NGHTTP3_H3_FRAME_ERROR 0x0106 +/** + * @macro + * + * :macro:`NGHTTP3_H3_EXCESSIVE_LOAD` is HTTP/3 application error code + * ``H3_EXCESSIVE_LOAD``. + */ +#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107 +/** + * @macro + * + * :macro:`NGHTTP3_H3_ID_ERROR` is HTTP/3 application error code + * ``H3_ID_ERROR``. + */ +#define NGHTTP3_H3_ID_ERROR 0x0108 +/** + * @macro + * + * :macro:`NGHTTP3_H3_SETTINGS_ERROR` is HTTP/3 application error code + * ``H3_SETTINGS_ERROR``. + */ +#define NGHTTP3_H3_SETTINGS_ERROR 0x0109 +/** + * @macro + * + * :macro:`NGHTTP3_H3_MISSING_SETTINGS` is HTTP/3 application error + * code ``H3_MISSING_SETTINGS``. + */ +#define NGHTTP3_H3_MISSING_SETTINGS 0x010a +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_REJECTED` is HTTP/3 application error + * code ``H3_REQUEST_REJECTED``. + */ +#define NGHTTP3_H3_REQUEST_REJECTED 0x010b +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_CANCELLED` is HTTP/3 application error + * code ``H3_REQUEST_CANCELLED``. + */ +#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_INCOMPLETE` is HTTP/3 application error + * code ``H3_REQUEST_INCOMPLETE``. + */ +#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d +/** + * @macro + * + * :macro:`NGHTTP3_H3_MESSAGE_ERROR` is HTTP/3 application error code + * ``H3_MESSAGE_ERROR``. + */ +#define NGHTTP3_H3_MESSAGE_ERROR 0x010e +/** + * @macro + * + * :macro:`NGHTTP3_H3_CONNECT_ERROR` is HTTP/3 application error code + * ``H3_CONNECT_ERROR``. + */ +#define NGHTTP3_H3_CONNECT_ERROR 0x010f +/** + * @macro + * + * :macro:`NGHTTP3_H3_VERSION_FALLBACK` is HTTP/3 application error + * code ``H3_VERSION_FALLBACK``. + */ +#define NGHTTP3_H3_VERSION_FALLBACK 0x0110 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECOMPRESSION_FAILED` is HTTP/3 application + * error code ``QPACK_DECOMPRESSION_FAILED``. + */ +#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_ENCODER_STREAM_ERROR` is HTTP/3 application + * error code ``QPACK_ENCODER_STREAM_ERROR``. + */ +#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODER_STREAM_ERROR` is HTTP/3 application + * error code ``QPACK_DECODER_STREAM_ERROR``. + */ +#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202 + +/** + * @functypedef + * + * :type:`nghttp3_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`.  The |user_data| is the + * :member:`nghttp3_mem.user_data`. + */ +typedef void *(*nghttp3_malloc)(size_t size, void *user_data); + +/** + * @functypedef + * + * :type:`nghttp3_free` is a custom memory allocator to replace + * :manpage:`free(3)`.  The |user_data| is the + * :member:`nghttp3_mem.user_data`. + */ +typedef void (*nghttp3_free)(void *ptr, void *user_data); + +/** + * @functypedef + * + * :type:`nghttp3_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`.  The |user_data| is the + * :member:`nghttp3_mem.user_data`. + */ +typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *user_data); + +/** + * @functypedef + * + * :type:`nghttp3_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`.  The |user_data| is the + * :member:`nghttp3_mem.user_data`. + */ +typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *user_data); + +/** + * @struct + * + * :type:`nghttp3_mem` is a custom memory allocator functions and user + * defined pointer.  The :member:`user_data` field 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 :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: + * + *     void *my_malloc_cb(size_t size, void *user_data) { + *       (void)user_data; + *       return my_malloc(size); + *     } + * + *     void my_free_cb(void *ptr, void *user_data) { + *       (void)user_data; + *       my_free(ptr); + *     } + * + *     void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + *       (void)user_data; + *       return my_calloc(nmemb, size); + *     } + * + *     void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + *       (void)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 nghttp3_mem { +  /** +   * :member:`user_data` is an arbitrary user supplied data.  This is +   * passed to each allocator function. +   */ +  void *user_data; +  /** +   * :member:`malloc` is a custom allocator function to replace +   * :manpage:`malloc(3)`. +   */ +  nghttp3_malloc malloc; +  /** +   * :member:`free` is a custom allocator function to replace +   * :manpage:`free(3)`. +   */ +  nghttp3_free free; +  /** +   * :member:`calloc` is a custom allocator function to replace +   * :manpage:`calloc(3)`. +   */ +  nghttp3_calloc calloc; +  /** +   * :member:`realloc` is a custom allocator function to replace +   * :manpage:`realloc(3)`. +   */ +  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 + * + * :type:`nghttp3_vec` is ``struct iovec`` compatible structure to + * reference arbitrary array of bytes. + */ +typedef struct nghttp3_vec { +  /** +   * :member:`base` points to the data. +   */ +  uint8_t *base; +  /** +   * :member:`len` is the number of bytes which the buffer pointed by +   * :member:`base` contains. +   */ +  size_t len; +} nghttp3_vec; + +/** + * @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 nghttp3_buf { +  /** +   * :member:`begin` points to the beginning of the buffer. +   */ +  uint8_t *begin; +  /** +   * :member:`end` points to the one beyond of the last byte of the +   * buffer +   */ +  uint8_t *end; +  /** +   * :member:`pos` points to the start of data.  Typically, this +   * points to the address that next data should be read.  Initially, +   * it points to :member:`begin`. +   */ +  uint8_t *pos; +  /** +   * :member:`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 :member:`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.  :member:`buf->begin <nghttp3_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 + * :member:`buf->end <nghttp3_buf.end>` - :member:`buf->last + * <nghttp3_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 :member:`buf->last <nghttp3_buf.last>` - + * :member:`buf->pos <nghttp3_buf.pos>`. + */ +NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_reset` sets :member:`buf->pos <nghttp3_buf.pos>` and + * :member:`buf->last <nghttp3_buf.last>` to :member:`buf->begin + * <nghttp3_buf.begin>`. + */ +NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); + +/** + * @macrosection + * + * Flags for HTTP field name/value pair + */ + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. + */ +#define NGHTTP3_NV_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this name/value + * pair must not be indexed.  Other implementation calls this bit as + * "sensitive". + */ +#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01u + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by application. + * If this flag is set, the library does not make a copy of field + * name.  This could improve performance. + */ +#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02u + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by + * application.  If this flag is set, the library does not make a copy + * of field value.  This could improve performance. + */ +#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04u + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_TRY_INDEX` gives a hint to QPACK encoder to + * index an HTTP field which is not indexed by default.  This is just + * a hint, and QPACK encoder might not encode the field in various + * reasons. + */ +#define NGHTTP3_NV_FLAG_TRY_INDEX 0x08u + +/** + * @struct + * + * :type:`nghttp3_nv` is the name/value pair, which mainly used to + * represent HTTP fields. + */ +typedef struct nghttp3_nv { +  /** +   * :member:`name` is the HTTP field name. +   */ +  const uint8_t *name; +  /** +   * :member:`value` is the HTTP field value. +   */ +  const uint8_t *value; +  /** +   * :member:`namelen` is the length of the |name|, excluding +   * terminating NULL. +   */ +  size_t namelen; +  /** +   * :member:`valuelen` is the length of the |value|, excluding +   * terminating NULL. +   */ +  size_t valuelen; +  /** +   * :member:`flags` is bitwise OR of one or more of +   * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`. +   */ +  uint8_t flags; +} nghttp3_nv; + +/* Generated by mkstatichdtbl.py */ +/** + * @enum + * + * :type:`nghttp3_qpack_token` defines HTTP field name tokens to + * identify field name quickly.  It appears in + * :member:`nghttp3_qpack_nv.token`. + */ +typedef enum nghttp3_qpack_token { +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__AUTHORITY` is a token for +   * ``:authority``. +   */ +  NGHTTP3_QPACK_TOKEN__AUTHORITY = 0, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__PATH` is a token for ``:path``. +   */ +  NGHTTP3_QPACK_TOKEN__PATH = 8, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_AGE` is a token for ``age``. +   */ +  NGHTTP3_QPACK_TOKEN_AGE = 43, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION` is a token for +   * ``content-disposition``. +   */ +  NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH` is a token for +   * ``content-length``. +   */ +  NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_COOKIE` is a token for ``cookie``. +   */ +  NGHTTP3_QPACK_TOKEN_COOKIE = 68, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_DATE` is a token for ``date``. +   */ +  NGHTTP3_QPACK_TOKEN_DATE = 69, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ETAG` is a token for ``etag``. +   */ +  NGHTTP3_QPACK_TOKEN_ETAG = 71, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE` is a token for +   * ``if-modified-since``. +   */ +  NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH` is a token for +   * ``if-none-match``. +   */ +  NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_LAST_MODIFIED` is a token for +   * ``last-modified``. +   */ +  NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_LINK` is a token for ``link``. +   */ +  NGHTTP3_QPACK_TOKEN_LINK = 78, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_LOCATION` is a token for ``location``. +   */ +  NGHTTP3_QPACK_TOKEN_LOCATION = 79, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_REFERER` is a token for ``referer``. +   */ +  NGHTTP3_QPACK_TOKEN_REFERER = 83, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_SET_COOKIE` is a token for +   * ``set-cookie``. +   */ +  NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__METHOD` is a token for ``:method``. +   */ +  NGHTTP3_QPACK_TOKEN__METHOD = 1, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__SCHEME` is a token for ``:scheme``. +   */ +  NGHTTP3_QPACK_TOKEN__SCHEME = 9, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__STATUS` is a token for ``:status``. +   */ +  NGHTTP3_QPACK_TOKEN__STATUS = 11, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT` is a token for ``accept``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCEPT = 25, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING` is a token for +   * ``accept-encoding``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES` is a token for +   * ``accept-ranges``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS` is a +   * token for ``access-control-allow-headers``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN` is a +   * token for ``access-control-allow-origin``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CACHE_CONTROL` is a token for +   * ``cache-control``. +   */ +  NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING` is a token for +   * ``content-encoding``. +   */ +  NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_TYPE` is a token for +   * ``content-type``. +   */ +  NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_RANGE` is a token for ``range``. +   */ +  NGHTTP3_QPACK_TOKEN_RANGE = 82, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY` is a token +   * for ``strict-transport-security``. +   */ +  NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_VARY` is a token for ``vary``. +   */ +  NGHTTP3_QPACK_TOKEN_VARY = 92, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS` is a token for +   * ``x-content-type-options``. +   */ +  NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION` is a token for +   * ``x-xss-protection``. +   */ +  NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE` is a token for +   * ``accept-language``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS` is a +   * token for ``access-control-allow-credentials``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS` is a +   * token for ``access-control-allow-methods``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS` is a +   * token for ``access-control-expose-headers``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS` is a +   * token for ``access-control-request-headers``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD` is a +   * token for ``access-control-request-method``. +   */ +  NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ALT_SVC` is a token for ``alt-svc``. +   */ +  NGHTTP3_QPACK_TOKEN_ALT_SVC = 44, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_AUTHORIZATION` is a token for +   * ``authorization``. +   */ +  NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY` is a token +   * for ``content-security-policy``. +   */ +  NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_EARLY_DATA` is a token for +   * ``early-data``. +   */ +  NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_EXPECT_CT` is a token for +   * ``expect-ct``. +   */ +  NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_FORWARDED` is a token for +   * ``forwarded``. +   */ +  NGHTTP3_QPACK_TOKEN_FORWARDED = 73, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_IF_RANGE` is a token for ``if-range``. +   */ +  NGHTTP3_QPACK_TOKEN_IF_RANGE = 76, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_ORIGIN` is a token for ``origin``. +   */ +  NGHTTP3_QPACK_TOKEN_ORIGIN = 80, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_PURPOSE` is a token for ``purpose``. +   */ +  NGHTTP3_QPACK_TOKEN_PURPOSE = 81, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_SERVER` is a token for ``server``. +   */ +  NGHTTP3_QPACK_TOKEN_SERVER = 84, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN` is a token for +   * ``timing-allow-origin``. +   */ +  NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS` is a token +   * for ``upgrade-insecure-requests``. +   */ +  NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_USER_AGENT` is a token for +   * ``user-agent``. +   */ +  NGHTTP3_QPACK_TOKEN_USER_AGENT = 91, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR` is a token for +   * ``x-forwarded-for``. +   */ +  NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS` is a token for +   * ``x-frame-options``. +   */ +  NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96, + +  /* Additional HTTP fields for HTTP messaging validation */ + +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_HOST` is a token for ``host``. +   */ +  NGHTTP3_QPACK_TOKEN_HOST = 1000, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_CONNECTION` is a token for +   * ``connection``. +   */ +  NGHTTP3_QPACK_TOKEN_CONNECTION, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_KEEP_ALIVE` is a token for +   * ``keep-alive``. +   */ +  NGHTTP3_QPACK_TOKEN_KEEP_ALIVE, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION` is a token for +   * ``proxy-connection``. +   */ +  NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING` is a token for +   * ``transfer-encoding``. +   */ +  NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE` is a token for ``upgrade``. +   */ +  NGHTTP3_QPACK_TOKEN_UPGRADE, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_TE` is a token for ``te``. +   */ +  NGHTTP3_QPACK_TOKEN_TE, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN__PROTOCOL` is a token for +   * ``:protocol``. +   */ +  NGHTTP3_QPACK_TOKEN__PROTOCOL, +  /** +   * :enum:`NGHTTP3_QPACK_TOKEN_PRIORITY` is a token for ``priority``. +   */ +  NGHTTP3_QPACK_TOKEN_PRIORITY +} nghttp3_qpack_token; + +/** + * @struct + * + * :type:`nghttp3_qpack_nv` represents HTTP 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. + */ +typedef struct nghttp3_qpack_nv { +  /** +   * :member:`name` is the buffer containing HTTP field name. +   * NULL-termination is guaranteed. +   */ +  nghttp3_rcbuf *name; +  /** +   * :member:`value` is the buffer containing HTTP field value. +   * NULL-termination is guaranteed. +   */ +  nghttp3_rcbuf *value; +  /** +   * :member:`token` is :type:`nghttp3_qpack_token` value of +   * :member:`name`.  It could be -1 if we have no token for that HTTP +   * field name. +   */ +  int32_t token; +  /** +   * :member:`flags` is a bitwise OR of one or more of +   * :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`. +   */ +  uint8_t flags; +} nghttp3_qpack_nv; + +/** + * @struct + * + * :type:`nghttp3_qpack_encoder` is QPACK encoder.  The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; + +/** + * @function + * + * `nghttp3_qpack_encoder_new` initializes QPACK encoder.  |pencoder| + * must be non-NULL pointer.  |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity.  |mem| is a memory allocator. + * This function allocates memory for :type:`nghttp3_qpack_encoder` + * itself, and assigns its pointer to |*pencoder| if it succeeds. + * + * The maximum dynamic table capacity is still 0.  In order to change + * the maximum dynamic table capacity, call + * `nghttp3_qpack_encoder_set_max_dtable_capacity`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, +                                             size_t hard_max_dtable_capacity, +                                             const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|. + * This function also frees memory pointed by |encoder| itself.  This + * function does nothing if |encoder| is NULL. + */ +NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder); + +/** + * @function + * + * `nghttp3_qpack_encoder_encode` encodes the list of HTTP fields + * |nva|.  |nvlen| is the length of |nva|.  |stream_id| is the + * identifier of the stream which these HTTP fields belong to.  This + * function writes field section prefix, encoded HTTP field section, + * and encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. + * Each :member:`nghttp3_buf.last` 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) <nghttp3_buf_init>`.  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 :member:`nghttp3_buf.begin` 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: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory + * :macro:`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: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + *     |encoder| is in unrecoverable error state, and cannot be used + *     anymore. + * :macro:`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_capacity` sets max dynamic + * table capacity to |max_dtable_capacity|.  If |max_dtable_capacity| + * is larger than ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_encoder_new`, it is truncated to the latter. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_set_max_dtable_capacity(nghttp3_qpack_encoder *encoder, +                                              size_t max_dtable_capacity); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_max_blocked_streams` sets the number of + * streams which can be blocked to |max_blocked_streams|. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_set_max_blocked_streams(nghttp3_qpack_encoder *encoder, +                                              size_t max_blocked_streams); + +/** + * @function + * + * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all + * encoded HTTP field sections 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_get_num_blocked_streams` returns the number + * of streams which are potentially blocked at decoder side. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_encoder_get_num_blocked_streams(nghttp3_qpack_encoder *encoder); + +/** + * @struct + * + * :type:`nghttp3_qpack_stream_context` is a decoder context for an + * individual stream.  Its state is per HTTP field section.  In order + * to reuse this object for another HTTP field section, call + * `nghttp3_qpack_stream_context_reset`.  The details of this + * structure are intentionally hidden from the public API. + */ +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: + * + * :macro:`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.  This + * function does nothing if |sctx| is NULL. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx); + +/** + * @function + * + * `nghttp3_qpack_stream_context_get_ricnt` returns required insert + * count. + */ +NGHTTP3_EXTERN uint64_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx); + +/** + * @function + * + * `nghttp3_qpack_stream_context_reset` resets the state of |sctx|. + * Then it can be reused for decoding an another HTTP field section in + * the same stream. + */ +NGHTTP3_EXTERN +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx); + +/** + * @struct + * + * :type:`nghttp3_qpack_decoder` is QPACK decoder.  The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; + +/** + * @function + * + * `nghttp3_qpack_decoder_new` initializes QPACK decoder.  |pdecoder| + * must be non-NULL pointer.  |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity.  |max_blocked_streams| 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: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, +                                             size_t hard_max_dtable_capacity, +                                             size_t max_blocked_streams, +                                             const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|. + * This function frees memory pointed by |decoder| itself.  This + * function does nothing if |decoder| is NULL. + */ +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: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + *     |decoder| is in unrecoverable error state, and cannot be used + *     anymore. + * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM` + *     Could not interpret encoder stream instruction. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder( +  nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen); + +/** + * @function + * + * `nghttp3_qpack_decoder_get_icnt` returns insert count. + */ +NGHTTP3_EXTERN uint64_t +nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); + +/** + * @macrosection + * + * Flags for QPACK decoder + */ + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that an HTTP + * field is successfully decoded. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01u + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that an entire + * HTTP field section has been decoded. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02u + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding + * has been blocked. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04u + +/** + * @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 created by + * `nghttp3_qpack_stream_context_new`.  |*pflags| must be non-NULL + * pointer.  |nv| must be non-NULL pointer. + * + * If this function succeeds, it assigns flags to |*pflags|.  If + * |*pflags| has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a + * decoded HTTP field is assigned to |nv|.  If |*pflags| has + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, an entire HTTP field + * section has been successfully decoded.  If |*pflags| has + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked + * due to required insert count. + * + * When an HTTP field is decoded, an application receives it in |nv|. + * :member:`nv->name <nghttp3_qpack_nv.name>` and :member:`nv->value + * <nghttp3_qpack_nv.value>` are reference counted buffer, and their + * reference counts are already incremented for application use. + * Therefore, when application finishes processing |nv|, it must call + * `nghttp3_rcbuf_decref(nv->name) <nghttp3_rcbuf_decref>` and + * `nghttp3_rcbuf_decref(nv->value) <nghttp3_rcbuf_decref>`, or memory + * leak might occur.  These :type:`nghttp3_rcbuf` objects hold the + * pointer to :type:`nghttp3_mem` that is passed to + * `nghttp3_qpack_decoder_new` (or either `nghttp3_conn_client_new` or + * `nghttp3_conn_server_new` if it is used indirectly).  As long as + * these objects are alive, the pointed :type:`nghttp3_mem` object + * must be available.  Otherwise, `nghttp3_rcbuf_decref` will cause + * undefined behavior. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + *     |decoder| is in unrecoverable error state, and cannot be used + *     anymore. + * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` + *     Could not interpret field line representations. + * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` + *     HTTP 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_buf_left>` >= + * `nghttp3_qpack_decoder_get_decoder_streamlen(decoder) + * <nghttp3_qpack_decoder_get_decoder_streamlen>`. + */ +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 that is currently pending. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder); + +/** + * @function + * + * `nghttp3_qpack_decoder_cancel_stream` cancels HTTP field section + * decoding for stream denoted by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`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_max_dtable_capacity` sets + * |max_dtable_capacity| as maximum dynamic table size. + * |max_dtable_capacity| must be equal to, or smaller than + * ``hard_max_dtable_capacity`` parameter of + * `nghttp3_qpack_decoder_new`.  Normally, the maximum capacity is + * communicated in encoder stream.  This function is provided for + * debugging and testing purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |max_dtable_capacity| exceeds the upper bound of the dynamic + *     table capacity. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_decoder_set_max_dtable_capacity(nghttp3_qpack_decoder *decoder, +                                              size_t max_dtable_capacity); + +/** + * @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|. + */ +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 :manpage:`vfprintf(3)`. + * + * The debug output is only enabled if the library is built with + * :macro:`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 :macro:`DEBUGBUILD` macro + * defined.  If a callback function is not set by this function, debug + * log is written into standard error output. + * + * For builds without :macro:`DEBUGBUILD` macro defined, this function + * is noop. + * + * Note that building with :macro:`DEBUGBUILD` may cause significant + * performance penalty to libnghttp3 because of extra processing.  It + * should be used for debugging purpose only. + * + * .. Warning:: + * + *   Building with :macro:`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); + +/** + * @macrosection + * + * Shutdown related constants + */ + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID` specifies stream ID sent + * by a server when it initiates graceful shutdown of the connection + * via `nghttp3_conn_submit_shutdown_notice`. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID ((1ull << 62) - 4) + +/** + * @macro + * + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID` specifies push ID sent by + * a client when it initiates graceful shutdown of the connection via + * `nghttp3_conn_submit_shutdown_notice`.  Note that libnghttp3 does + * not implement HTTP/3 Server Push. + */ +#define NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID ((1ull << 62) - 1) + +/** + * @struct + * + * :type:`nghttp3_conn` represents a single HTTP/3 connection.  The + * details of this structure are intentionally hidden from the public + * API. + */ +typedef struct nghttp3_conn nghttp3_conn; + +#define NGHTTP3_SETTINGS_V1 1 +#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V1 + +/** + * @struct + * + * :type:`nghttp3_settings` defines HTTP/3 settings. + */ +typedef struct nghttp3_settings { +  /** +   * :member:`max_field_section_size` specifies the maximum header +   * section (block) size. +   */ +  uint64_t max_field_section_size; +  /** +   * :member:`qpack_max_dtable_capacity` is the maximum size of QPACK +   * dynamic table. +   */ +  size_t qpack_max_dtable_capacity; +  /** +   * :member:`qpack_encoder_max_dtable_capacity` is the upper bound of +   * QPACK dynamic table capacity that the QPACK encoder is willing to +   * use.  The effective maximum dynamic table capacity is the minimum +   * of this field and the value of the received +   * SETTINGS_QPACK_MAX_TABLE_CAPACITY.  If this field is set to 0, +   * the encoder does not use the dynamic table. +   * +   * When :type:`nghttp3_settings` is passed to +   * :member:`nghttp3_callbacks.recv_settings` callback, this field +   * should be ignored. +   */ +  size_t qpack_encoder_max_dtable_capacity; +  /** +   * :member:`qpack_blocked_streams` is the maximum number of streams +   * which can be blocked while they are being decoded. +   */ +  size_t qpack_blocked_streams; +  /** +   * :member:`enable_connect_protocol`, if set to nonzero, enables +   * Extended CONNECT Method (see :rfc:`9220`).  Client ignores this +   * field. +   */ +  uint8_t enable_connect_protocol; +  /** +   * :member:`h3_datagram`, if set to nonzero, enables HTTP/3 +   * Datagrams (see :rfc:`9297`). +   */ +  uint8_t h3_datagram; +} nghttp3_settings; + +/** + * @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 :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, +                                         uint64_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.  QUIC + * application error code |app_error_code| indicates the reason of + * this closure. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_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 + * (say, increasing by |datalen| bytes). + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_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 due to synchronization between + * streams.  The application is responsible for increasing flow + * control credit by |consumed| bytes. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id, +                                        size_t consumed, void *conn_user_data, +                                        void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_begin_headers` is a callback function which is + * invoked when an incoming HTTP field section is started on a stream + * denoted by |stream_id|.  Each HTTP field is passed to application + * by :type:`nghttp3_recv_header` callback.  And then + * :type:`nghttp3_end_headers` is called when a whole HTTP field + * section is processed. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, +                                     void *conn_user_data, +                                     void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_header` is a callback function which is invoked + * when an HTTP field is received on a stream denoted by |stream_id|. + * |name| contains a field name, and |value| contains a field value. + * |token| is one of token defined in :type:`nghttp3_qpack_token` or + * -1 if no token is defined for |name|.  |flags| is bitwise OR of + * zero or more of :macro:`NGHTTP3_NV_FLAG_* <NGHTTP3_NV_FLAG_NONE>`. + * + * The buffers for |name| and |value| are reference counted. If + * application needs to keep them, increment the reference count with + * `nghttp3_rcbuf_incref`.  When they are no longer used, call + * `nghttp3_rcbuf_decref`. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_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); + +/** + * @functypedef + * + * :type:`nghttp3_end_headers` is a callback function which is invoked + * when an incoming HTTP field section has ended. + * + * If the stream ends with this HTTP field section, |fin| is set to + * nonzero. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, +                                   int fin, 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 :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, +                                  void *conn_user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_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|.  QUIC application error code + * |app_error_code| indicates the reason for this action. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_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_reset_stream` is a callback function which is + * invoked when the library asks application to reset stream + * identified by |stream_id|.  QUIC application error code + * |app_error_code| indicates the reason for this action. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, +                                    uint64_t app_error_code, +                                    void *conn_user_data, +                                    void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_shutdown` is a callback function which is invoked + * when a shutdown is initiated by the remote endpoint. For client, + * |id| contains a stream ID of a client initiated stream, for server, + * it contains a push ID. All client streams with stream ID, or pushes + * with push ID equal to, or larger than |ID| are guaranteed to not be + * processed by the remote endpoint.  Note that libnghttp3 does not + * implement Server Push. + * + * Parameter |id| for client can contain a special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID`, and for server it can + * contain special value + * :macro:`NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID`. These values signal + * request for graceful shutdown of the connection, triggered by + * remote endpoint's invocation of + * `nghttp3_conn_submit_shutdown_notice`. + * + * It is possible that this callback is invoked multiple times on a + * single connection, however the |id| can only stay the same or + * decrease, never increase. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_shutdown)(nghttp3_conn *conn, int64_t id, +                                void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_settings` is a callback function which is + * invoked when SETTINGS frame is received.  |settings| is a received + * remote HTTP/3 settings. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately.  Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_settings)(nghttp3_conn *conn, +                                     const nghttp3_settings *settings, +                                     void *conn_user_data); + +#define NGHTTP3_CALLBACKS_V1 1 +#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V1 + +/** + * @struct + * + * :type:`nghttp3_callbacks` holds a set of callback functions. + */ +typedef struct nghttp3_callbacks { +  /** +   * :member:`acked_stream_data` is a callback function which is +   * invoked when data sent on a particular stream have been +   * acknowledged by a remote endpoint. +   */ +  nghttp3_acked_stream_data acked_stream_data; +  /** +   * :member:`stream_close` is a callback function which is invoked +   * when a particular stream has closed. +   */ +  nghttp3_stream_close stream_close; +  /** +   * :member:`recv_data` is a callback function which is invoked when +   * stream data is received. +   */ +  nghttp3_recv_data recv_data; +  /** +   * :member:`deferred_consume` is a callback function which is +   * invoked when the library consumed data for a particular stream +   * which had been blocked for synchronization between streams. +   */ +  nghttp3_deferred_consume deferred_consume; +  /** +   * :member:`begin_headers` is a callback function which is invoked +   * when an HTTP header field section has started on a particular +   * stream. +   */ +  nghttp3_begin_headers begin_headers; +  /** +   * :member:`recv_header` is a callback function which is invoked +   * when a single HTTP header field is received on a particular +   * stream. +   */ +  nghttp3_recv_header recv_header; +  /** +   * :member:`end_headers` is a callback function which is invoked +   * when an HTTP header field section has ended on a particular +   * stream. +   */ +  nghttp3_end_headers end_headers; +  /** +   * :member:`begin_trailers` is a callback function which is invoked +   * when an HTTP trailer field section has started on a particular +   * stream. +   */ +  nghttp3_begin_headers begin_trailers; +  /** +   * :member:`recv_trailer` is a callback function which is invoked +   * when a single HTTP trailer field is received on a particular +   * stream. +   */ +  nghttp3_recv_header recv_trailer; +  /** +   * :member:`end_trailers` is a callback function which is invoked +   * when an HTTP trailer field section has ended on a particular +   * stream. +   */ +  nghttp3_end_headers end_trailers; +  /** +   * :member:`stop_sending` is a callback function which is invoked +   * when the library asks application to send STOP_SENDING to a +   * particular stream. +   */ +  nghttp3_stop_sending stop_sending; +  /** +   * :member:`end_stream` is a callback function which is invoked when +   * a receiving side of stream has been closed. +   */ +  nghttp3_end_stream end_stream; +  /** +   * :member:`reset_stream` is a callback function which is invoked +   * when the library asks application to reset stream (by sending +   * RESET_STREAM). +   */ +  nghttp3_reset_stream reset_stream; +  /** +   * :member:`shutdown` is a callback function which is invoked when +   * the remote endpoint has signalled initiation of connection +   * shutdown. +   */ +  nghttp3_shutdown shutdown; +  /** +   * :member:`recv_settings` is a callback function which is invoked +   * when SETTINGS frame is received. +   */ +  nghttp3_recv_settings recv_settings; +} nghttp3_callbacks; + +/** + * @function + * + * `nghttp3_settings_default` fills |settings| with the default + * values. + * + * - :member:`max_field_section_size + *   <nghttp3_settings.max_field_section_size>` = :expr:`((1ull << 62) - 1)` + * - :member:`qpack_max_dtable_capacity + *   <nghttp3_settings.qpack_max_dtable_capacity>` = 0 + * - :member:`qpack_encoder_max_dtable_capacity + *   <nghttp3_settings.qpack_encoder_max_dtable_capacity>` = 4096 + * - :member:`qpack_blocked_streams + *   <nghttp3_settings.qpack_blocked_streams>` = 0 + * - :member:`enable_connect_protocol + *   <nghttp3_settings.enable_connect_protocol>` = 0 + */ +NGHTTP3_EXTERN void +nghttp3_settings_default_versioned(int settings_version, +                                   nghttp3_settings *settings); + +/** + * @function + * + * `nghttp3_conn_client_new` creates :type:`nghttp3_conn`, and + * initializes it for client use.  The pointer to the object is stored + * in |*pconn|.  If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int +nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, int callbacks_version, +                                  const nghttp3_callbacks *callbacks, +                                  int settings_version, +                                  const nghttp3_settings *settings, +                                  const nghttp3_mem *mem, void *conn_user_data); + +/** + * @function + * + * `nghttp3_conn_server_new` creates :type:`nghttp3_conn`, and + * initializes it for server use.  The pointer to the object is stored + * in |*pconn|.  If |mem| is ``NULL``, the memory allocator returned + * by `nghttp3_mem_default` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int +nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, int callbacks_version, +                                  const nghttp3_callbacks *callbacks, +                                  int settings_version, +                                  const nghttp3_settings *settings, +                                  const nghttp3_mem *mem, void *conn_user_data); + +/** + * @function + * + * `nghttp3_conn_del` frees resources allocated for |conn|.  This + * function also frees memory pointed by |conn| itself.  This function + * does nothing if |conn| is NULL. + */ +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: + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` + *     Control stream has already corresponding stream ID. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +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: + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` + *     QPACK encoder/decoder stream have already corresponding stream + *     IDs. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, +                                                   int64_t qenc_stream_id, +                                                   int64_t qdec_stream_id); + +/** + * @function + * + * `nghttp3_conn_read_stream` reads data |src| of length |srclen| on + * stream identified by |stream_id|.  It returns the number of bytes + * consumed.  The "consumed" means that application can increase flow + * control credit (both stream and connection) of underlying QUIC + * connection by that amount.  It does not include the amount of data + * carried by DATA frame which contains application data (excluding + * any control or QPACK unidirectional streams) .  See + * :type:`nghttp3_recv_data` to handle those bytes.  If |fin| is + * nonzero, this is the last data from remote endpoint in this stream. + * + * This function returns the number of bytes consumed, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + *     User callback failed. + * + * It may return the other error codes.  The negative error code means + * that |conn| encountered a connection error, and the connection must + * be closed.  Calling nghttp3 API other than `nghttp3_conn_del` + * causes undefined behavior. + */ +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|.  This + * function may return 0, and |*pstream_id| is not -1, and |*pfin| is + * nonzero.  It means 0 length data to |*pstream_id|, and it is the + * last data to the stream.  They must be passed to QUIC stack, and + * they are accepted, the application has to call + * `nghttp3_conn_add_write_offset` with 0 byte. + * + * This function returns the number of bytes consumed, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + *     User callback failed. + * + * It may return the other error codes.  The negative error code means + * that |conn| encountered a connection error, and the connection must + * be closed.  Calling nghttp3 API other than `nghttp3_conn_del` + * causes undefined behavior. + */ +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. + * + * If a stream denoted by |stream_id| is not found, this function + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +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. + * + * If a stream denoted by |stream_id| is not found, this function + * returns 0. + * + * Alternatively, `nghttp3_conn_update_ack_offset` can be used to + * accomplish the same thing. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + *     User callback failed. + */ +NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, +                                               int64_t stream_id, uint64_t n); + +/** + * @function + * + * `nghttp3_conn_update_ack_offset` tells |conn| that QUIC stack has + * acknowledged the stream data up to |offset| for a stream denoted by + * |stream_id|. + * + * If a stream denoted by |stream_id| is not found, this function + * returns 0. + * + * Alternatively, `nghttp3_conn_add_ack_offset` can be used to + * accomplish the same thing. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |offset| is less than the number of bytes acknowledged so far. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + *     User callback failed. + */ +NGHTTP3_EXTERN int nghttp3_conn_update_ack_offset(nghttp3_conn *conn, +                                                  int64_t stream_id, +                                                  uint64_t offset); + +/** + * @function + * + * `nghttp3_conn_block_stream` tells the library that stream + * identified by |stream_id| is blocked due to QUIC flow control. + */ +NGHTTP3_EXTERN void 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 + * (see `nghttp3_conn_block_stream`) is unblocked. + * + * If a stream denoted by |stream_id| is not found, this function + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, +                                               int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_is_stream_writable` returns nonzero if a stream + * identified by |stream_id| is writable.  It is not writable if: + * + * - the stream does not exist; or, + * - the stream is closed (e.g., `nghttp3_conn_close_stream` is + *   called); or, + * - the stream is QUIC flow control blocked (e.g., + *   `nghttp3_conn_block_stream` is called); or, + * - the stream is input data blocked (e.g., + *   :macro:`NGHTTP3_ERR_WOULDBLOCK` is returned from + *   :type:`nghttp3_read_data_callback`); or, + * - the stream is half-closed local (e.g., + *   `nghttp3_conn_shutdown_stream_write` is called). + */ +NGHTTP3_EXTERN int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, +                                                   int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_shutdown_stream_write` tells the library that any + * further write operation to stream identified by |stream_id| is + * prohibited.  This works like `nghttp3_conn_block_stream`, but it + * cannot be unblocked by `nghttp3_conn_unblock_stream`. + */ +NGHTTP3_EXTERN void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, +                                                       int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_shutdown_stream_read` tells the library that + * read-side of stream denoted by |stream_id| is abruptly closed, and + * any further incoming data and pending stream data should be + * discarded. + * + * If a stream denoted by |stream_id| is not client bidirectional + * stream, this function returns 0.  If the stream has already + * shutdown read-side stream, this function returns 0. + * + * This function does not fail if a stream denoted by |stream_id| is + * not found, although it may fail with the other reasons. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + *     QPACK decoder stream overflow. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_read(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.  See + * :type:`nghttp3_read_data_callback`. + * + * If a stream denoted by |stream_id| is not found, this function + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, +                                              int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_close_stream` tells the library that a stream + * identified by |stream_id| has been closed.  QUIC application error + * code |app_error_code| is the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found. + * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` + *     A critical stream is closed. + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, +                                             int64_t stream_id, +                                             uint64_t app_error_code); + +/** + * @macrosection + * + * Data flags + */ + +/** + * @macro + * + * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set. + */ +#define NGHTTP3_DATA_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`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 + * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time. + */ +#define NGHTTP3_DATA_FLAG_EOF 0x01u + +/** + * @macro + * + * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending + * side of stream is not closed even if :macro:`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. + */ +#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02u + +/** + * @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 + * :macro:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|. + * + * If the application is unable to provide data temporarily, return + * :macro:`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 + * :macro:`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 nghttp3_data_reader { +  /** +   * :member:`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. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |stream_id| identifies unidirectional stream. + * :macro:`NGHTTP3_ERR_CONN_CLOSING` + *     Connection is shutting down, and no new stream is allowed. + * :macro:`NGHTTP3_ERR_STREAM_IN_USE` + *     Stream has already been opened. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +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_info` submits HTTP non-final response header + * fields on the stream identified by |stream_id|.  |nva| of length + * |nvlen| specifies HTTP response header fields. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +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. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +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. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found + * :macro:`NGHTTP3_ERR_INVALID_STATE` + *     Application has already submitted fin to stream. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, +                                                int64_t stream_id, +                                                const nghttp3_nv *nva, +                                                size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint + * to stop creating new stream.  After a couple of RTTs later, call + * `nghttp3_conn_shutdown` to start graceful shutdown. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_shutdown` starts graceful shutdown.  It should be + * called after `nghttp3_conn_submit_shutdown_notice` and a couple of + * RTTs.  After calling this function, the local endpoint starts + * rejecting new incoming streams.  The existing streams are processed + * normally.  See also `nghttp3_conn_is_drained`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the + * stream identified by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found. + */ +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, or |stream_id| identifies + * neither client bidirectional stream nor remote control stream, it + * returns 0. + */ +NGHTTP3_EXTERN uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, +                                                            int64_t stream_id); + +/** + * @macrosection + * + * HTTP stream priority flags + */ + +/** + * @macro + * + * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level. + */ +#define NGHTTP3_DEFAULT_URGENCY 3 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level. + */ +#define NGHTTP3_URGENCY_HIGH 0 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level. + */ +#define NGHTTP3_URGENCY_LOW 7 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels. + */ +#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1) + +#define NGHTTP3_PRI_V1 1 +#define NGHTTP3_PRI_VERSION NGHTTP3_PRI_V1 + +/** + * @struct + * + * :type:`nghttp3_pri` represents HTTP priority. + */ +typedef struct NGHTTP3_ALIGN(8) nghttp3_pri { +  /** +   * :member:`urgency` is the urgency of a stream, it must be in +   * [:macro:`NGHTTP3_URGENCY_HIGH`, :macro:`NGHTTP3_URGENCY_LOW`], +   * inclusive, and 0 is the highest urgency. +   */ +  uint32_t urgency; +  /** +   * :member:`inc` indicates that a content can be processed +   * incrementally or not.  If it is 0, it cannot be processed +   * incrementally.  If it is 1, it can be processed incrementally. +   * Other value is not permitted. +   */ +  uint8_t inc; +} nghttp3_pri; + +/** + * @function + * + * `nghttp3_conn_get_stream_priority` stores stream priority of a + * stream denoted by |stream_id| into |*dest|.  |stream_id| must + * identify client initiated bidirectional stream.  Only server can + * use this function. + * + * This function must not be called if |conn| is initialized as + * client. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |stream_id| is not a client initiated bidirectional stream ID. + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found. + */ +NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority_versioned( +  nghttp3_conn *conn, int pri_version, nghttp3_pri *dest, int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_set_client_stream_priority` updates priority of a + * stream denoted by |stream_id| with the value pointed by |data| of + * length |datalen|, which should be a serialized :rfc:`9218` priority + * field value.  |stream_id| must identify client initiated + * bidirectional stream. + * + * This function must not be called if |conn| is initialized as + * server. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |stream_id| is not a client initiated bidirectional stream ID. + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, +                                                           int64_t stream_id, +                                                           const uint8_t *data, +                                                           size_t datalen); + +/** + * @function + * + * `nghttp3_conn_set_server_stream_priority` updates priority of a + * stream denoted by |stream_id| with the value pointed by |pri|. + * |stream_id| must identify client initiated bidirectional stream. + * + * This function must not be called if |conn| is initialized as + * client. + * + * This function completely overrides stream priority set by client, + * and any attempts to update priority by client are ignored. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     |stream_id| is not a client initiated bidirectional stream ID. + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + *     Stream not found. + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_set_server_stream_priority_versioned( +  nghttp3_conn *conn, int64_t stream_id, int pri_version, +  const nghttp3_pri *pri); + +/** + * @function + * + * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| + * elements. + */ +NGHTTP3_EXTERN uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); + +/** + * @function + * + * `nghttp3_check_header_name` returns nonzero if HTTP field name + * |name| of length |len| is valid according to + * :rfc:`7230#section-3.2`. + * + * Because this is an HTTP 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 field value + * |value| of length |len| is valid according to + * :rfc:`7230#section-3.2`. + */ +NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); + +/** + * @function + * + * `nghttp3_conn_is_drained` returns nonzero if + * `nghttp3_conn_shutdown` has been called, and there is no active + * remote streams.  This function is for server use only. + */ +NGHTTP3_EXTERN int nghttp3_conn_is_drained(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_pri_parse_priority` parses Priority header field value + * pointed by |value| of length |len|, and stores the result in the + * object pointed by |dest|.  Priority header field is defined in + * :rfc:`9218`. + * + * This function does not initialize the object pointed by |dest| + * before storing the result.  It only assigns the values that the + * parser correctly extracted to fields. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     Failed to parse the header field value. + */ +NGHTTP3_EXTERN int nghttp3_pri_parse_priority_versioned(int pri_version, +                                                        nghttp3_pri *dest, +                                                        const uint8_t *value, +                                                        size_t len); + +/** + * @macrosection + * + * nghttp3_info flags + */ + +/** + * @macro + * + * :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`. + */ +#define NGHTTP3_VERSION_AGE 1 + +/** + * @struct + * + * :type:`nghttp3_info` is what `nghttp3_version` returns.  It holds + * information about the particular nghttp3 version. + */ +typedef struct nghttp3_info { +  /** +   * :member:`age` is the 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; +  /** +   * :member:`version_num` is the :macro:`NGHTTP3_VERSION_NUM` number +   * (since age == 1) +   */ +  int version_num; +  /** +   * :member:`version_str` 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 + * + * `nghttp3_version` returns a pointer to a :type:`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 const nghttp3_info *nghttp3_version(int least_version); + +/** + * @function + * + * `nghttp3_err_is_fatal` returns nonzero if |liberr| is a fatal + * error.  |liberr| must be one of nghttp3 library error codes (which + * is defined as NGHTTP3_ERR_* macro, such as + * :macro:`NGHTTP3_ERR_NOMEM`). + */ +NGHTTP3_EXTERN int nghttp3_err_is_fatal(int liberr); + +/* + * Versioned function wrappers + */ + +/* + * `nghttp3_settings_default` is a wrapper around + * `nghttp3_settings_default_versioned` to set the correct struct + * version. + */ +#define nghttp3_settings_default(SETTINGS)                                     \ +  nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_VERSION, (SETTINGS)) + +/* + * `nghttp3_conn_client_new` is a wrapper around + * `nghttp3_conn_client_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_client_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA)    \ +  nghttp3_conn_client_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION,        \ +                                    (CALLBACKS), NGHTTP3_SETTINGS_VERSION,     \ +                                    (SETTINGS), (MEM), (USER_DATA)) + +/* + * `nghttp3_conn_server_new` is a wrapper around + * `nghttp3_conn_server_new_versioned` to set the correct struct + * version. + */ +#define nghttp3_conn_server_new(PCONN, CALLBACKS, SETTINGS, MEM, USER_DATA)    \ +  nghttp3_conn_server_new_versioned((PCONN), NGHTTP3_CALLBACKS_VERSION,        \ +                                    (CALLBACKS), NGHTTP3_SETTINGS_VERSION,     \ +                                    (SETTINGS), (MEM), (USER_DATA)) + +/* + * `nghttp3_conn_set_server_stream_priority` is a wrapper around + * `nghttp3_conn_set_server_stream_priority_versioned` to set the + * correct struct version. + */ +#define nghttp3_conn_set_server_stream_priority(CONN, STREAM_ID, PRI)          \ +  nghttp3_conn_set_server_stream_priority_versioned(                           \ +    (CONN), (STREAM_ID), NGHTTP3_PRI_VERSION, (PRI)) + +/* + * `nghttp3_conn_get_stream_priority` is a wrapper around + * `nghttp3_conn_get_stream_priority_versioned` to set the correct + * struct version. + */ +#define nghttp3_conn_get_stream_priority(CONN, DEST, STREAM_ID)                \ +  nghttp3_conn_get_stream_priority_versioned((CONN), NGHTTP3_PRI_VERSION,      \ +                                             (DEST), (STREAM_ID)) + +/* + * `nghttp3_pri_parse_priority` is a wrapper around + * `nghttp3_pri_parse_priority_versioned` to set the correct struct + * version. + */ +#define nghttp3_pri_parse_priority(DEST, VALUE, LEN)                           \ +  nghttp3_pri_parse_priority_versioned(NGHTTP3_PRI_VERSION, (DEST), (VALUE),   \ +                                       (LEN)) + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGHTTP3_H) */ diff --git a/contrib/libs/nghttp3/lib/includes/nghttp3/version.h b/contrib/libs/nghttp3/lib/includes/nghttp3/version.h new file mode 100644 index 00000000000..7f6cb8acffe --- /dev/null +++ b/contrib/libs/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 "1.6.0" + +/** + * @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 0x010600 + +#endif /* !defined(NGHTTP3_VERSION_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_balloc.c b/contrib/libs/nghttp3/lib/nghttp3_balloc.c new file mode 100644 index 00000000000..544e4fb1306 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_balloc.c @@ -0,0 +1,91 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_balloc.h" + +#include <assert.h> + +#include "nghttp3_mem.h" + +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, +                         const nghttp3_mem *mem) { +  assert((blklen & 0xfu) == 0); + +  balloc->mem = mem; +  balloc->blklen = blklen; +  balloc->head = NULL; +  nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +void nghttp3_balloc_free(nghttp3_balloc *balloc) { +  if (balloc == NULL) { +    return; +  } + +  nghttp3_balloc_clear(balloc); +} + +void nghttp3_balloc_clear(nghttp3_balloc *balloc) { +  nghttp3_memblock_hd *p, *next; + +  for (p = balloc->head; p; p = next) { +    next = p->next; +    nghttp3_mem_free(balloc->mem, p); +  } + +  balloc->head = NULL; +  nghttp3_buf_wrap_init(&balloc->buf, (void *)"", 0); +} + +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n) { +  uint8_t *p; +  nghttp3_memblock_hd *hd; + +  assert(n <= balloc->blklen); + +  if (nghttp3_buf_left(&balloc->buf) < n) { +    p = nghttp3_mem_malloc(balloc->mem, +                           sizeof(nghttp3_memblock_hd) + 0x8u + balloc->blklen); +    if (p == NULL) { +      return NGHTTP3_ERR_NOMEM; +    } + +    hd = (nghttp3_memblock_hd *)(void *)p; +    hd->next = balloc->head; +    balloc->head = hd; +    nghttp3_buf_wrap_init( +      &balloc->buf, +      (uint8_t *)(((uintptr_t)p + sizeof(nghttp3_memblock_hd) + 0xfu) & +                  ~(uintptr_t)0xfu), +      balloc->blklen); +  } + +  assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + +  *pbuf = balloc->buf.last; +  balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + +  return 0; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_balloc.h b/contrib/libs/nghttp3/lib/nghttp3_balloc.h new file mode 100644 index 00000000000..c95f0426a92 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_balloc.h @@ -0,0 +1,95 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_BALLOC_H +#define NGHTTP3_BALLOC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_buf.h" + +typedef struct nghttp3_memblock_hd nghttp3_memblock_hd; + +/* + * nghttp3_memblock_hd is the header of memory block. + */ +struct nghttp3_memblock_hd { +  union { +    nghttp3_memblock_hd *next; +    uint64_t pad; +  }; +}; + +/* + * nghttp3_balloc is a custom memory allocator.  It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct nghttp3_balloc { +  /* mem is the underlying memory allocator. */ +  const nghttp3_mem *mem; +  /* blklen is the size of memory block. */ +  size_t blklen; +  /* head points to the list of memory block allocated so far. */ +  nghttp3_memblock_hd *head; +  /* buf wraps the current memory block for allocation requests. */ +  nghttp3_buf buf; +} nghttp3_balloc; + +/* + * nghttp3_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void nghttp3_balloc_init(nghttp3_balloc *balloc, size_t blklen, +                         const nghttp3_mem *mem); + +/* + * nghttp3_balloc_free releases all allocated memory blocks. + */ +void nghttp3_balloc_free(nghttp3_balloc *balloc); + +/* + * nghttp3_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_balloc_get(nghttp3_balloc *balloc, void **pbuf, size_t n); + +/* + * nghttp3_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void nghttp3_balloc_clear(nghttp3_balloc *balloc); + +#endif /* !defined(NGHTTP3_BALLOC_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_buf.c b/contrib/libs/nghttp3/lib/nghttp3_buf.c new file mode 100644 index 00000000000..aae075a73cc --- /dev/null +++ b/contrib/libs/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/contrib/libs/nghttp3/lib/nghttp3_buf.h b/contrib/libs/nghttp3/lib/nghttp3_buf.h new file mode 100644 index 00000000000..9fa067de91b --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); + +/* + * nghttp3_buf_cap returns the capacity of the buffer.  In other + * words, it returns buf->end - buf->begin. + */ +size_t nghttp3_buf_cap(const nghttp3_buf *buf); + +int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); + +/* + * nghttp3_buf_swap swaps |a| and |b|. + */ +void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b); + +typedef enum nghttp3_buf_type { +  /* 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_typed_buf { +  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 /* !defined(NGHTTP3_BUF_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_conn.c b/contrib/libs/nghttp3/lib/nghttp3_conn.c new file mode 100644 index 00000000000..f70b4f5472d --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_conn.c @@ -0,0 +1,2638 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_conn.h" + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include "nghttp3_mem.h" +#include "nghttp3_macro.h" +#include "nghttp3_err.h" +#include "nghttp3_conv.h" +#include "nghttp3_http.h" +#include "nghttp3_unreachable.h" + +/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the +   dynamic table capacity that QPACK encoder is willing to use. */ +#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 + +nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent); + +/* + * 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.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 fin) { +  int rv; + +  if (!conn->callbacks.end_headers) { +    return 0; +  } + +  rv = conn->callbacks.end_headers(conn, stream->node.id, fin, 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.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 fin) { +  int rv; + +  if (!conn->callbacks.end_trailers) { +    return 0; +  } + +  rv = conn->callbacks.end_trailers(conn, stream->node.id, fin, 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.id, conn->user_data, +                                  stream->user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_stop_sending(nghttp3_conn *conn, nghttp3_stream *stream, +                                  uint64_t app_error_code) { +  int rv; + +  if (!conn->callbacks.stop_sending) { +    return 0; +  } + +  rv = conn->callbacks.stop_sending(conn, stream->node.id, app_error_code, +                                    conn->user_data, stream->user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream, +                                  uint64_t app_error_code) { +  int rv; + +  if (!conn->callbacks.reset_stream) { +    return 0; +  } + +  rv = conn->callbacks.reset_stream(conn, stream->node.id, app_error_code, +                                    conn->user_data, stream->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.id, nconsumed, +                                        conn->user_data, stream->user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_settings(nghttp3_conn *conn) { +  int rv; + +  if (!conn->callbacks.recv_settings) { +    return 0; +  } + +  rv = conn->callbacks.recv_settings(conn, &conn->remote.settings, +                                     conn->user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int ricnt_less(const nghttp3_pq_entry *lhsx, +                      const nghttp3_pq_entry *rhsx) { +  nghttp3_stream *lhs = +    nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe); +  nghttp3_stream *rhs = +    nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe); + +  return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt; +} + +static int cycle_less(const nghttp3_pq_entry *lhsx, +                      const nghttp3_pq_entry *rhsx) { +  const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe); +  const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe); + +  if (lhs->cycle == rhs->cycle) { +    return lhs->id < rhs->id; +  } + +  return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; +} + +static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, +                    const nghttp3_callbacks *callbacks, int settings_version, +                    const nghttp3_settings *settings, const nghttp3_mem *mem, +                    void *user_data) { +  int rv; +  nghttp3_conn *conn; +  size_t i; +  (void)callbacks_version; +  (void)settings_version; + +  if (mem == NULL) { +    mem = nghttp3_mem_default(); +  } + +  conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); +  if (conn == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  nghttp3_objalloc_init(&conn->out_chunk_objalloc, +                        NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem); +  nghttp3_objalloc_stream_init(&conn->stream_objalloc, 8, mem); + +  nghttp3_map_init(&conn->streams, mem); + +  rv = +    nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity, +                               settings->qpack_blocked_streams, mem); +  if (rv != 0) { +    goto qdec_init_fail; +  } + +  rv = nghttp3_qpack_encoder_init( +    &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem); +  if (rv != 0) { +    goto qenc_init_fail; +  } + +  nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); + +  for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { +    nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem); +  } + +  nghttp3_idtr_init(&conn->remote.bidi.idtr, mem); + +  conn->callbacks = *callbacks; +  conn->local.settings = *settings; +  if (!server) { +    conn->local.settings.enable_connect_protocol = 0; +  } +  nghttp3_settings_default(&conn->remote.settings); +  conn->mem = mem; +  conn->user_data = user_data; +  conn->server = server; +  conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1; +  conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1; +  conn->rx.max_stream_id_bidi = -4; + +  *pconn = conn; + +  return 0; + +qenc_init_fail: +  nghttp3_qpack_decoder_free(&conn->qdec); +qdec_init_fail: +  nghttp3_map_free(&conn->streams); +  nghttp3_objalloc_free(&conn->stream_objalloc); +  nghttp3_objalloc_free(&conn->out_chunk_objalloc); +  nghttp3_mem_free(mem, conn); + +  return rv; +} + +int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, +                                      int callbacks_version, +                                      const nghttp3_callbacks *callbacks, +                                      int settings_version, +                                      const nghttp3_settings *settings, +                                      const nghttp3_mem *mem, void *user_data) { +  int rv; + +  rv = conn_new(pconn, /* server = */ 0, callbacks_version, callbacks, +                settings_version, settings, mem, user_data); +  if (rv != 0) { +    return rv; +  } + +  return 0; +} + +int nghttp3_conn_server_new_versioned(nghttp3_conn **pconn, +                                      int callbacks_version, +                                      const nghttp3_callbacks *callbacks, +                                      int settings_version, +                                      const nghttp3_settings *settings, +                                      const nghttp3_mem *mem, void *user_data) { +  int rv; + +  rv = conn_new(pconn, /* server = */ 1, callbacks_version, callbacks, +                settings_version, settings, mem, user_data); +  if (rv != 0) { +    return rv; +  } + +  return 0; +} + +static int free_stream(void *data, void *ptr) { +  nghttp3_stream *stream = data; + +  (void)ptr; + +  nghttp3_stream_del(stream); + +  return 0; +} + +void nghttp3_conn_del(nghttp3_conn *conn) { +  size_t i; + +  if (conn == NULL) { +    return; +  } + +  nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); +  nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); + +  nghttp3_idtr_free(&conn->remote.bidi.idtr); + +  for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { +    nghttp3_pq_free(&conn->sched[i].spq); +  } + +  nghttp3_pq_free(&conn->qpack_blocked_streams); + +  nghttp3_qpack_encoder_free(&conn->qenc); +  nghttp3_qpack_decoder_free(&conn->qdec); + +  nghttp3_map_each(&conn->streams, free_stream, NULL); +  nghttp3_map_free(&conn->streams); + +  nghttp3_objalloc_free(&conn->stream_objalloc); +  nghttp3_objalloc_free(&conn->out_chunk_objalloc); + +  nghttp3_mem_free(conn->mem, conn); +} + +static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) { +  int rv; + +  rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); +  if (rv != 0) { +    return rv; +  } + +  if (nghttp3_ksl_len(&conn->remote.bidi.idtr.gap.gap) > 32) { +    nghttp3_gaptr_drop_first_gap(&conn->remote.bidi.idtr.gap); +  } + +  return 0; +} + +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 = conn_bidi_idtr_open(conn, stream_id); +        if (rv != 0) { +          if (nghttp3_err_is_fatal(rv)) { +            return rv; +          } + +          /* Ignore return value.  We might drop the first gap if there +             are many gaps if QUIC stack allows too many holes in stream +             ID space.  idtr is used to decide whether PRIORITY_UPDATE +             frame should be ignored or not and the frame is optional. +             Ignoring them causes no harm. */ +        } + +        conn->rx.max_stream_id_bidi = +          nghttp3_max_int64(conn->rx.max_stream_id_bidi, stream_id); +        rv = nghttp3_conn_create_stream(conn, &stream, stream_id); +        if (rv != 0) { +          return rv; +        } + +        if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && +            conn->tx.goaway_id <= stream_id) { +          stream->rstate.state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; + +          rv = nghttp3_conn_reject_stream(conn, stream); +          if (rv != 0) { +            return rv; +          } +        } +      } else { +        /* unidirectional stream */ +        if (srclen == 0 && fin) { +          return 0; +        } + +        rv = nghttp3_conn_create_stream(conn, &stream, stream_id); +        if (rv != 0) { +          return rv; +        } +      } + +      stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; +      stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; +    } else if (nghttp3_stream_uni(stream_id)) { +      if (srclen == 0 && fin) { +        return 0; +      } + +      rv = nghttp3_conn_create_stream(conn, &stream, stream_id); +      if (rv != 0) { +        return rv; +      } + +      stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; +      stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; +    } else { +      /* client doesn't expect to receive new bidirectional stream +         from server. */ +      return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; +    } +  } else if (conn->server) { +    if (nghttp3_client_stream_bidi(stream_id)) { +      if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { +        stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; +        stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; +      } +    } +  } + +  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, 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: +    return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; +  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; +  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_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_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR); +    if (rv != 0) { +      return rv; +    } +    break; +  default: +    nghttp3_unreachable(); +  } + +  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; +  const uint8_t *pri_field_value = NULL; +  size_t pri_field_valuelen = 0; + +  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, end, /* 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, end, /* 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_SETTINGS: +        /* SETTINGS frame might be empty. */ +        if (rstate->left == 0) { +          rv = conn_call_recv_settings(conn); +          if (rv != 0) { +            return rv; +          } + +          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_PRIORITY_UPDATE: +        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_PRIORITY_UPDATE_PRI_ELEM_ID; +        break; +      case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: +        /* We do not support push */ +        return NGHTTP3_ERR_H3_ID_ERROR; +      case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */ +      case NGHTTP3_FRAME_DATA: +      case NGHTTP3_FRAME_HEADERS: +      case NGHTTP3_FRAME_PUSH_PROMISE: +      case NGHTTP3_H2_FRAME_PRIORITY: +      case NGHTTP3_H2_FRAME_PING: +      case NGHTTP3_H2_FRAME_WINDOW_UPDATE: +      case NGHTTP3_H2_FRAME_CONTINUATION: +        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_SETTINGS: +      for (;;) { +        if (rstate->left == 0) { +          rv = conn_call_recv_settings(conn); +          if (rv != 0) { +            return rv; +          } + +          nghttp3_stream_read_state_reset(rstate); +          break; +        } + +        if (p == end) { +          return (nghttp3_ssize)nconsumed; +        } + +        /* Read Identifier */ +        len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +        assert(len > 0); +        nread = nghttp3_read_varint(rvint, p, 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, 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_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      nread = nghttp3_read_varint(rvint, p, 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_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      nread = nghttp3_read_varint(rvint, p, 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; +      } + +      rv = conn_call_recv_settings(conn); +      if (rv != 0) { +        return rv; +      } + +      nghttp3_stream_read_state_reset(rstate); +      break; +    case NGHTTP3_CTRL_STREAM_STATE_GOAWAY: +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      nread = nghttp3_read_varint(rvint, p, 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->server && !nghttp3_client_stream_bidi(rvint->acc)) { +        return NGHTTP3_ERR_H3_ID_ERROR; +      } +      if (conn->rx.goaway_id < rvint->acc) { +        return NGHTTP3_ERR_H3_ID_ERROR; +      } + +      conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_RECVED; +      conn->rx.goaway_id = rvint->acc; +      nghttp3_varint_read_state_reset(rvint); + +      if (conn->callbacks.shutdown) { +        rv = +          conn->callbacks.shutdown(conn, conn->rx.goaway_id, conn->user_data); +        if (rv != 0) { +          return NGHTTP3_ERR_CALLBACK_FAILURE; +        } +      } + +      nghttp3_stream_read_state_reset(rstate); +      break; +    case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: +      /* server side only */ +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      nread = nghttp3_read_varint(rvint, p, 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 + 1) { +        return NGHTTP3_ERR_H3_FRAME_ERROR; +      } + +      conn->local.uni.max_pushes = (uint64_t)rvint->acc + 1; +      nghttp3_varint_read_state_reset(rvint); + +      nghttp3_stream_read_state_reset(rstate); +      break; +    case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID: +      /* server side only */ +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      nread = nghttp3_read_varint(rvint, p, 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.priority_update.pri_elem_id = rvint->acc; +      nghttp3_varint_read_state_reset(rvint); + +      if (rstate->left == 0) { +        rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; +        rstate->fr.priority_update.pri.inc = 0; + +        rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); +        if (rv != 0) { +          return rv; +        } + +        nghttp3_stream_read_state_reset(rstate); +        break; +      } + +      rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE; + +      /* Fall through */ +    case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE: +      /* We need to buffer Priority Field Value because it might be +         fragmented. */ +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      assert(len > 0); +      if (conn->rx.pri_fieldbuflen == 0 && rstate->left == (int64_t)len) { +        /* Everything is in the input buffer.  Apply same length +           limit we impose when buffering the field. */ +        if (len > sizeof(conn->rx.pri_fieldbuf)) { +          busy = 1; +          rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; +          break; +        } + +        pri_field_value = p; +        pri_field_valuelen = len; +      } else if (len + conn->rx.pri_fieldbuflen > +                 sizeof(conn->rx.pri_fieldbuf)) { +        busy = 1; +        rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; +        break; +      } else { +        memcpy(conn->rx.pri_fieldbuf + conn->rx.pri_fieldbuflen, p, len); +        conn->rx.pri_fieldbuflen += len; + +        if (rstate->left == (int64_t)len) { +          pri_field_value = conn->rx.pri_fieldbuf; +          pri_field_valuelen = conn->rx.pri_fieldbuflen; +        } +      } + +      p += len; +      nconsumed += len; +      rstate->left -= (int64_t)len; + +      if (rstate->left) { +        return (nghttp3_ssize)nconsumed; +      } + +      rstate->fr.priority_update.pri.urgency = NGHTTP3_DEFAULT_URGENCY; +      rstate->fr.priority_update.pri.inc = 0; + +      if (nghttp3_http_parse_priority(&rstate->fr.priority_update.pri, +                                      pri_field_value, +                                      pri_field_valuelen) != 0) { +        return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; +      } + +      rv = nghttp3_conn_on_priority_update(conn, &rstate->fr.priority_update); +      if (rv != 0) { +        return rv; +      } + +      conn->rx.pri_fieldbuflen = 0; + +      nghttp3_stream_read_state_reset(rstate); +      break; +    case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      p += len; +      nconsumed += len; +      rstate->left -= (int64_t)len; + +      if (rstate->left) { +        return (nghttp3_ssize)nconsumed; +      } + +      nghttp3_stream_read_state_reset(rstate); +      break; +    default: +      nghttp3_unreachable(); +    } +  } + +  return (nghttp3_ssize)nconsumed; +} + +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { +  int bidi = nghttp3_client_stream_bidi(stream->node.id); +  int rv; + +  rv = conn_call_deferred_consume(conn, stream, +                                  nghttp3_stream_get_buffered_datalen(stream)); +  if (rv != 0) { +    return rv; +  } + +  if (bidi && conn->callbacks.stream_close) { +    rv = conn->callbacks.stream_close(conn, stream->node.id, stream->error_code, +                                      conn->user_data, stream->user_data); +    if (rv != 0) { +      return NGHTTP3_ERR_CALLBACK_FAILURE; +    } +  } + +  if (conn->server && nghttp3_client_stream_bidi(stream->node.id)) { +    assert(conn->remote.bidi.num_streams > 0); + +    --conn->remote.bidi.num_streams; +  } + +  rv = +    nghttp3_map_remove(&conn->streams, (nghttp3_map_key_type)stream->node.id); + +  assert(0 == rv); + +  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; + +  assert(nghttp3_client_stream_bidi(stream->node.id)); + +  for (;;) { +    len = nghttp3_ringbuf_len(&stream->inq); +    if (len == 0) { +      break; +    } + +    buf = nghttp3_ringbuf_get(&stream->inq, 0); + +    nconsumed = nghttp3_conn_read_bidi( +      conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), +      len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); +    if (nconsumed < 0) { +      return (int)nconsumed; +    } + +    buf->pos += nproc; + +    rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed); +    if (rv != 0) { +      return 0; +    } + +    if (nghttp3_buf_len(buf) == 0) { +      nghttp3_buf_free(buf, stream->mem); +      nghttp3_ringbuf_pop_front(&stream->inq); +    } + +    if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { +      break; +    } +  } + +  if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) && +      (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) { +    assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); + +    rv = conn_delete_stream(conn, stream); +    if (rv != 0) { +      return rv; +    } +  } + +  return 0; +} + +nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, +                                              const uint8_t *src, +                                              size_t srclen) { +  nghttp3_ssize nconsumed = +    nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen); +  nghttp3_stream *stream; +  int rv; + +  if (nconsumed < 0) { +    return nconsumed; +  } + +  for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) { +    stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams), +                               nghttp3_stream, qpack_blocked_pe); +    if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) > +        nghttp3_qpack_decoder_get_icnt(&conn->qdec)) { +      break; +    } + +    nghttp3_conn_qpack_blocked_streams_pop(conn); +    stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; +    stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; + +    rv = conn_process_blocked_stream_data(conn, stream); +    if (rv != 0) { +      return rv; +    } +  } + +  return nconsumed; +} + +nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, +                                              const uint8_t *src, +                                              size_t srclen) { +  return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); +} + +static nghttp3_tnode *stream_get_sched_node(nghttp3_stream *stream) { +  return &stream->node; +} + +static int conn_update_stream_priority(nghttp3_conn *conn, +                                       nghttp3_stream *stream, +                                       const nghttp3_pri *pri) { +  assert(nghttp3_client_stream_bidi(stream->node.id)); + +  if (nghttp3_pri_eq(&stream->node.pri, pri)) { +    return 0; +  } + +  nghttp3_conn_unschedule_stream(conn, stream); + +  stream->node.pri = *pri; + +  if (nghttp3_stream_require_schedule(stream)) { +    return nghttp3_conn_schedule_stream(conn, stream); +  } + +  return 0; +} + +nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, +                                     nghttp3_stream *stream, const uint8_t *src, +                                     size_t srclen, int fin) { +  const uint8_t *p = src, *end = src ? src + srclen : src; +  int rv; +  nghttp3_stream_read_state *rstate = &stream->rstate; +  nghttp3_varint_read_state *rvint = &rstate->rvint; +  nghttp3_ssize nread; +  size_t nconsumed = 0; +  int busy = 0; +  size_t len; + +  if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { +    *pnproc = srclen; + +    return (nghttp3_ssize)srclen; +  } + +  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, end, 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, end, 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: +          nghttp3_unreachable(); +        } + +        if (rv != 0) { +          return rv; +        } + +        rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; +        break; +      case NGHTTP3_FRAME_PUSH_PROMISE: /* We do not support push */ +      case NGHTTP3_FRAME_CANCEL_PUSH: +      case NGHTTP3_FRAME_SETTINGS: +      case NGHTTP3_FRAME_GOAWAY: +      case NGHTTP3_FRAME_MAX_PUSH_ID: +      case NGHTTP3_FRAME_PRIORITY_UPDATE: +      case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: +      case NGHTTP3_H2_FRAME_PRIORITY: +      case NGHTTP3_H2_FRAME_PING: +      case NGHTTP3_H2_FRAME_WINDOW_UPDATE: +      case NGHTTP3_H2_FRAME_CONTINUATION: +        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_int64(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_HEADERS: +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      nread = nghttp3_conn_on_headers(conn, stream, p, len, +                                      (int64_t)len == rstate->left); +      if (nread < 0) { +        if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { +          goto http_header_error; +        } + +        return nread; +      } + +      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: +        nghttp3_unreachable(); +      } + +      if (rv != 0) { +        if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { +          goto http_header_error; +        } + +        return rv; +      } + +      switch (stream->rx.hstate) { +      case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: +        /* Only server utilizes priority information to schedule +           streams. */ +        if (conn->server && +            (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_PRIORITY) && +            !(stream->flags & NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED) && +            !(stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET)) { +          rv = conn_update_stream_priority(conn, stream, &stream->rx.http.pri); +          if (rv != 0) { +            return rv; +          } +        } +        /* fall through */ +      case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: +        rv = conn_call_end_headers(conn, stream, p == end && fin); +        break; +      case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: +      case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: +        rv = conn_call_end_trailers(conn, stream, p == end && fin); +        break; +      default: +        nghttp3_unreachable(); +      } + +      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; + +    http_header_error: +      stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR; + +      busy = 1; +      rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; + +      rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); +      if (rv != 0) { +        return rv; +      } + +      rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); +      if (rv != 0) { +        return rv; +      } + +      break; +    case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: +      len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); +      p += len; +      nconsumed += len; +      rstate->left -= (int64_t)len; + +      if (rstate->left) { +        goto almost_done; +      } + +      nghttp3_stream_read_state_reset(rstate); +      break; +    case NGHTTP3_REQ_STREAM_STATE_IGN_REST: +      nconsumed += (size_t)(end - p); +      *pnproc = (size_t)(end - src); +      return (nghttp3_ssize)nconsumed; +    } +  } + +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; +    case NGHTTP3_REQ_STREAM_STATE_IGN_REST: +      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.id, data, datalen, +                                 conn->user_data, stream->user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { +  assert(tnode->pri.urgency < NGHTTP3_URGENCY_LEVELS); + +  return &conn->sched[tnode->pri.urgency].spq; +} + +static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, +                                         nghttp3_stream *stream, +                                         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; + +  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: +    nghttp3_unreachable(); +  } +  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) { +      rv = nghttp3_http_on_header( +        http, &nv, request, trailers, +        conn->server && conn->local.settings.enable_connect_protocol); +      switch (rv) { +      case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: +        break; +      case NGHTTP3_ERR_REMOVE_HTTP_HEADER: +        rv = 0; +        break; +      case 0: +        if (recv_header) { +          rv = recv_header(conn, stream->node.id, nv.token, nv.name, nv.value, +                           nv.flags, conn->user_data, stream->user_data); +          if (rv != 0) { +            rv = NGHTTP3_ERR_CALLBACK_FAILURE; +          } +        } +        break; +      default: +        nghttp3_unreachable(); +      } + +      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, +                                      const uint8_t *src, size_t srclen, +                                      int fin) { +  if (srclen == 0 && !fin) { +    return 0; +  } + +  return conn_decode_headers(conn, stream, 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_settings *dest = &conn->remote.settings; + +  /* TODO Check for duplicates */ +  switch (ent->id) { +  case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE: +    dest->max_field_section_size = ent->value; +    break; +  case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: +    if (dest->qpack_max_dtable_capacity != 0) { +      return NGHTTP3_ERR_H3_SETTINGS_ERROR; +    } + +    if (ent->value == 0) { +      break; +    } + +    dest->qpack_max_dtable_capacity = (size_t)ent->value; + +    nghttp3_qpack_encoder_set_max_dtable_capacity(&conn->qenc, +                                                  (size_t)ent->value); +    break; +  case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: +    if (dest->qpack_blocked_streams != 0) { +      return NGHTTP3_ERR_H3_SETTINGS_ERROR; +    } + +    if (ent->value == 0) { +      break; +    } + +    dest->qpack_blocked_streams = (size_t)ent->value; + +    nghttp3_qpack_encoder_set_max_blocked_streams( +      &conn->qenc, (size_t)nghttp3_min_uint64(100, ent->value)); +    break; +  case NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL: +    if (!conn->server) { +      break; +    } + +    switch (ent->value) { +    case 0: +      if (dest->enable_connect_protocol) { +        return NGHTTP3_ERR_H3_SETTINGS_ERROR; +      } + +      break; +    case 1: +      break; +    default: +      return NGHTTP3_ERR_H3_SETTINGS_ERROR; +    } + +    dest->enable_connect_protocol = (uint8_t)ent->value; +    break; +  case NGHTTP3_SETTINGS_ID_H3_DATAGRAM: +    switch (ent->value) { +    case 0: +    case 1: +      break; +    default: +      return NGHTTP3_ERR_H3_SETTINGS_ERROR; +    } + +    dest->h3_datagram = (uint8_t)ent->value; +    break; +  case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH: +  case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS: +  case NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE: +  case NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE: +    return NGHTTP3_ERR_H3_SETTINGS_ERROR; +  default: +    /* Ignore unknown settings ID */ +    break; +  } + +  return 0; +} + +static int +conn_on_priority_update_stream(nghttp3_conn *conn, +                               const nghttp3_frame_priority_update *fr) { +  int64_t stream_id = fr->pri_elem_id; +  nghttp3_stream *stream; +  int rv; + +  if (!nghttp3_client_stream_bidi(stream_id) || +      nghttp3_ord_stream_id(stream_id) > conn->remote.bidi.max_client_streams) { +    return NGHTTP3_ERR_H3_ID_ERROR; +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream == NULL) { +    if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && +        conn->tx.goaway_id <= stream_id) { +      /* Connection is going down.  Ignore priority signal. */ +      return 0; +    } + +    rv = conn_bidi_idtr_open(conn, stream_id); +    if (rv != 0) { +      if (nghttp3_err_is_fatal(rv)) { +        return rv; +      } + +      assert(rv == NGHTTP3_ERR_STREAM_IN_USE); + +      /* The stream is gone.  Just ignore. */ +      return 0; +    } + +    conn->rx.max_stream_id_bidi = +      nghttp3_max_int64(conn->rx.max_stream_id_bidi, stream_id); +    rv = nghttp3_conn_create_stream(conn, &stream, stream_id); +    if (rv != 0) { +      return rv; +    } + +    stream->node.pri = fr->pri; +    stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + +    return 0; +  } + +  if (stream->flags & NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET) { +    return 0; +  } + +  stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + +  return conn_update_stream_priority(conn, stream, &fr->pri); +} + +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, +                                    const nghttp3_frame_priority_update *fr) { +  assert(conn->server); +  assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE); + +  return conn_on_priority_update_stream(conn, fr); +} + +static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, +                                  uint64_t datalen, void *user_data) { +  nghttp3_conn *conn = stream->conn; +  int rv; + +  if (!conn->callbacks.acked_stream_data) { +    return 0; +  } + +  rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen, +                                         conn->user_data, user_data); +  if (rv != 0) { +    return NGHTTP3_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, +                               int64_t stream_id) { +  nghttp3_stream *stream; +  int rv; +  nghttp3_stream_callbacks callbacks = { +    conn_stream_acked_data, +  }; + +  rv = nghttp3_stream_new(&stream, stream_id, &callbacks, +                          &conn->out_chunk_objalloc, &conn->stream_objalloc, +                          conn->mem); +  if (rv != 0) { +    return rv; +  } + +  stream->conn = conn; + +  rv = nghttp3_map_insert(&conn->streams, (nghttp3_map_key_type)stream->node.id, +                          stream); +  if (rv != 0) { +    nghttp3_stream_del(stream); +    return rv; +  } + +  if (conn->server && nghttp3_client_stream_bidi(stream_id)) { +    ++conn->remote.bidi.num_streams; +  } + +  *pstream = stream; + +  return 0; +} + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, +                                         int64_t stream_id) { +  return nghttp3_map_find(&conn->streams, (nghttp3_map_key_type)stream_id); +} + +int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { +  nghttp3_stream *stream; +  nghttp3_frame_entry frent; +  int rv; + +  assert(!conn->server || nghttp3_server_stream_uni(stream_id)); +  assert(conn->server || nghttp3_client_stream_uni(stream_id)); + +  if (conn->tx.ctrl) { +    return NGHTTP3_ERR_INVALID_STATE; +  } + +  rv = nghttp3_conn_create_stream(conn, &stream, stream_id); +  if (rv != 0) { +    return rv; +  } + +  stream->type = NGHTTP3_STREAM_TYPE_CONTROL; + +  conn->tx.ctrl = stream; + +  rv = nghttp3_stream_write_stream_type(stream); +  if (rv != 0) { +    return rv; +  } + +  frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; +  frent.aux.settings.local_settings = &conn->local.settings; + +  return nghttp3_stream_frq_add(stream, &frent); +} + +int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, +                                    int64_t qdec_stream_id) { +  nghttp3_stream *stream; +  int rv; + +  assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id)); +  assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id)); +  assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id)); +  assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id)); + +  if (conn->tx.qenc || conn->tx.qdec) { +    return NGHTTP3_ERR_INVALID_STATE; +  } + +  rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id); +  if (rv != 0) { +    return rv; +  } + +  stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; + +  conn->tx.qenc = stream; + +  rv = nghttp3_stream_write_stream_type(stream); +  if (rv != 0) { +    return rv; +  } + +  rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id); +  if (rv != 0) { +    return rv; +  } + +  stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; + +  conn->tx.qdec = stream; + +  return nghttp3_stream_write_stream_type(stream); +} + +static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id, +                                        int *pfin, nghttp3_vec *vec, +                                        size_t veccnt, nghttp3_stream *stream) { +  int rv; +  size_t 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.id) && conn->tx.qenc && +      !nghttp3_stream_is_blocked(conn->tx.qenc)) { +    n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt); +    if (n) { +      *pstream_id = conn->tx.qenc->node.id; +      return (nghttp3_ssize)n; +    } +  } + +  n = nghttp3_stream_writev(stream, pfin, vec, veccnt); +  /* We might just want to write stream fin without sending any stream +     data. */ +  if (n == 0 && *pfin == 0) { +    return 0; +  } + +  *pstream_id = stream->node.id; + +  return (nghttp3_ssize)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)) { +    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_client_stream_bidi(stream->node.id) && +      !nghttp3_stream_require_schedule(stream)) { +    nghttp3_conn_unschedule_stream(conn, stream); +  } + +  return ncnt; +} + +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { +  size_t i; +  nghttp3_tnode *tnode; +  nghttp3_pq *pq; + +  for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { +    pq = &conn->sched[i].spq; +    if (nghttp3_pq_empty(pq)) { +      continue; +    } + +    tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); + +    return nghttp3_struct_of(tnode, nghttp3_stream, node); +  } + +  return NULL; +} + +int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, +                                  size_t n) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return 0; +  } + +  nghttp3_stream_add_outq_offset(stream, n); + +  stream->unscheduled_nwrite += n; + +  if (!nghttp3_client_stream_bidi(stream->node.id)) { +    return 0; +  } + +  if (!nghttp3_stream_require_schedule(stream)) { +    nghttp3_conn_unschedule_stream(conn, stream); +    return 0; +  } + +  if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) { +    return 0; +  } + +  return nghttp3_conn_schedule_stream(conn, stream); +} + +int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, +                                uint64_t n) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return 0; +  } + +  return nghttp3_stream_update_ack_offset(stream, stream->ack_offset + n); +} + +int nghttp3_conn_update_ack_offset(nghttp3_conn *conn, int64_t stream_id, +                                   uint64_t offset) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return 0; +  } + +  if (stream->ack_offset > offset) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  return nghttp3_stream_update_ack_offset(stream, offset); +} + +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 = {0}; + +  rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  frent.fr.hd.type = NGHTTP3_FRAME_HEADERS; +  frent.fr.headers.nva = nnva; +  frent.fr.headers.nvlen = nvlen; + +  rv = nghttp3_stream_frq_add(stream, &frent); +  if (rv != 0) { +    nghttp3_nva_del(nnva, conn->mem); +    return rv; +  } + +  if (dr) { +    frent.fr.hd.type = NGHTTP3_FRAME_DATA; +    frent.aux.data.dr = *dr; + +    rv = nghttp3_stream_frq_add(stream, &frent); +    if (rv != 0) { +      return rv; +    } +  } + +  if (nghttp3_stream_require_schedule(stream)) { +    return nghttp3_conn_schedule_stream(conn, stream); +  } + +  return 0; +} + +int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { +  /* Assume that stream stays on the same urgency level */ +  nghttp3_tnode *node = stream_get_sched_node(stream); +  int rv; + +  rv = nghttp3_tnode_schedule(node, conn_get_sched_pq(conn, node), +                              stream->unscheduled_nwrite); +  if (rv != 0) { +    return rv; +  } + +  stream->unscheduled_nwrite = 0; + +  return 0; +} + +int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, +                                         nghttp3_stream *stream) { +  if (nghttp3_tnode_is_scheduled(stream_get_sched_node(stream))) { +    return 0; +  } + +  return nghttp3_conn_schedule_stream(conn, stream); +} + +void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, +                                    nghttp3_stream *stream) { +  nghttp3_tnode *node = stream_get_sched_node(stream); + +  nghttp3_tnode_unschedule(node, conn_get_sched_pq(conn, node)); +} + +int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, +                                const nghttp3_nv *nva, size_t nvlen, +                                const nghttp3_data_reader *dr, +                                void *stream_user_data) { +  nghttp3_stream *stream; +  int rv; + +  assert(!conn->server); +  assert(conn->tx.qenc); + +  assert(nghttp3_client_stream_bidi(stream_id)); + +  /* TODO Should we check that stream_id is client stream_id? */ +  /* TODO Check GOAWAY last stream ID */ +  if (nghttp3_stream_uni(stream_id)) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { +    return NGHTTP3_ERR_CONN_CLOSING; +  } + +  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_shutdown_notice(nghttp3_conn *conn) { +  nghttp3_frame_entry frent = {0}; +  int rv; + +  assert(conn->tx.ctrl); + +  frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; +  frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID +                                    : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID; + +  assert(frent.fr.goaway.id <= conn->tx.goaway_id); + +  rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); +  if (rv != 0) { +    return rv; +  } + +  conn->tx.goaway_id = frent.fr.goaway.id; +  conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED; + +  return 0; +} + +int nghttp3_conn_shutdown(nghttp3_conn *conn) { +  nghttp3_frame_entry frent = {0}; +  int rv; + +  assert(conn->tx.ctrl); + +  frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; +  if (conn->server) { +    frent.fr.goaway.id = +      nghttp3_min_int64((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); +  } else { +    frent.fr.goaway.id = 0; +  } + +  assert(frent.fr.goaway.id <= conn->tx.goaway_id); + +  rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); +  if (rv != 0) { +    return rv; +  } + +  conn->tx.goaway_id = frent.fr.goaway.id; +  conn->flags |= +    NGHTTP3_CONN_FLAG_GOAWAY_QUEUED | NGHTTP3_CONN_FLAG_SHUTDOWN_COMMENCED; + +  return 0; +} + +int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { +  int rv; + +  rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); +  if (rv != 0) { +    return rv; +  } + +  return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); +} + +void 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; +  } + +  stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; +  stream->unscheduled_nwrite = 0; + +  if (nghttp3_client_stream_bidi(stream->node.id)) { +    nghttp3_conn_unschedule_stream(conn, stream); +  } +} + +void nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return; +  } + +  stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR; +  stream->unscheduled_nwrite = 0; + +  if (nghttp3_client_stream_bidi(stream->node.id)) { +    nghttp3_conn_unschedule_stream(conn, stream); +  } +} + +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 0; +  } + +  stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; + +  if (nghttp3_client_stream_bidi(stream->node.id) && +      nghttp3_stream_require_schedule(stream)) { +    return nghttp3_conn_ensure_stream_scheduled(conn, stream); +  } + +  return 0; +} + +int nghttp3_conn_is_stream_writable(nghttp3_conn *conn, int64_t stream_id) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return 0; +  } + +  return (stream->flags & +          (NGHTTP3_STREAM_FLAG_FC_BLOCKED | +           NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED | NGHTTP3_STREAM_FLAG_SHUT_WR | +           NGHTTP3_STREAM_FLAG_CLOSED)) == 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 0; +  } + +  stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; + +  if (nghttp3_client_stream_bidi(stream->node.id) && +      nghttp3_stream_require_schedule(stream)) { +    return nghttp3_conn_ensure_stream_scheduled(conn, stream); +  } + +  return 0; +} + +int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, +                              uint64_t app_error_code) { +  nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + +  if (stream == NULL) { +    return NGHTTP3_ERR_STREAM_NOT_FOUND; +  } + +  if (nghttp3_stream_uni(stream_id) && +      stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) { +    return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; +  } + +  stream->error_code = app_error_code; + +  nghttp3_conn_unschedule_stream(conn, stream); + +  if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX) { +    return conn_delete_stream(conn, stream); +  } + +  stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED; +  return 0; +} + +int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) { +  nghttp3_stream *stream; + +  if (!nghttp3_client_stream_bidi(stream_id)) { +    return 0; +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream) { +    if (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_RD) { +      return 0; +    } + +    stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_RD; +  } + +  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_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; +} + +uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, +                                             int64_t stream_id) { +  nghttp3_stream *stream; +  int uni = 0; + +  if (!nghttp3_client_stream_bidi(stream_id)) { +    uni = conn_remote_stream_uni(conn, stream_id); +    if (!uni) { +      return 0; +    } +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream == NULL) { +    return 0; +  } + +  if (uni && stream->type != NGHTTP3_STREAM_TYPE_CONTROL) { +    return 0; +  } + +  return (uint64_t)stream->rstate.left; +} + +int nghttp3_conn_get_stream_priority_versioned(nghttp3_conn *conn, +                                               int pri_version, +                                               nghttp3_pri *dest, +                                               int64_t stream_id) { +  nghttp3_stream *stream; +  (void)pri_version; + +  assert(conn->server); + +  if (!nghttp3_client_stream_bidi(stream_id)) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream == NULL) { +    return NGHTTP3_ERR_STREAM_NOT_FOUND; +  } + +  *dest = stream->node.pri; + +  return 0; +} + +int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, +                                            int64_t stream_id, +                                            const uint8_t *data, +                                            size_t datalen) { +  nghttp3_stream *stream; +  nghttp3_frame_entry frent = {0}; +  uint8_t *buf = NULL; + +  assert(!conn->server); + +  if (!nghttp3_client_stream_bidi(stream_id)) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream == NULL) { +    return NGHTTP3_ERR_STREAM_NOT_FOUND; +  } + +  if (datalen) { +    buf = nghttp3_mem_malloc(conn->mem, datalen); +    if (buf == NULL) { +      return NGHTTP3_ERR_NOMEM; +    } + +    memcpy(buf, data, datalen); +  } + +  frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE; +  frent.fr.priority_update.pri_elem_id = stream_id; +  frent.fr.priority_update.data = buf; +  frent.fr.priority_update.datalen = datalen; + +  return nghttp3_stream_frq_add(conn->tx.ctrl, &frent); +} + +int nghttp3_conn_set_server_stream_priority_versioned(nghttp3_conn *conn, +                                                      int64_t stream_id, +                                                      int pri_version, +                                                      const nghttp3_pri *pri) { +  nghttp3_stream *stream; +  (void)pri_version; + +  assert(conn->server); +  assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); +  assert(pri->inc == 0 || pri->inc == 1); + +  if (!nghttp3_client_stream_bidi(stream_id)) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  stream = nghttp3_conn_find_stream(conn, stream_id); +  if (stream == NULL) { +    return NGHTTP3_ERR_STREAM_NOT_FOUND; +  } + +  stream->flags |= NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET; + +  return conn_update_stream_priority(conn, stream, pri); +} + +int nghttp3_conn_is_drained(nghttp3_conn *conn) { +  assert(conn->server); + +  return (conn->flags & NGHTTP3_CONN_FLAG_SHUTDOWN_COMMENCED) && +         conn->remote.bidi.num_streams == 0 && +         nghttp3_stream_outq_write_done(conn->tx.ctrl) && +         nghttp3_ringbuf_len(&conn->tx.ctrl->frq) == 0; +} + +void nghttp3_settings_default_versioned(int settings_version, +                                        nghttp3_settings *settings) { +  (void)settings_version; + +  memset(settings, 0, sizeof(nghttp3_settings)); +  settings->max_field_section_size = NGHTTP3_VARINT_MAX; +  settings->qpack_encoder_max_dtable_capacity = +    NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_conn.h b/contrib/libs/nghttp3/lib/nghttp3_conn.h new file mode 100644 index 00000000000..1218ba508ba --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_conn.h @@ -0,0 +1,207 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_CONN_H +#define NGHTTP3_CONN_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_stream.h" +#include "nghttp3_map.h" +#include "nghttp3_qpack.h" +#include "nghttp3_tnode.h" +#include "nghttp3_idtr.h" +#include "nghttp3_gaptr.h" + +#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic +   table size for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384 + +/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of +   blocked streams for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 + +/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_CONN_FLAG_NONE 0x0000u +/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has +   been received. */ +#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001u +/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has +   opened. */ +#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002u +/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder +   stream has opened. */ +#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004u +/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder +   stream has opened. */ +#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008u +/* NGHTTP3_CONN_FLAG_SHUTDOWN_COMMENCED is set when graceful shutdown +   has started. */ +#define NGHTTP3_CONN_FLAG_SHUTDOWN_COMMENCED 0x0010u +/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has +   received. */ +#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020u +/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has +   been submitted for transmission. */ +#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040u + +typedef struct nghttp3_chunk { +  nghttp3_opl_entry oplent; +} nghttp3_chunk; + +nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent); + +struct nghttp3_conn { +  nghttp3_objalloc out_chunk_objalloc; +  nghttp3_objalloc stream_objalloc; +  nghttp3_callbacks callbacks; +  nghttp3_map streams; +  nghttp3_qpack_decoder qdec; +  nghttp3_qpack_encoder qenc; +  nghttp3_pq qpack_blocked_streams; +  struct { +    nghttp3_pq spq; +  } sched[NGHTTP3_URGENCY_LEVELS]; +  const nghttp3_mem *mem; +  void *user_data; +  int server; +  uint16_t flags; + +  struct { +    nghttp3_settings settings; +    struct { +      /* max_pushes is the number of push IDs that local endpoint can +         issue.  This field is used by server only and used just for +         validation */ +      uint64_t max_pushes; +    } 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; +      /* num_streams is the number of client initiated bidirectional +         streams that are currently open.  This field is for server +         use only. */ +      size_t num_streams; +    } bidi; +    nghttp3_settings settings; +  } remote; + +  struct { +    /* goaway_id is the latest ID received in GOAWAY frame. */ +    int64_t goaway_id; + +    int64_t max_stream_id_bidi; + +    /* pri_fieldbuf is a buffer to store incoming Priority Field Value +       in PRIORITY_UPDATE frame. */ +    uint8_t pri_fieldbuf[8]; +    /* pri_fieldlen is the number of bytes written into +       pri_fieldbuf. */ +    size_t pri_fieldbuflen; +  } rx; + +  struct { +    struct { +      nghttp3_buf rbuf; +      nghttp3_buf ebuf; +    } qpack; +    nghttp3_stream *ctrl; +    nghttp3_stream *qenc; +    nghttp3_stream *qdec; +    /* goaway_id is the latest ID sent in GOAWAY frame. */ +    int64_t goaway_id; +  } tx; +}; + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, +                               int64_t stream_id); + +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_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_data(nghttp3_conn *conn, nghttp3_stream *stream, +                         const uint8_t *data, size_t datalen); + +int nghttp3_conn_on_priority_update(nghttp3_conn *conn, +                                    const nghttp3_frame_priority_update *fr); + +nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, +                                      nghttp3_stream *stream, +                                      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_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, +                                         nghttp3_stream *stream); + +void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +/* + * nghttp3_conn_get_next_tx_stream returns next stream to send.  It + * returns NULL if there is no such stream. + */ +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); + +#endif /* !defined(NGHTTP3_CONN_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_conv.c b/contrib/libs/nghttp3/lib/nghttp3_conv.c new file mode 100644 index 00000000000..6439a6d7829 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_conv.c @@ -0,0 +1,128 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_conv.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp3_str.h" +#include "nghttp3_unreachable.h" + +const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p) { +  union { +    uint8_t n8; +    uint16_t n16; +    uint32_t n32; +    uint64_t n64; +  } n; + +  switch (*p >> 6) { +  case 0: +    *dest = *p++; +    return p; +  case 1: +    memcpy(&n, p, 2); +    n.n8 &= 0x3f; +    *dest = ntohs(n.n16); + +    return p + 2; +  case 2: +    memcpy(&n, p, 4); +    n.n8 &= 0x3f; +    *dest = ntohl(n.n32); + +    return p + 4; +  case 3: +    memcpy(&n, p, 8); +    n.n8 &= 0x3f; +    *dest = (int64_t)nghttp3_ntohl64(n.n64); + +    return p + 8; +  default: +    nghttp3_unreachable(); +  } +} + +int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } + +size_t nghttp3_get_varintlen(const uint8_t *p) { +  return (size_t)(1u << (*p >> 6)); +} + +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_uint32be(uint8_t *p, uint32_t n) { +  n = htonl(n); +  return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +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_varintlen(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/contrib/libs/nghttp3/lib/nghttp3_conv.h b/contrib/libs/nghttp3/lib/nghttp3_conv.h new file mode 100644 index 00000000000..40f5d4de782 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_conv.h @@ -0,0 +1,194 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_CONV_H +#define NGHTTP3_CONV_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#ifdef HAVE_ARPA_INET_H +#  include <arpa/inet.h> +#endif /* defined(HAVE_ARPA_INET_H) */ + +#ifdef HAVE_NETINET_IN_H +#  include <netinet/in.h> +#endif /* defined(HAVE_NETINET_IN_H) */ + +#ifdef HAVE_BYTESWAP_H +#  include <byteswap.h> +#endif /* defined(HAVE_BYTESWAP_H) */ + +#ifdef HAVE_ENDIAN_H +#  include <endian.h> +#endif /* defined(HAVE_ENDIAN_H) */ + +#ifdef HAVE_SYS_ENDIAN_H +#  include <sys/endian.h> +#endif /* defined(HAVE_SYS_ENDIAN_H) */ + +#ifdef __APPLE__ +#  include <libkern/OSByteOrder.h> +#endif /* defined(__APPLE__) */ + +#include <nghttp3/nghttp3.h> + +#if HAVE_DECL_BE64TOH +#  define nghttp3_ntohl64(N) be64toh(N) +#  define nghttp3_htonl64(N) htobe64(N) +#else /* !HAVE_DECL_BE64TOH */ +#  ifdef WORDS_BIGENDIAN +#    define nghttp3_ntohl64(N) (N) +#    define nghttp3_htonl64(N) (N) +#  else /* !defined(WORDS_BIGENDIAN) */ +#    if HAVE_DECL_BSWAP_64 +#      define nghttp3_bswap64 bswap_64 +#    elif defined(WIN32) +#      define nghttp3_bswap64 _byteswap_uint64 +#    elif defined(__APPLE__) +#      define nghttp3_bswap64 OSSwapInt64 +#    else /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#      define nghttp3_bswap64(N)                                               \ +        ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +#    endif /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#    define nghttp3_ntohl64(N) nghttp3_bswap64(N) +#    define nghttp3_htonl64(N) nghttp3_bswap64(N) +#  endif /* !defined(WORDS_BIGENDIAN) */ +#endif   /* !HAVE_DECL_BE64TOH */ + +#ifdef 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 /* !defined(_MSC_VER) */ +#    define STIN static inline +#  endif /* !defined(_MSC_VER) */ + +STIN uint32_t htonl(uint32_t hostlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&res; +  *p++ = (unsigned char)(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++ = (unsigned char)(hostshort >> 8); +  *p = hostshort & 0xffu; +  return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&netlong; +  res = (uint32_t)(*p++ << 24); +  res += (uint32_t)(*p++ << 16); +  res += (uint32_t)(*p++ << 8); +  res += *p; +  return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { +  uint16_t res; +  unsigned char *p = (unsigned char *)&netshort; +  res = (uint16_t)(*p++ << 8); +  res += *p; +  return res; +} + +#endif /* defined(WIN32) */ + +/* + * nghttp3_get_varint reads variable-length unsigned integer from |p|, + * and stores it in the buffer pointed by |dest| in host byte order. + * It returns |p| plus the number of bytes read from |p|. + */ +const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p); + +/* + * nghttp3_get_varint_fb reads first byte of encoded variable-length + * integer from |p|. + */ +int64_t nghttp3_get_varint_fb(const uint8_t *p); + +/* + * nghttp3_get_varintlen returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t nghttp3_get_varintlen(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_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_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_varintlen returns the required number of bytes to + * encode |n|. + */ +size_t nghttp3_put_varintlen(int64_t n); + +/* + * nghttp3_ord_stream_id returns the ordinal number of |stream_id|. + */ +uint64_t nghttp3_ord_stream_id(int64_t stream_id); + +/* + * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from + * a value produced by nghttp3_pri_to_uint8. + */ +#define NGHTTP3_PRI_INC_MASK (1 << 7) + +#endif /* !defined(NGHTTP3_CONV_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_debug.c b/contrib/libs/nghttp3/lib/nghttp3_debug.c new file mode 100644 index 00000000000..0235217e962 --- /dev/null +++ b/contrib/libs/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 <stdio.h> + +#ifdef DEBUGBUILD + +static void nghttp3_default_debug_vfprintf_callback(const char *fmt, +                                                    va_list args) { +  vfprintf(stderr, fmt, args); +} + +static nghttp3_debug_vprintf_callback static_debug_vprintf_callback = +  nghttp3_default_debug_vfprintf_callback; + +void nghttp3_debug_vprintf(const char *format, ...) { +  if (static_debug_vprintf_callback) { +    va_list args; +    va_start(args, format); +    static_debug_vprintf_callback(format, args); +    va_end(args); +  } +} + +void nghttp3_set_debug_vprintf_callback( +  nghttp3_debug_vprintf_callback debug_vprintf_callback) { +  static_debug_vprintf_callback = debug_vprintf_callback; +} + +#else /* !defined(DEBUGBUILD) */ + +void nghttp3_set_debug_vprintf_callback( +  nghttp3_debug_vprintf_callback debug_vprintf_callback) { +  (void)debug_vprintf_callback; +} + +#endif /* !defined(DEBUGBUILD) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_debug.h b/contrib/libs/nghttp3/lib/nghttp3_debug.h new file mode 100644 index 00000000000..d73bf8ecf31 --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#ifdef DEBUGBUILD +#  define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__) +void nghttp3_debug_vprintf(const char *format, ...); +#else /* !defined(DEBUGBUILD) */ +#  define DEBUGF(...)                                                          \ +    do {                                                                       \ +    } while (0) +#endif /* !defined(DEBUGBUILD) */ + +#endif /* !defined(NGHTTP3_DEBUG_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_err.c b/contrib/libs/nghttp3/lib/nghttp3_err.c new file mode 100644 index 00000000000..0d596bfab6d --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_err.c @@ -0,0 +1,127 @@ +/* + * 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_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_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_QPACK_FATAL: +    return "ERR_QPACK_FATAL"; +  case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: +    return "ERR_QPACK_HEADER_TOO_LARGE"; +  case NGHTTP3_ERR_STREAM_NOT_FOUND: +    return "ERR_STREAM_NOT_FOUND"; +  case NGHTTP3_ERR_CONN_CLOSING: +    return "ERR_CONN_CLOSING"; +  case NGHTTP3_ERR_STREAM_DATA_OVERFLOW: +    return "ERR_STREAM_DATA_OVERFLOW"; +  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: +  case NGHTTP3_ERR_NOMEM: +  case NGHTTP3_ERR_CALLBACK_FAILURE: +  case NGHTTP3_ERR_QPACK_FATAL: +  case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: +  case NGHTTP3_ERR_STREAM_DATA_OVERFLOW: +    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; +  case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: +  case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING: +    return NGHTTP3_H3_MESSAGE_ERROR; +  default: +    return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; +  } +} + +int nghttp3_err_is_fatal(int liberr) { return liberr < NGHTTP3_ERR_FATAL; } diff --git a/contrib/libs/nghttp3/lib/nghttp3_err.h b/contrib/libs/nghttp3/lib/nghttp3_err.h new file mode 100644 index 00000000000..6f8205cc17c --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#endif /* !defined(NGHTTP3_ERR_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_frame.c b/contrib/libs/nghttp3/lib/nghttp3_frame.c new file mode 100644 index 00000000000..1d87e448d88 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_frame.c @@ -0,0 +1,203 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_frame.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp3_conv.h" +#include "nghttp3_str.h" + +uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) { +  p = nghttp3_put_varint(p, hd->type); +  p = nghttp3_put_varint(p, hd->length); +  return p; +} + +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { +  return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(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_varintlen((int64_t)fr->iv[i].id) + +                  nghttp3_put_varintlen((int64_t)fr->iv[i].value); +  } + +  *ppayloadlen = (int64_t)payloadlen; + +  return nghttp3_put_varintlen(NGHTTP3_FRAME_SETTINGS) + +         nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, +                                    const nghttp3_frame_goaway *fr) { +  p = nghttp3_frame_write_hd(p, &fr->hd); +  p = nghttp3_put_varint(p, fr->id); + +  return p; +} + +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, +                                      const nghttp3_frame_goaway *fr) { +  size_t payloadlen = nghttp3_put_varintlen(fr->id); + +  *ppayloadlen = (int64_t)payloadlen; + +  return nghttp3_put_varintlen(NGHTTP3_FRAME_GOAWAY) + +         nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; +} + +uint8_t * +nghttp3_frame_write_priority_update(uint8_t *p, +                                    const nghttp3_frame_priority_update *fr) { +  p = nghttp3_frame_write_hd(p, &fr->hd); +  p = nghttp3_put_varint(p, fr->pri_elem_id); +  if (fr->datalen) { +    p = nghttp3_cpymem(p, fr->data, fr->datalen); +  } + +  return p; +} + +size_t nghttp3_frame_write_priority_update_len( +  int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr) { +  size_t payloadlen = nghttp3_put_varintlen(fr->pri_elem_id) + fr->datalen; + +  *ppayloadlen = (int64_t)payloadlen; + +  return nghttp3_put_varintlen(fr->hd.type) + +         nghttp3_put_varintlen((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); +        nghttp3_downcase(data, nva[i].namelen); +      } +      p->name = data; +      p->namelen = nva[i].namelen; +      data[p->namelen] = '\0'; +      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_priority_update_free(nghttp3_frame_priority_update *fr, +                                        const nghttp3_mem *mem) { +  if (fr == NULL) { +    return; +  } + +  nghttp3_mem_free(mem, fr->data); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_frame.h b/contrib/libs/nghttp3/lib/nghttp3_frame.h new file mode 100644 index 00000000000..e216967d740 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_frame.h @@ -0,0 +1,230 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_FRAME_H +#define NGHTTP3_FRAME_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_buf.h" + +#define NGHTTP3_FRAME_DATA 0x00 +#define NGHTTP3_FRAME_HEADERS 0x01 +#define NGHTTP3_FRAME_CANCEL_PUSH 0x03 +#define NGHTTP3_FRAME_SETTINGS 0x04 +#define NGHTTP3_FRAME_PUSH_PROMISE 0x05 +#define NGHTTP3_FRAME_GOAWAY 0x07 +#define NGHTTP3_FRAME_MAX_PUSH_ID 0x0d +/* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */ +#define NGHTTP3_FRAME_PRIORITY_UPDATE 0x0f0700 +#define NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID 0x0f0701 + +/* Frame types that are reserved for HTTP/2, and must not be used in +   HTTP/3. */ +#define NGHTTP3_H2_FRAME_PRIORITY 0x02 +#define NGHTTP3_H2_FRAME_PING 0x06 +#define NGHTTP3_H2_FRAME_WINDOW_UPDATE 0x08 +#define NGHTTP3_H2_FRAME_CONTINUATION 0x9 + +typedef struct nghttp3_frame_hd { +  int64_t type; +  int64_t length; +} nghttp3_frame_hd; + +typedef struct nghttp3_frame_data { +  nghttp3_frame_hd hd; +} nghttp3_frame_data; + +typedef struct nghttp3_frame_headers { +  nghttp3_frame_hd hd; +  nghttp3_nv *nva; +  size_t nvlen; +} nghttp3_frame_headers; + +#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06 +#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 +#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 +#define NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL 0x08 +#define NGHTTP3_SETTINGS_ID_H3_DATAGRAM 0x33 + +#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2 +#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3 +#define NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE 0x4 +#define NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE 0x5 + +typedef struct nghttp3_settings_entry { +  uint64_t id; +  uint64_t value; +} nghttp3_settings_entry; + +typedef struct nghttp3_frame_settings { +  nghttp3_frame_hd hd; +  size_t niv; +  nghttp3_settings_entry iv[1]; +} nghttp3_frame_settings; + +typedef struct nghttp3_frame_goaway { +  nghttp3_frame_hd hd; +  int64_t id; +} nghttp3_frame_goaway; + +typedef struct nghttp3_frame_priority_update { +  nghttp3_frame_hd hd; +  /* pri_elem_id is stream ID if hd.type == +     NGHTTP3_FRAME_PRIORITY_UPDATE.  It is push ID if hd.type == +     NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID.  It is undefined +     otherwise. */ +  int64_t pri_elem_id; +  /* When sending this frame, data should point to the buffer +     containing a serialized priority field value and its length is +     set to datalen.  On reception, pri contains the decoded priority +     header value. */ +  union { +    nghttp3_pri pri; +    struct { +      uint8_t *data; +      size_t datalen; +    }; +  }; +} nghttp3_frame_priority_update; + +typedef union nghttp3_frame { +  nghttp3_frame_hd hd; +  nghttp3_frame_data data; +  nghttp3_frame_headers headers; +  nghttp3_frame_settings settings; +  nghttp3_frame_goaway goaway; +  nghttp3_frame_priority_update priority_update; +} nghttp3_frame; + +/* + * nghttp3_frame_write_hd writes frame header |hd| to |dest|.  This + * function assumes that |dest| has enough space to write |hd|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_hd_len returns the number of bytes required to + * write |hd|.  hd->length must be set. + */ +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_settings(uint8_t *dest, +                                      const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_settings_len returns the number of bytes + * required to write |fr|.  fr->hd.length is ignored.  This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, +                                        const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_goaway writes GOAWAY 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_goaway(uint8_t *dest, +                                    const nghttp3_frame_goaway *fr); + +/* + * nghttp3_frame_write_goaway_len returns the number of bytes required + * to write |fr|.  fr->hd.length is ignored.  This function stores + * payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, +                                      const nghttp3_frame_goaway *fr); + +/* + * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE 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_priority_update(uint8_t *dest, +                                    const nghttp3_frame_priority_update *fr); + +/* + * nghttp3_frame_write_priority_update_len returns the number of bytes + * required to write |fr|.  fr->hd.length is ignored.  This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_priority_update_len( +  int64_t *ppayloadlen, const nghttp3_frame_priority_update *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_priority_update_free frees memory allocated for |fr|. + * This function should only be called for an outgoing frame. + */ +void nghttp3_frame_priority_update_free(nghttp3_frame_priority_update *fr, +                                        const nghttp3_mem *mem); + +#endif /* !defined(NGHTTP3_FRAME_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_gaptr.c b/contrib/libs/nghttp3/lib/nghttp3_gaptr.c new file mode 100644 index 00000000000..20eed5faa2b --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_gaptr.c @@ -0,0 +1,163 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_gaptr.h" + +#include <string.h> +#include <assert.h> + +void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { +  nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range), +                   mem); + +  gaptr->mem = mem; +} + +static int gaptr_gap_init(nghttp3_gaptr *gaptr) { +  nghttp3_range range = {0, UINT64_MAX}; + +  return nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); +} + +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, +                       uint64_t datalen) { +  int rv; +  nghttp3_range k, m, l, r, q = {offset, offset + datalen}; +  nghttp3_ksl_it it; + +  if (nghttp3_ksl_len(&gaptr->gap) == 0) { +    rv = gaptr_gap_init(gaptr); +    if (rv != 0) { +      return rv; +    } +  } + +  it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, +                                      nghttp3_ksl_range_exclusive_compar); + +  for (; !nghttp3_ksl_it_end(&it);) { +    k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); +    m = nghttp3_range_intersect(&q, &k); +    if (!nghttp3_range_len(&m)) { +      break; +    } + +    if (nghttp3_range_eq(&k, &m)) { +      nghttp3_ksl_remove_hint(&gaptr->gap, &it, &it, &k); +      continue; +    } + +    nghttp3_range_cut(&l, &r, &k, &m); + +    if (nghttp3_range_len(&l)) { +      nghttp3_ksl_update_key(&gaptr->gap, &k, &l); + +      if (nghttp3_range_len(&r)) { +        rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL); +        if (rv != 0) { +          return rv; +        } +      } +    } else if (nghttp3_range_len(&r)) { +      nghttp3_ksl_update_key(&gaptr->gap, &k, &r); +    } + +    nghttp3_ksl_it_next(&it); +  } + +  return 0; +} + +uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { +  nghttp3_ksl_it it; + +  if (nghttp3_ksl_len(&gaptr->gap) == 0) { +    return 0; +  } + +  it = nghttp3_ksl_begin(&gaptr->gap); + +  return ((nghttp3_range *)nghttp3_ksl_it_key(&it))->begin; +} + +nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, +                                                uint64_t offset) { +  nghttp3_range q = {offset, offset + 1}; +  nghttp3_ksl_it it; + +  if (nghttp3_ksl_len(&gaptr->gap) == 0) { +    nghttp3_range r = {0, UINT64_MAX}; +    return r; +  } + +  it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, +                                      nghttp3_ksl_range_exclusive_compar); + +  assert(!nghttp3_ksl_it_end(&it)); + +  return *(nghttp3_range *)nghttp3_ksl_it_key(&it); +} + +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, +                            uint64_t datalen) { +  nghttp3_range q = {offset, offset + datalen}; +  nghttp3_ksl_it it; +  nghttp3_range m; + +  if (nghttp3_ksl_len(&gaptr->gap) == 0) { +    return 0; +  } + +  it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, +                                      nghttp3_ksl_range_exclusive_compar); +  m = nghttp3_range_intersect(&q, (nghttp3_range *)nghttp3_ksl_it_key(&it)); + +  return nghttp3_range_len(&m) == 0; +} + +void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) { +  nghttp3_ksl_it it; +  nghttp3_range r; + +  if (nghttp3_ksl_len(&gaptr->gap) == 0) { +    return; +  } + +  it = nghttp3_ksl_begin(&gaptr->gap); + +  assert(!nghttp3_ksl_it_end(&it)); + +  r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + +  nghttp3_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_gaptr.h b/contrib/libs/nghttp3/lib/nghttp3_gaptr.h new file mode 100644 index 00000000000..7578fdc14f6 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_gaptr.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_GAPTR_H +#define NGHTTP3_GAPTR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" +#include "nghttp3_ksl.h" +#include "nghttp3_range.h" + +/* + * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct nghttp3_gaptr { +  /* gap maintains the range of offset which is not pushed +     yet. Initially, its range is [0, UINT64_MAX).  "gap" is the range +     that is not pushed yet. */ +  nghttp3_ksl gap; +  /* mem is custom memory allocator */ +  const nghttp3_mem *mem; +} nghttp3_gaptr; + +/* + * nghttp3_gaptr_init initializes |gaptr|. + */ +void 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 pushes the range [offset, offset + datalen). + * + * 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, uint64_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 first gap which + * includes or comes after |offset|. + */ +nghttp3_range 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, +                            uint64_t datalen); + +/* + * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if + * the range is pushed.  This function assumes that at least one gap + * exists. + */ +void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr); + +#endif /* !defined(NGHTTP3_GAPTR_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_http.c b/contrib/libs/nghttp3/lib/nghttp3_http.c new file mode 100644 index 00000000000..38092cfb7c3 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_http.c @@ -0,0 +1,1024 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_http.h" + +#include <string.h> +#include <assert.h> + +#ifdef __AVX2__ +#  include <immintrin.h> +#endif /* __AVX2__ */ + +#include "nghttp3_stream.h" +#include "nghttp3_macro.h" +#include "nghttp3_conv.h" +#include "nghttp3_unreachable.h" +#include "sfparse/sfparse.h" + +static uint8_t downcase(uint8_t c) { +  return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +} + +/* + * memieq returns 1 if the data pointed by |a| of length |n| equals to + * |b| of the same length in case-insensitive manner.  The data + * pointed by |a| must not include upper cased letters (A-Z). + */ +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 (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 check_pseudo_header(nghttp3_http_state *http, +                               const nghttp3_qpack_nv *nv, uint32_t flag) { +  if ((http->flags & flag) || nv->value->len == 0) { +    return 0; +  } +  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_flags(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 is_ws(uint8_t c) { +  switch (c) { +  case ' ': +  case '\t': +    return 1; +  default: +    return 0; +  } +} + +int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, +                                size_t valuelen) { +  nghttp3_pri pri = *dest; +  sf_parser sfp; +  sf_vec key; +  sf_value val; +  int rv; + +  sf_parser_init(&sfp, value, valuelen); + +  for (;;) { +    rv = sf_parser_dict(&sfp, &key, &val); +    if (rv != 0) { +      if (rv == SF_ERR_EOF) { +        break; +      } + +      return NGHTTP3_ERR_INVALID_ARGUMENT; +    } + +    if (key.len != 1) { +      continue; +    } + +    switch (key.base[0]) { +    case 'i': +      if (val.type != SF_TYPE_BOOLEAN) { +        return NGHTTP3_ERR_INVALID_ARGUMENT; +      } + +      pri.inc = (uint8_t)val.boolean; + +      break; +    case 'u': +      if (val.type != SF_TYPE_INTEGER || val.integer < NGHTTP3_URGENCY_HIGH || +          NGHTTP3_URGENCY_LOW < val.integer) { +        return NGHTTP3_ERR_INVALID_ARGUMENT; +      } + +      pri.urgency = (uint32_t)val.integer; + +      break; +    } +  } + +  *dest = pri; + +  return 0; +} + +int nghttp3_pri_parse_priority_versioned(int pri_version, nghttp3_pri *dest, +                                         const uint8_t *value, +                                         size_t valuelen) { +  (void)pri_version; + +  return nghttp3_http_parse_priority(dest, value, valuelen); +} + +/* 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; +} + +/* Generated by genmethodchartbl.py */ +static char VALID_METHOD_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 /* @    */, 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 /* ^    */, 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 */ +}; + +static int check_method(const uint8_t *value, size_t len) { +  const uint8_t *last; +  if (len == 0) { +    return 0; +  } +  for (last = value + len; value != last; ++value) { +    if (!VALID_METHOD_CHARS[*value]) { +      return 0; +    } +  } +  return 1; +} + +/* Generated by genpathchartbl.py */ +static char VALID_PATH_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 /* !    */, 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 */ +}; + +static int check_path(const uint8_t *value, size_t len) { +  const uint8_t *last; +  for (last = value + len; value != last; ++value) { +    if (!VALID_PATH_CHARS[*value]) { +      return 0; +    } +  } +  return 1; +} + +static int http_request_on_header(nghttp3_http_state *http, +                                  nghttp3_qpack_nv *nv, int trailers, +                                  int connect_protocol) { +  nghttp3_pri pri; + +  switch (nv->token) { +  case NGHTTP3_QPACK_TOKEN__AUTHORITY: +    if (!check_authority(nv->value->base, nv->value->len) || +        !check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  case NGHTTP3_QPACK_TOKEN__METHOD: +    if (!check_method(nv->value->base, nv->value->len) || +        !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)) { +          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_path(nv->value->base, nv->value->len) || +        !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_scheme(nv->value->base, nv->value->len) || +        !check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    /* scheme is case-insensitive: +       https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 */ +    if (lstrieq("http", nv->value->base, nv->value->len) || +        lstrieq("https", nv->value->base, nv->value->len)) { +      http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; +    } +    break; +  case NGHTTP3_QPACK_TOKEN__PROTOCOL: +    if (!connect_protocol || +        !nghttp3_check_header_value(nv->value->base, nv->value->len) || +        !check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  case NGHTTP3_QPACK_TOKEN_HOST: +    if (!check_authority(nv->value->base, nv->value->len)) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } +    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { +    /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender +       MUST NOT generate a trailer that contains a field necessary for +       message framing (e.g., Transfer-Encoding and Content-Length), +       ... */ +    if (trailers) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } +    if (http->content_length != -1) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    http->content_length = parse_uint(nv->value->base, nv->value->len); +    if (http->content_length == -1) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  } +  /* disallowed header fields */ +  case NGHTTP3_QPACK_TOKEN_CONNECTION: +  case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: +  case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: +  case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: +  case NGHTTP3_QPACK_TOKEN_UPGRADE: +    return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +  case NGHTTP3_QPACK_TOKEN_TE: +    if (!lstrieq("trailers", nv->value->base, nv->value->len)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  case NGHTTP3_QPACK_TOKEN_PRIORITY: +    if (!nghttp3_check_header_value(nv->value->base, nv->value->len)) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } + +    if (trailers || (http->flags & NGHTTP3_HTTP_FLAG_BAD_PRIORITY)) { +      break; +    } + +    pri = http->pri; + +    if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == +        0) { +      http->pri = pri; +      http->flags |= NGHTTP3_HTTP_FLAG_PRIORITY; +      break; +    } + +    http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY; +    http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY; + +    break; +  default: +    if (nv->name->base[0] == ':') { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } + +    if (!nghttp3_check_header_value(nv->value->base, nv->value->len)) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } +  } + +  return 0; +} + +static int http_response_on_header(nghttp3_http_state *http, +                                   nghttp3_qpack_nv *nv, int trailers) { +  switch (nv->token) { +  case NGHTTP3_QPACK_TOKEN__STATUS: { +    if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS) || +        nv->value->len != 3) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); +    if (http->status_code < 100 || http->status_code == 101) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +    break; +  } +  case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { +    /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender +       MUST NOT generate a trailer that contains a field necessary for +       message framing (e.g., Transfer-Encoding and Content-Length), +       ... */ +    if (trailers) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } +    if (http->status_code == 204) { +      /* content-length header field in 204 response is prohibited by +         RFC 7230.  But some widely used servers send content-length: +         0.  Until they get fixed, we ignore it. */ +      if (/* Found multiple content-length field */ +          http->content_length != -1 || +          !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 || +        /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ +        (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 (!nghttp3_check_header_value(nv->value->base, nv->value->len)) { +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    } +  } + +  return 0; +} + +static int http_check_nonempty_header_name(const uint8_t *name, size_t len); + +int nghttp3_http_on_header(nghttp3_http_state *http, nghttp3_qpack_nv *nv, +                           int request, int trailers, int connect_protocol) { +  if (nv->name->len == 0) { +    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + +    return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +  } + +  if (nv->name->base[0] == ':') { +    /* pseudo header must have a valid token. */ +    if (nv->token == -1 || trailers || +        (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +  } else { +    http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + +    switch (http_check_nonempty_header_name(nv->name->base, nv->name->len)) { +    case 0: +      return NGHTTP3_ERR_REMOVE_HTTP_HEADER; +    case -1: +      /* header field name must be lower-cased without exception */ +      return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; +    } +  } + +  assert(nv->name->len > 0); + +  if (request) { +    return http_request_on_header(http, nv, trailers, connect_protocol); +  } + +  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_flags(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 = (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 &= ~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->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 /* @    */, +  -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 /* ^    */, +  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; +} + +/* http_check_nonempty_header_name validates regular header name +   pointed by |name| of length |len|.  |len| must be greater than +   zero.  This function returns 1 if it succeeds, or -1 if the name +   contains a character in [A-Z], otherwise 0. */ +static int http_check_nonempty_header_name(const uint8_t *name, size_t len) { +  const uint8_t *last; +  int rv; + +  for (last = name + len; name != last; ++name) { +    rv = VALID_HD_NAME_CHARS[*name]; +    if (rv != 1) { +      return rv; +    } +  } + +  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 */ +}; + +#ifdef __AVX2__ +static int contains_bad_header_value_char_avx2(const uint8_t *first, +                                               const uint8_t *last) { +  const __m256i ctll = _mm256_set1_epi8(0x00 - 1); +  const __m256i ctlr = _mm256_set1_epi8(0x1f + 1); +  const __m256i ht = _mm256_set1_epi8('\t'); +  const __m256i del = _mm256_set1_epi8(0x7f); +  __m256i s, x; +  uint32_t m; + +  for (; first != last; first += 32) { +    s = _mm256_loadu_si256((void *)first); + +    x = _mm256_andnot_si256( +      _mm256_cmpeq_epi8(s, ht), +      _mm256_and_si256(_mm256_cmpgt_epi8(s, ctll), _mm256_cmpgt_epi8(ctlr, s))); +    x = _mm256_or_si256(_mm256_cmpeq_epi8(s, del), x); + +    m = (uint32_t)_mm256_movemask_epi8(x); +    if (m) { +      return 1; +    } +  } + +  return 0; +} +#endif /* __AVX2__ */ + +int nghttp3_check_header_value(const uint8_t *value, size_t len) { +  const uint8_t *last; +#ifdef __AVX2__ +  const uint8_t *last32; +#endif /* __AVX2__ */ + +  switch (len) { +  case 0: +    return 1; +  case 1: +    return !is_ws(*value); +  default: +    if (is_ws(*value) || is_ws(*(value + len - 1))) { +      return 0; +    } +  } + +  last = value + len; + +#ifdef __AVX2__ +  if (len >= 32) { +    last32 = value + (len & ~0x1fu); +    if (contains_bad_header_value_char_avx2(value, last32)) { +      return 0; +    } + +    value = last32; +  } +#endif /* __AVX2__ */ + +  for (; value != last; ++value) { +    if (!VALID_HD_VALUE_CHARS[*value]) { +      return 0; +    } +  } +  return 1; +} + +int nghttp3_pri_eq(const nghttp3_pri *a, const nghttp3_pri *b) { +  return a->urgency == b->urgency && a->inc == b->inc; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_http.h b/contrib/libs/nghttp3/lib/nghttp3_http.h new file mode 100644 index 00000000000..f0bfc69fbad --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_http.h @@ -0,0 +1,173 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_HTTP_H +#define NGHTTP3_HTTP_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +typedef struct nghttp3_stream nghttp3_stream; + +typedef struct nghttp3_http_state nghttp3_http_state; + +/* HTTP related flags to enforce HTTP semantics */ + +/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_HTTP_FLAG_NONE 0x00u +/* header field seen so far */ +#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01u +#define NGHTTP3_HTTP_FLAG__PATH 0x02u +#define NGHTTP3_HTTP_FLAG__METHOD 0x04u +#define NGHTTP3_HTTP_FLAG__SCHEME 0x08u +/* host is not pseudo header, but we require either host or +   :authority */ +#define NGHTTP3_HTTP_FLAG_HOST 0x10u +#define NGHTTP3_HTTP_FLAG__STATUS 0x20u +/* required header fields for HTTP request except for CONNECT +   method. */ +#define NGHTTP3_HTTP_FLAG_REQ_HEADERS                                          \ +  (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH |                       \ +   NGHTTP3_HTTP_FLAG__SCHEME) +#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40u +/* HTTP method flags */ +#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80u +#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100u +#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200u +#define 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 "/" */ +#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400u +/* path "*" */ +#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800u +/* scheme */ +/* "http" or "https" scheme */ +#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000u +/* set if final response is expected */ +#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000u +/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header +   field is seen. */ +#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000u +/* NGHTTP3_HTTP_FLAG_PRIORITY is set when priority header field is +   processed. */ +#define NGHTTP3_HTTP_FLAG_PRIORITY 0x8000u +/* NGHTTP3_HTTP_FLAG_BAD_PRIORITY is set when an error is encountered +   while parsing priority header field. */ +#define NGHTTP3_HTTP_FLAG_BAD_PRIORITY 0x010000u + +/* + * This function is called when HTTP header field |nv| 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.  |connect_protocol| + * is nonzero if Extended CONNECT Method is enabled. + * + * 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, nghttp3_qpack_nv *nv, +                           int request, int trailers, int connect_protocol); + +/* + * 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); + +/** + * @function + * + * `nghttp3_http_parse_priority` parses priority HTTP header field + * stored in the buffer pointed by |value| of length |len|.  If it + * successfully processed header field value, it stores the result + * into |*dest|.  This function just overwrites what it sees in the + * header field value and does not initialize any field in |*dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + *     The function could not parse the provided value. + */ +int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, +                                size_t len); + +int nghttp3_pri_eq(const nghttp3_pri *a, const nghttp3_pri *b); + +#endif /* !defined(NGHTTP3_HTTP_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_idtr.c b/contrib/libs/nghttp3/lib/nghttp3_idtr.c new file mode 100644 index 00000000000..ffed3064d2b --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_idtr.c @@ -0,0 +1,67 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_idtr.h" + +#include <assert.h> + +void nghttp3_idtr_init(nghttp3_idtr *idtr, const nghttp3_mem *mem) { +  nghttp3_gaptr_init(&idtr->gap, mem); +} + +void nghttp3_idtr_free(nghttp3_idtr *idtr) { +  if (idtr == NULL) { +    return; +  } + +  nghttp3_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to an internal ID. + */ +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; + +  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; + +  q = id_from_stream_id(stream_id); + +  return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_idtr.h b/contrib/libs/nghttp3/lib/nghttp3_idtr.h new file mode 100644 index 00000000000..8ba15fc810c --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_idtr.h @@ -0,0 +1,77 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_IDTR_H +#define NGHTTP3_IDTR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" +#include "nghttp3_gaptr.h" + +/* + * nghttp3_idtr tracks the usage of stream ID. + */ +typedef struct nghttp3_idtr { +  /* gap maintains the range of an internal ID which is not used yet. +     Initially, its range is [0, UINT64_MAX).  The internal ID and +     stream ID are in the different number spaces.  See +     id_from_stream_id to convert a stream ID to an internal ID. */ +  nghttp3_gaptr gap; +} nghttp3_idtr; + +/* + * nghttp3_idtr_init initializes |idtr|. + */ +void nghttp3_idtr_init(nghttp3_idtr *idtr, 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 use. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_STREAM_IN_USE + *     |stream_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 returns nonzero if |stream_id| is in use. + */ +int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id); + +#endif /* !defined(NGHTTP3_IDTR_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_ksl.c b/contrib/libs/nghttp3/lib/nghttp3_ksl.c new file mode 100644 index 00000000000..a3b5fbcb05f --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_ksl.c @@ -0,0 +1,833 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_ksl.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" +#include "nghttp3_range.h" + +static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + +nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent); + +static size_t ksl_nodelen(size_t keylen) { +  assert(keylen >= sizeof(uint64_t)); + +  return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0x7u) & +         ~(uintptr_t)0x7u; +} + +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); +} + +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, +                      size_t keylen, const nghttp3_mem *mem) { +  size_t nodelen = ksl_nodelen(keylen); + +  nghttp3_objalloc_init(&ksl->blkalloc, +                        (ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu, mem); + +  ksl->head = NULL; +  ksl->front = ksl->back = NULL; +  ksl->compar = compar; +  ksl->n = 0; +  ksl->keylen = keylen; +  ksl->nodelen = nodelen; +} + +static nghttp3_ksl_blk *ksl_blk_objalloc_new(nghttp3_ksl *ksl) { +  return nghttp3_objalloc_ksl_blk_len_get(&ksl->blkalloc, +                                          ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { +  nghttp3_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(nghttp3_ksl *ksl) { +  nghttp3_ksl_blk *head = ksl_blk_objalloc_new(ksl); + +  if (!head) { +    return NGHTTP3_ERR_NOMEM; +  } + +  head->next = head->prev = NULL; +  head->n = 0; +  head->leaf = 1; + +  ksl->head = head; +  ksl->front = ksl->back = head; + +  return 0; +} + +#ifdef NOMEMPOOL +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { +  size_t i; + +  if (!blk->leaf) { +    for (i = 0; i < blk->n; ++i) { +      ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk); +    } +  } + +  ksl_blk_objalloc_del(ksl, blk); +} +#endif /* defined(NOMEMPOOL) */ + +void nghttp3_ksl_free(nghttp3_ksl *ksl) { +  if (!ksl || !ksl->head) { +    return; +  } + +#ifdef NOMEMPOOL +  ksl_free_blk(ksl, ksl->head); +#endif /* defined(NOMEMPOOL) */ + +  nghttp3_objalloc_free(&ksl->blkalloc); +} + +/* + * 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 = ksl_blk_objalloc_new(ksl); +  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; +  blk->n -= rblk->n; + +  memcpy(rblk->nodes, blk->nodes + ksl->nodelen * blk->n, +         ksl->nodelen * rblk->n); + +  assert(blk->n >= NGHTTP3_KSL_MIN_NBLK); +  assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK); + +  return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes.  The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { +  nghttp3_ksl_node *node; +  nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk; + +  rblk = ksl_split_blk(ksl, lblk); +  if (rblk == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  memmove(blk->nodes + (i + 2) * ksl->nodelen, +          blk->nodes + (i + 1) * ksl->nodelen, +          ksl->nodelen * (blk->n - (i + 1))); + +  node = nghttp3_ksl_nth_node(ksl, blk, i + 1); +  node->blk = rblk; +  ++blk->n; +  ksl_node_set_key(ksl, node, +                   nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + +  node = nghttp3_ksl_nth_node(ksl, blk, i); +  ksl_node_set_key(ksl, node, +                   nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + +  return 0; +} + +/* + * ksl_split_head splits a head (root) block.  It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int ksl_split_head(nghttp3_ksl *ksl) { +  nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; +  nghttp3_ksl_node *node; + +  rblk = ksl_split_blk(ksl, ksl->head); +  if (rblk == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  lblk = ksl->head; + +  nhead = ksl_blk_objalloc_new(ksl); + +  if (nhead == NULL) { +    ksl_blk_objalloc_del(ksl, rblk); +    return NGHTTP3_ERR_NOMEM; +  } + +  nhead->next = nhead->prev = NULL; +  nhead->n = 2; +  nhead->leaf = 0; + +  node = nghttp3_ksl_nth_node(ksl, nhead, 0); +  ksl_node_set_key(ksl, node, +                   nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +  node->blk = lblk; + +  node = nghttp3_ksl_nth_node(ksl, nhead, 1); +  ksl_node_set_key(ksl, node, +                   nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); +  node->blk = rblk; + +  ksl->head = nhead; + +  return 0; +} + +/* + * ksl_insert_node inserts a node whose key is |key| with the + * associated |data| at the index of |i|.  This function assumes that + * the number of nodes contained by |blk| is strictly less than + * NGHTTP3_KSL_MAX_NBLK. + */ +static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, +                            const nghttp3_ksl_key *key, void *data) { +  nghttp3_ksl_node *node; + +  assert(blk->n < NGHTTP3_KSL_MAX_NBLK); + +  memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, +          ksl->nodelen * (blk->n - i)); + +  node = nghttp3_ksl_nth_node(ksl, blk, i); +  ksl_node_set_key(ksl, node, key); +  node->data = data; + +  ++blk->n; +} + +static size_t ksl_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, +                         const nghttp3_ksl_key *key, +                         nghttp3_ksl_compar compar) { +  size_t i; +  nghttp3_ksl_node *node; + +  for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; +       i < blk->n && compar((nghttp3_ksl_key *)node->key, key); +       ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) +    ; + +  return i; +} + +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                       const nghttp3_ksl_key *key, void *data) { +  nghttp3_ksl_blk *blk; +  nghttp3_ksl_node *node; +  size_t i; +  int rv; + +  if (!ksl->head) { +    rv = ksl_head_init(ksl); +    if (rv != 0) { +      return rv; +    } +  } + +  if (ksl->head->n == NGHTTP3_KSL_MAX_NBLK) { +    rv = ksl_split_head(ksl); +    if (rv != 0) { +      return rv; +    } +  } + +  blk = ksl->head; + +  for (;;) { +    i = ksl_search(ksl, blk, key, ksl->compar); + +    if (blk->leaf) { +      if (i < blk->n && +          !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { +        if (it) { +          *it = nghttp3_ksl_end(ksl); +        } + +        return NGHTTP3_ERR_INVALID_ARGUMENT; +      } + +      ksl_insert_node(ksl, blk, i, key, data); +      ++ksl->n; + +      if (it) { +        nghttp3_ksl_it_init(it, ksl, blk, i); +      } + +      return 0; +    } + +    if (i == blk->n) { +      /* This insertion extends the largest key in this subtree. */ +      for (; !blk->leaf;) { +        node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); +        if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { +          rv = ksl_split_node(ksl, blk, blk->n - 1); +          if (rv != 0) { +            return rv; +          } + +          node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); +        } + +        ksl_node_set_key(ksl, node, key); +        blk = node->blk; +      } + +      ksl_insert_node(ksl, blk, blk->n, key, data); +      ++ksl->n; + +      if (it) { +        nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1); +      } + +      return 0; +    } + +    node = nghttp3_ksl_nth_node(ksl, blk, i); + +    if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { +      rv = ksl_split_node(ksl, blk, i); +      if (rv != 0) { +        return rv; +      } + +      if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { +        node = nghttp3_ksl_nth_node(ksl, blk, i + 1); + +        if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { +          ksl_node_set_key(ksl, node, key); +        } +      } +    } + +    blk = node->blk; +  } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { +  memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, +          ksl->nodelen * (blk->n - (i + 1))); + +  --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the head (root) block and it contains just 2 nodes + * before merging 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_node *lnode; +  nghttp3_ksl_blk *lblk, *rblk; + +  assert(i + 1 < blk->n); + +  lnode = nghttp3_ksl_nth_node(ksl, blk, i); + +  lblk = lnode->blk; +  rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk; + +  assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK); + +  memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, +         ksl->nodelen * rblk->n); + +  lblk->n += rblk->n; +  lblk->next = rblk->next; + +  if (lblk->next) { +    lblk->next->prev = lblk; +  } else if (ksl->back == rblk) { +    ksl->back = lblk; +  } + +  ksl_blk_objalloc_del(ksl, rblk); + +  if (ksl->head == blk && blk->n == 2) { +    ksl_blk_objalloc_del(ksl, ksl->head); +    ksl->head = lblk; +  } else { +    ksl_remove_node(ksl, blk, i + 1); +    ksl_node_set_key(ksl, lnode, +                     nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +  } + +  return lblk; +} + +/* + * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i - 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { +  nghttp3_ksl_node *lnode, *rnode; +  nghttp3_ksl_blk *lblk, *rblk; +  size_t n; + +  assert(i > 0); + +  lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1); +  rnode = nghttp3_ksl_nth_node(ksl, blk, i); + +  lblk = lnode->blk; +  rblk = rnode->blk; + +  assert(lblk->n < NGHTTP3_KSL_MAX_NBLK); +  assert(rblk->n > NGHTTP3_KSL_MIN_NBLK); + +  n = (lblk->n + rblk->n + 1) / 2 - lblk->n; + +  assert(n > 0); +  assert(lblk->n <= NGHTTP3_KSL_MAX_NBLK - n); +  assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK + n); + +  memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, ksl->nodelen * n); + +  lblk->n += (uint32_t)n; +  rblk->n -= (uint32_t)n; + +  ksl_node_set_key(ksl, lnode, +                   nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + +  memmove(rblk->nodes, rblk->nodes + ksl->nodelen * n, ksl->nodelen * rblk->n); +} + +/* + * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i + 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { +  nghttp3_ksl_node *lnode, *rnode; +  nghttp3_ksl_blk *lblk, *rblk; +  size_t n; + +  assert(i < blk->n - 1); + +  lnode = nghttp3_ksl_nth_node(ksl, blk, i); +  rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1); + +  lblk = lnode->blk; +  rblk = rnode->blk; + +  assert(lblk->n > NGHTTP3_KSL_MIN_NBLK); +  assert(rblk->n < NGHTTP3_KSL_MAX_NBLK); + +  n = (lblk->n + rblk->n + 1) / 2 - rblk->n; + +  assert(n > 0); +  assert(lblk->n >= NGHTTP3_KSL_MIN_NBLK + n); +  assert(rblk->n <= NGHTTP3_KSL_MAX_NBLK - n); + +  memmove(rblk->nodes + ksl->nodelen * n, rblk->nodes, ksl->nodelen * rblk->n); + +  rblk->n += (uint32_t)n; +  lblk->n -= (uint32_t)n; + +  memcpy(rblk->nodes, lblk->nodes + ksl->nodelen * lblk->n, ksl->nodelen * n); + +  ksl_node_set_key(ksl, lnode, +                   nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, +                     const nghttp3_ksl_key *rhs) { +  return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                            const nghttp3_ksl_it *hint, +                            const nghttp3_ksl_key *key) { +  nghttp3_ksl_blk *blk = hint->blk; + +  assert(ksl->head); + +  if (blk->n <= NGHTTP3_KSL_MIN_NBLK) { +    return nghttp3_ksl_remove(ksl, it, key); +  } + +  ksl_remove_node(ksl, blk, hint->i); + +  --ksl->n; + +  if (it) { +    if (hint->i == blk->n && blk->next) { +      nghttp3_ksl_it_init(it, ksl, blk->next, 0); +    } else { +      nghttp3_ksl_it_init(it, ksl, blk, hint->i); +    } +  } + +  return 0; +} + +int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                       const nghttp3_ksl_key *key) { +  nghttp3_ksl_blk *blk = ksl->head; +  nghttp3_ksl_node *node; +  size_t i; + +  if (!blk) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  if (!blk->leaf && blk->n == 2 && +      nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && +      nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { +    blk = ksl_merge_node(ksl, blk, 0); +  } + +  for (;;) { +    i = ksl_search(ksl, blk, key, ksl->compar); + +    if (i == blk->n) { +      if (it) { +        *it = nghttp3_ksl_end(ksl); +      } + +      return NGHTTP3_ERR_INVALID_ARGUMENT; +    } + +    if (blk->leaf) { +      if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { +        if (it) { +          *it = nghttp3_ksl_end(ksl); +        } + +        return NGHTTP3_ERR_INVALID_ARGUMENT; +      } + +      ksl_remove_node(ksl, blk, i); +      --ksl->n; + +      if (it) { +        if (blk->n == i && blk->next) { +          nghttp3_ksl_it_init(it, ksl, blk->next, 0); +        } else { +          nghttp3_ksl_it_init(it, ksl, blk, i); +        } +      } + +      return 0; +    } + +    node = nghttp3_ksl_nth_node(ksl, blk, i); + +    if (node->blk->n > NGHTTP3_KSL_MIN_NBLK) { +      blk = node->blk; +      continue; +    } + +    assert(node->blk->n == NGHTTP3_KSL_MIN_NBLK); + +    if (i + 1 < blk->n && +        nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { +      ksl_shift_left(ksl, blk, i + 1); +      blk = node->blk; + +      continue; +    } + +    if (i > 0 && +        nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { +      ksl_shift_right(ksl, blk, i - 1); +      blk = node->blk; + +      continue; +    } + +    if (i + 1 < blk->n) { +      blk = ksl_merge_node(ksl, blk, i); +      continue; +    } + +    assert(i > 0); + +    blk = ksl_merge_node(ksl, blk, i - 1); +  } +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl, +                                       const nghttp3_ksl_key *key) { +  return nghttp3_ksl_lower_bound_compar(ksl, key, ksl->compar); +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const 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; + +  if (!blk) { +    nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); +    return it; +  } + +  for (;;) { +    i = ksl_search(ksl, blk, key, compar); + +    if (blk->leaf) { +      if (i == blk->n && blk->next) { +        blk = blk->next; +        i = 0; +      } + +      nghttp3_ksl_it_init(&it, ksl, blk, i); + +      return it; +    } + +    if (i == blk->n) { +      /* This happens if descendant has smaller key.  Fast forward to +         find last node in this subtree. */ +      for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk) +        ; + +      if (blk->next) { +        blk = blk->next; +        i = 0; +      } else { +        i = blk->n; +      } + +      nghttp3_ksl_it_init(&it, ksl, blk, i); + +      return it; +    } + +    blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; +  } +} + +void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, +                            const nghttp3_ksl_key *new_key) { +  nghttp3_ksl_blk *blk = ksl->head; +  nghttp3_ksl_node *node; +  size_t i; + +  assert(ksl->head); + +  for (;;) { +    i = ksl_search(ksl, blk, old_key, ksl->compar); + +    assert(i < blk->n); +    node = nghttp3_ksl_nth_node(ksl, blk, i); + +    if (blk->leaf) { +      assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key)); +      ksl_node_set_key(ksl, node, new_key); + +      return; +    } + +    if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) || +        ksl->compar((nghttp3_ksl_key *)node->key, new_key)) { +      ksl_node_set_key(ksl, node, new_key); +    } + +    blk = node->blk; +  } +} + +size_t nghttp3_ksl_len(const nghttp3_ksl *ksl) { return ksl->n; } + +void nghttp3_ksl_clear(nghttp3_ksl *ksl) { +  if (!ksl->head) { +    return; +  } + +#ifdef NOMEMPOOL +  ksl_free_blk(ksl, ksl->head); +#endif /* defined(NOMEMPOOL) */ + +  ksl->front = ksl->back = ksl->head = NULL; +  ksl->n = 0; + +  nghttp3_objalloc_clear(&ksl->blkalloc); +} + +#ifndef WIN32 +static void ksl_print(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, +                      size_t level) { +  size_t i; +  nghttp3_ksl_node *node; + +  fprintf(stderr, "LV=%zu n=%u\n", level, blk->n); + +  if (blk->leaf) { +    for (i = 0; i < blk->n; ++i) { +      node = nghttp3_ksl_nth_node(ksl, blk, i); +      fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); +    } + +    fprintf(stderr, "\n"); + +    return; +  } + +  for (i = 0; i < blk->n; ++i) { +    ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1); +  } +} + +void nghttp3_ksl_print(const nghttp3_ksl *ksl) { +  if (!ksl->head) { +    return; +  } + +  ksl_print(ksl, ksl->head, 0); +} +#endif /* !defined(WIN32) */ + +nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { +  nghttp3_ksl_it it; + +  if (ksl->head) { +    nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); +  } else { +    nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); +  } + +  return it; +} + +nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { +  nghttp3_ksl_it it; + +  if (ksl->head) { +    nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); +  } else { +    nghttp3_ksl_it_init(&it, ksl, &null_blk, 0); +  } + +  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_prev(nghttp3_ksl_it *it) { +  assert(!nghttp3_ksl_it_begin(it)); + +  if (it->i == 0) { +    it->blk = it->blk->prev; +    it->i = it->blk->n - 1; +  } else { +    --it->i; +  } +} + +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) { +  return it->i == 0 && it->blk->prev == NULL; +} + +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, +                             const nghttp3_ksl_key *rhs) { +  const nghttp3_range *a = lhs, *b = rhs; +  return a->begin < b->begin; +} + +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, +                                       const nghttp3_ksl_key *rhs) { +  const nghttp3_range *a = lhs, *b = rhs; +  return a->begin < b->begin && !(nghttp3_max_uint64(a->begin, b->begin) < +                                  nghttp3_min_uint64(a->end, b->end)); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_ksl.h b/contrib/libs/nghttp3/lib/nghttp3_ksl.h new file mode 100644 index 00000000000..e15e227ce50 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_ksl.h @@ -0,0 +1,351 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_KSL_H +#define NGHTTP3_KSL_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <stdlib.h> + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_objalloc.h" + +#define NGHTTP3_KSL_DEGR 16 +/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single +   block can contain. */ +#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1) +/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single +   block other than root must contain. */ +#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1) + +/* + * nghttp3_ksl_key represents key in nghttp3_ksl. + */ +typedef void nghttp3_ksl_key; + +typedef struct nghttp3_ksl_node nghttp3_ksl_node; + +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 { +  union { +    struct { +      /* 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. */ +      uint32_t n; +      /* leaf is nonzero if this block contains leaf nodes. */ +      uint32_t 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_opl_entry oplent; +  }; +}; + +nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent); + +/* + * 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); + +typedef struct nghttp3_ksl nghttp3_ksl; + +typedef struct nghttp3_ksl_it nghttp3_ksl_it; + +/* + * nghttp3_ksl_it is a bidirectional 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 { +  nghttp3_objalloc blkalloc; +  /* 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; +  /* n is the number of elements stored. */ +  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; +}; + +/* + * nghttp3_ksl_init initializes |ksl|.  |compar| specifies compare + * function.  |keylen| is the length of key and must be at least + * sizeof(uint64_t). + */ +void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, +                      size_t keylen, const nghttp3_mem *mem); + +/* + * nghttp3_ksl_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| if |it| is not NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + * NGHTTP3_ERR_INVALID_ARGUMENT + *     |key| already exists. + */ +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                       const nghttp3_ksl_key *key, void *data); + +/* + * nghttp3_ksl_remove removes the |key| from |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL.  If |key| is not found, no deletion takes place and + * the return value of nghttp3_ksl_end(ksl) is assigned to |*it| if + * |it| is not NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + *     |key| does not exist. + */ +int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                       const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_remove_hint removes the |key| from |ksl|.  |hint| must + * point to the same node denoted by |key|.  |hint| is used to remove + * a node efficiently in some cases.  Other than that, it behaves + * exactly like nghttp3_ksl_remove.  |it| and |hint| can point to the + * same object. + */ +int nghttp3_ksl_remove_hint(nghttp3_ksl *ksl, nghttp3_ksl_it *it, +                            const nghttp3_ksl_it *hint, +                            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(const nghttp3_ksl *ksl, +                                       const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const 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 both nghttp3_ksl_it_begin(it) != 0 and + * 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 and + * nghttp3_ksl_it_end(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(const nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_clear removes all elements stored in |ksl|. + */ +void nghttp3_ksl_clear(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_nth_node returns the |n|th node under |blk|. + */ +#define nghttp3_ksl_nth_node(KSL, BLK, N)                                      \ +  ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) + +#ifndef WIN32 +/* + * 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(const nghttp3_ksl *ksl); +#endif /* !defined(WIN32) */ + +/* + * 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. + */ +#define nghttp3_ksl_it_get(IT)                                                 \ +  nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data + +/* + * nghttp3_ksl_it_next advances the iterator by one.  It is undefined + * if this function is called when nghttp3_ksl_it_end(it) returns + * nonzero. + */ +#define nghttp3_ksl_it_next(IT)                                                \ +  (++(IT)->i == (IT)->blk->n && (IT)->blk->next                                \ +     ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0)                              \ +     : 0) + +/* + * 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 one beyond + * the last node. + */ +#define nghttp3_ksl_it_end(IT)                                                 \ +  ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) + +/* + * nghttp3_ksl_it_begin returns nonzero if |it| points to the first + * node.  |it| might satisfy both nghttp3_ksl_it_begin(it) != 0 and + * nghttp3_ksl_it_end(it) != 0 if the skip list has no node. + */ +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when nghttp3_ksl_it_end(it) + * returns nonzero. + */ +#define nghttp3_ksl_it_key(IT)                                                 \ +  ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) + +/* + * nghttp3_ksl_range_compar is an implementation of + * nghttp3_ksl_compar.  lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin. + */ +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, +                             const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_range_exclusive_compar is an implementation of + * nghttp3_ksl_compar.  lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, +                                       const nghttp3_ksl_key *rhs); + +#endif /* !defined(NGHTTP3_KSL_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_macro.h b/contrib/libs/nghttp3/lib/nghttp3_macro.h new file mode 100644 index 00000000000..a4e1dfea3cd --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_macro.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_MACRO_H +#define NGHTTP3_MACRO_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <stddef.h> + +#include <nghttp3/nghttp3.h> + +#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) + +/* NGHTTP3_MAX_VARINT` is the maximum value which can be encoded in +   variable-length integer encoding. */ +#define NGHTTP3_MAX_VARINT ((1ULL << 62) - 1) + +#define nghttp3_max_def(SUFFIX, T)                                             \ +  static inline T nghttp3_max_##SUFFIX(T a, T b) { return a < b ? b : a; } + +nghttp3_max_def(int8, int8_t); +nghttp3_max_def(int16, int16_t); +nghttp3_max_def(int32, int32_t); +nghttp3_max_def(int64, int64_t); +nghttp3_max_def(uint8, uint8_t); +nghttp3_max_def(uint16, uint16_t); +nghttp3_max_def(uint32, uint32_t); +nghttp3_max_def(uint64, uint64_t); +nghttp3_max_def(size, size_t); + +#define nghttp3_min_def(SUFFIX, T)                                             \ +  static inline T nghttp3_min_##SUFFIX(T a, T b) { return a < b ? a : b; } + +nghttp3_min_def(int8, int8_t); +nghttp3_min_def(int16, int16_t); +nghttp3_min_def(int32, int32_t); +nghttp3_min_def(int64, int64_t); +nghttp3_min_def(uint8, uint8_t); +nghttp3_min_def(uint16, uint16_t); +nghttp3_min_def(uint32, uint32_t); +nghttp3_min_def(uint64, uint64_t); +nghttp3_min_def(size, size_t); + +#endif /* !defined(NGHTTP3_MACRO_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_map.c b/contrib/libs/nghttp3/lib/nghttp3_map.c new file mode 100644 index 00000000000..cc5e42a2caf --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_map.c @@ -0,0 +1,303 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_map.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_conv.h" + +#define NGHTTP3_INITIAL_TABLE_LENBITS 4 + +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { +  map->mem = mem; +  map->hashbits = 0; +  map->table = NULL; +  map->size = 0; +} + +void nghttp3_map_free(nghttp3_map *map) { +  if (!map) { +    return; +  } + +  nghttp3_mem_free(map->mem, map->table); +} + +int nghttp3_map_each(const nghttp3_map *map, int (*func)(void *data, void *ptr), +                     void *ptr) { +  int rv; +  size_t i; +  nghttp3_map_bucket *bkt; +  size_t tablelen; + +  if (map->size == 0) { +    return 0; +  } + +  tablelen = 1u << map->hashbits; + +  for (i = 0; i < tablelen; ++i) { +    bkt = &map->table[i]; + +    if (bkt->data == NULL) { +      continue; +    } + +    rv = func(bkt->data, ptr); +    if (rv != 0) { +      return rv; +    } +  } + +  return 0; +} + +static size_t hash(nghttp3_map_key_type key, size_t bits) { +  return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +} + +static void map_bucket_swap(nghttp3_map_bucket *a, nghttp3_map_bucket *b) { +  nghttp3_map_bucket c = *a; + +  *a = *b; +  *b = c; +} + +#ifndef WIN32 +void nghttp3_map_print_distance(const nghttp3_map *map) { +  size_t i; +  size_t idx; +  nghttp3_map_bucket *bkt; +  size_t tablelen; + +  if (map->size == 0) { +    return; +  } + +  tablelen = 1u << map->hashbits; + +  for (i = 0; i < tablelen; ++i) { +    bkt = &map->table[i]; + +    if (bkt->data == NULL) { +      fprintf(stderr, "@%zu <EMPTY>\n", i); +      continue; +    } + +    idx = hash(bkt->key, map->hashbits); +    fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, +            hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); +  } +} +#endif /* !defined(WIN32) */ + +static int insert(nghttp3_map_bucket *table, size_t hashbits, +                  nghttp3_map_key_type key, void *data) { +  size_t idx = hash(key, hashbits); +  nghttp3_map_bucket b = {0, key, data}, *bkt; +  size_t mask = (1u << hashbits) - 1; + +  for (;;) { +    bkt = &table[idx]; + +    if (bkt->data == NULL) { +      *bkt = b; +      return 0; +    } + +    if (b.psl > bkt->psl) { +      map_bucket_swap(bkt, &b); +    } else if (bkt->key == key) { +      /* TODO This check is just a waste after first swap or if this +         function is called from map_resize.  That said, there is no +         difference with or without this conditional in performance +         wise. */ +      return NGHTTP3_ERR_INVALID_ARGUMENT; +    } + +    ++b.psl; +    idx = (idx + 1) & mask; +  } +} + +static int map_resize(nghttp3_map *map, size_t new_hashbits) { +  size_t i; +  nghttp3_map_bucket *new_table; +  nghttp3_map_bucket *bkt; +  size_t tablelen; +  int rv; +  (void)rv; + +  new_table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits, +                                 sizeof(nghttp3_map_bucket)); +  if (new_table == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  if (map->size) { +    tablelen = 1u << map->hashbits; + +    for (i = 0; i < tablelen; ++i) { +      bkt = &map->table[i]; +      if (bkt->data == NULL) { +        continue; +      } + +      rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + +      assert(0 == rv); +    } +  } + +  nghttp3_mem_free(map->mem, map->table); +  map->hashbits = new_hashbits; +  map->table = new_table; + +  return 0; +} + +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { +  int rv; + +  assert(data); + +  /* Load factor is 0.75 */ +  /* Under the very initial condition, that is map->size == 0 and +     map->hashbits == 0, 4 > 3 still holds nicely. */ +  if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { +    if (map->hashbits) { +      rv = map_resize(map, map->hashbits + 1); +      if (rv != 0) { +        return rv; +      } +    } else { +      rv = map_resize(map, NGHTTP3_INITIAL_TABLE_LENBITS); +      if (rv != 0) { +        return rv; +      } +    } +  } + +  rv = insert(map->table, map->hashbits, key, data); +  if (rv != 0) { +    return rv; +  } + +  ++map->size; + +  return 0; +} + +void *nghttp3_map_find(const nghttp3_map *map, nghttp3_map_key_type key) { +  size_t idx; +  nghttp3_map_bucket *bkt; +  size_t psl = 0; +  size_t mask; + +  if (map->size == 0) { +    return NULL; +  } + +  idx = hash(key, map->hashbits); +  mask = (1u << map->hashbits) - 1; + +  for (;;) { +    bkt = &map->table[idx]; + +    if (bkt->data == NULL || psl > bkt->psl) { +      return NULL; +    } + +    if (bkt->key == key) { +      return bkt->data; +    } + +    ++psl; +    idx = (idx + 1) & mask; +  } +} + +int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) { +  size_t idx; +  nghttp3_map_bucket *b, *bkt; +  size_t psl = 0; +  size_t mask; + +  if (map->size == 0) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  idx = hash(key, map->hashbits); +  mask = (1u << map->hashbits) - 1; + +  for (;;) { +    bkt = &map->table[idx]; + +    if (bkt->data == NULL || psl > bkt->psl) { +      return NGHTTP3_ERR_INVALID_ARGUMENT; +    } + +    if (bkt->key == key) { +      b = bkt; +      idx = (idx + 1) & mask; + +      for (;;) { +        bkt = &map->table[idx]; +        if (bkt->data == NULL || bkt->psl == 0) { +          b->data = NULL; +          break; +        } + +        --bkt->psl; +        *b = *bkt; +        b = bkt; + +        idx = (idx + 1) & mask; +      } + +      --map->size; + +      return 0; +    } + +    ++psl; +    idx = (idx + 1) & mask; +  } +} + +void nghttp3_map_clear(nghttp3_map *map) { +  if (map->size == 0) { +    return; +  } + +  memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits)); +  map->size = 0; +} + +size_t nghttp3_map_size(const nghttp3_map *map) { return map->size; } diff --git a/contrib/libs/nghttp3/lib/nghttp3_map.h b/contrib/libs/nghttp3/lib/nghttp3_map.h new file mode 100644 index 00000000000..2b1a6ecab5c --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_map.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_MAP_H +#define NGHTTP3_MAP_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +/* Implementation of unordered map */ + +typedef uint64_t nghttp3_map_key_type; + +typedef struct nghttp3_map_bucket { +  uint32_t psl; +  nghttp3_map_key_type key; +  void *data; +} nghttp3_map_bucket; + +typedef struct nghttp3_map { +  nghttp3_map_bucket *table; +  const nghttp3_mem *mem; +  size_t size; +  size_t hashbits; +} nghttp3_map; + +/* + * nghttp3_map_init initializes the map |map|. + */ +void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); + +/* + * nghttp3_map_free deallocates any resources allocated for |map|. + * The stored entries are not freed by this function.  Use + * nghttp3_map_each() to free each entry. + */ +void nghttp3_map_free(nghttp3_map *map); + +/* + * nghttp3_map_insert inserts the new |data| with the |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_key_type key, void *data); + +/* + * nghttp3_map_find returns the entry associated by the key |key|.  If + * there is no such entry, this function returns NULL. + */ +void *nghttp3_map_find(const nghttp3_map *map, nghttp3_map_key_type key); + +/* + * nghttp3_map_remove 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, nghttp3_map_key_type key); + +/* + * nghttp3_map_clear removes all entries from |map|.  The removed + * entry is not freed by this function. + */ +void nghttp3_map_clear(nghttp3_map *map); + +/* + * nghttp3_map_size returns the number of items stored in the map + * |map|. + */ +size_t nghttp3_map_size(const nghttp3_map *map); + +/* + * nghttp3_map_each 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. + */ +int nghttp3_map_each(const nghttp3_map *map, int (*func)(void *data, void *ptr), +                     void *ptr); + +#ifndef WIN32 +void nghttp3_map_print_distance(const nghttp3_map *map); +#endif /* !defined(WIN32) */ + +#endif /* !defined(NGHTTP3_MAP_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_mem.c b/contrib/libs/nghttp3/lib/nghttp3_mem.c new file mode 100644 index 00000000000..687872f9020 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_mem.c @@ -0,0 +1,124 @@ +/* + * 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" + +#include <stdio.h> + +static void *default_malloc(size_t size, void *user_data) { +  (void)user_data; + +  return malloc(size); +} + +static void default_free(void *ptr, void *user_data) { +  (void)user_data; + +  free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { +  (void)user_data; + +  return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *user_data) { +  (void)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; } + +#ifndef MEMDEBUG +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { +  return mem->malloc(size, mem->user_data); +} + +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { +  mem->free(ptr, mem->user_data); +} + +void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { +  return mem->calloc(nmemb, size, mem->user_data); +} + +void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { +  return mem->realloc(ptr, size, mem->user_data); +} +#else  /* defined(MEMDEBUG) */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, +                               const char *func, const char *file, +                               size_t line) { +  void *nptr = mem->malloc(size, mem->user_data); + +  fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, +          file, line); + +  return nptr; +} + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, +                            const char *file, size_t line) { +  fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + +  mem->free(ptr, mem->user_data); +} + +void nghttp3_mem_free2_debug(const nghttp3_free free_func, void *ptr, +                             void *user_data, const char *func, +                             const char *file, size_t line) { +  fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + +  free_func(ptr, user_data); +} + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, +                               size_t size, const char *func, const char *file, +                               size_t line) { +  void *nptr = mem->calloc(nmemb, size, mem->user_data); + +  fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, +          size, func, file, line); + +  return nptr; +} + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, +                                const char *func, const char *file, +                                size_t line) { +  void *nptr = mem->realloc(ptr, size, mem->user_data); + +  fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, +          size, func, file, line); + +  return nptr; +} +#endif /* defined(MEMDEBUG) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_mem.h b/contrib/libs/nghttp3/lib/nghttp3_mem.h new file mode 100644 index 00000000000..1ae53c91575 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_mem.h @@ -0,0 +1,80 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_MEM_H +#define NGHTTP3_MEM_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +/* Convenient wrapper functions to call allocator function in +   |mem|. */ +#ifndef MEMDEBUG +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); +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); +#else /* defined(MEMDEBUG) */ +void *nghttp3_mem_malloc_debug(const nghttp3_mem *mem, size_t size, +                               const char *func, const char *file, size_t line); + +#  define nghttp3_mem_malloc(MEM, SIZE)                                        \ +    nghttp3_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free_debug(const nghttp3_mem *mem, void *ptr, const char *func, +                            const char *file, size_t line); + +#  define nghttp3_mem_free(MEM, PTR)                                           \ +    nghttp3_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void nghttp3_mem_free2_debug(nghttp3_free free_func, void *ptr, void *user_data, +                             const char *func, const char *file, size_t line); + +#  define nghttp3_mem_free2(FREE_FUNC, PTR, USER_DATA)                         \ +    nghttp3_mem_free2_debug((FREE_FUNC), (PTR), (USER_DATA), __func__,         \ +                            __FILE__, __LINE__) + +void *nghttp3_mem_calloc_debug(const nghttp3_mem *mem, size_t nmemb, +                               size_t size, const char *func, const char *file, +                               size_t line); + +#  define nghttp3_mem_calloc(MEM, NMEMB, SIZE)                                 \ +    nghttp3_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__,       \ +                             __LINE__) + +void *nghttp3_mem_realloc_debug(const nghttp3_mem *mem, void *ptr, size_t size, +                                const char *func, const char *file, +                                size_t line); + +#  define nghttp3_mem_realloc(MEM, PTR, SIZE)                                  \ +    nghttp3_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__,        \ +                              __LINE__) +#endif /* defined(MEMDEBUG) */ + +#endif /* !defined(NGHTTP3_MEM_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_objalloc.c b/contrib/libs/nghttp3/lib/nghttp3_objalloc.c new file mode 100644 index 00000000000..0c97860136e --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_objalloc.c @@ -0,0 +1,41 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_objalloc.h" + +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, +                           const nghttp3_mem *mem) { +  nghttp3_balloc_init(&objalloc->balloc, blklen, mem); +  nghttp3_opl_init(&objalloc->opl); +} + +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc) { +  nghttp3_balloc_free(&objalloc->balloc); +} + +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc) { +  nghttp3_opl_clear(&objalloc->opl); +  nghttp3_balloc_clear(&objalloc->balloc); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_objalloc.h b/contrib/libs/nghttp3/lib/nghttp3_objalloc.h new file mode 100644 index 00000000000..4f8ffa09375 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_objalloc.h @@ -0,0 +1,148 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_OBJALLOC_H +#define NGHTTP3_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_balloc.h" +#include "nghttp3_opl.h" +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" + +/* + * nghttp3_objalloc combines nghttp3_balloc and nghttp3_opl, and + * provides an object pool with the custom allocator to reduce the + * allocation and deallocation overheads for small objects. + */ +typedef struct nghttp3_objalloc { +  nghttp3_balloc balloc; +  nghttp3_opl opl; +} nghttp3_objalloc; + +/* + * nghttp3_objalloc_init initializes |objalloc|.  |blklen| is directly + * passed to nghttp3_balloc_init. + */ +void nghttp3_objalloc_init(nghttp3_objalloc *objalloc, size_t blklen, +                           const nghttp3_mem *mem); + +/* + * nghttp3_objalloc_free releases all allocated resources. + */ +void nghttp3_objalloc_free(nghttp3_objalloc *objalloc); + +/* + * nghttp3_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void nghttp3_objalloc_clear(nghttp3_objalloc *objalloc); + +#ifndef NOMEMPOOL +#  define nghttp3_objalloc_decl(NAME, TYPE, OPLENTFIELD)                       \ +    inline static void nghttp3_objalloc_##NAME##_init(                         \ +      nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) {      \ +      nghttp3_objalloc_init(                                                   \ +        objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem);    \ +    }                                                                          \ +                                                                               \ +    TYPE *nghttp3_objalloc_##NAME##_get(nghttp3_objalloc *objalloc);           \ +                                                                               \ +    TYPE *nghttp3_objalloc_##NAME##_len_get(nghttp3_objalloc *objalloc,        \ +                                            size_t len);                       \ +                                                                               \ +    inline static void nghttp3_objalloc_##NAME##_release(                      \ +      nghttp3_objalloc *objalloc, TYPE *obj) {                                 \ +      nghttp3_opl_push(&objalloc->opl, &obj->OPLENTFIELD);                     \ +    } + +#  define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD)                        \ +    TYPE *nghttp3_objalloc_##NAME##_get(nghttp3_objalloc *objalloc) {          \ +      nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl);             \ +      TYPE *obj;                                                               \ +      int rv;                                                                  \ +                                                                               \ +      if (!oplent) {                                                           \ +        rv =                                                                   \ +          nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE));  \ +        if (rv != 0) {                                                         \ +          return NULL;                                                         \ +        }                                                                      \ +                                                                               \ +        return obj;                                                            \ +      }                                                                        \ +                                                                               \ +      return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD);                     \ +    }                                                                          \ +                                                                               \ +    TYPE *nghttp3_objalloc_##NAME##_len_get(nghttp3_objalloc *objalloc,        \ +                                            size_t len) {                      \ +      nghttp3_opl_entry *oplent = nghttp3_opl_pop(&objalloc->opl);             \ +      TYPE *obj;                                                               \ +      int rv;                                                                  \ +                                                                               \ +      if (!oplent) {                                                           \ +        rv = nghttp3_balloc_get(&objalloc->balloc, (void **)&obj, len);        \ +        if (rv != 0) {                                                         \ +          return NULL;                                                         \ +        }                                                                      \ +                                                                               \ +        return obj;                                                            \ +      }                                                                        \ +                                                                               \ +      return nghttp3_struct_of(oplent, TYPE, OPLENTFIELD);                     \ +    } +#else /* defined(NOMEMPOOL) */ +#  define nghttp3_objalloc_decl(NAME, TYPE, OPLENTFIELD)                       \ +    inline static void nghttp3_objalloc_##NAME##_init(                         \ +      nghttp3_objalloc *objalloc, size_t nmemb, const nghttp3_mem *mem) {      \ +      nghttp3_objalloc_init(                                                   \ +        objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem);    \ +    }                                                                          \ +                                                                               \ +    inline static TYPE *nghttp3_objalloc_##NAME##_get(                         \ +      nghttp3_objalloc *objalloc) {                                            \ +      return nghttp3_mem_malloc(objalloc->balloc.mem, sizeof(TYPE));           \ +    }                                                                          \ +                                                                               \ +    inline static TYPE *nghttp3_objalloc_##NAME##_len_get(                     \ +      nghttp3_objalloc *objalloc, size_t len) {                                \ +      return nghttp3_mem_malloc(objalloc->balloc.mem, len);                    \ +    }                                                                          \ +                                                                               \ +    inline static void nghttp3_objalloc_##NAME##_release(                      \ +      nghttp3_objalloc *objalloc, TYPE *obj) {                                 \ +      nghttp3_mem_free(objalloc->balloc.mem, obj);                             \ +    } + +#  define nghttp3_objalloc_def(NAME, TYPE, OPLENTFIELD) +#endif /* defined(NOMEMPOOL) */ + +#endif /* !defined(NGHTTP3_OBJALLOC_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_opl.c b/contrib/libs/nghttp3/lib/nghttp3_opl.c new file mode 100644 index 00000000000..eb8ebdd1854 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_opl.c @@ -0,0 +1,47 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_opl.h" + +void nghttp3_opl_init(nghttp3_opl *opl) { opl->head = NULL; } + +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent) { +  ent->next = opl->head; +  opl->head = ent; +} + +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl) { +  nghttp3_opl_entry *ent = opl->head; + +  if (!ent) { +    return NULL; +  } + +  opl->head = ent->next; + +  return ent; +} + +void nghttp3_opl_clear(nghttp3_opl *opl) { opl->head = NULL; } diff --git a/contrib/libs/nghttp3/lib/nghttp3_opl.h b/contrib/libs/nghttp3/lib/nghttp3_opl.h new file mode 100644 index 00000000000..6609371dbfb --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_opl.h @@ -0,0 +1,66 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_OPL_H +#define NGHTTP3_OPL_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +typedef struct nghttp3_opl_entry nghttp3_opl_entry; + +struct nghttp3_opl_entry { +  nghttp3_opl_entry *next; +}; + +/* + * nghttp3_opl is an object memory pool. + */ +typedef struct nghttp3_opl { +  nghttp3_opl_entry *head; +} nghttp3_opl; + +/* + * nghttp3_opl_init initializes |opl|. + */ +void nghttp3_opl_init(nghttp3_opl *opl); + +/* + * nghttp3_opl_push inserts |ent| to |opl| head. + */ +void nghttp3_opl_push(nghttp3_opl *opl, nghttp3_opl_entry *ent); + +/* + * nghttp3_opl_pop removes the first nghttp3_opl_entry from |opl| and + * returns it.  If |opl| does not have any entry, it returns NULL. + */ +nghttp3_opl_entry *nghttp3_opl_pop(nghttp3_opl *opl); + +void nghttp3_opl_clear(nghttp3_opl *opl); + +#endif /* !defined(NGHTTP3_OPL_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_pq.c b/contrib/libs/nghttp3/lib/nghttp3_pq.c new file mode 100644 index 00000000000..feefcd6fc71 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_pq.c @@ -0,0 +1,183 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_pq.h" + +#include <assert.h> + +#include "nghttp3_macro.h" + +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_pq_less less, +                     const nghttp3_mem *mem) { +  pq->q = NULL; +  pq->mem = mem; +  pq->length = 0; +  pq->capacity = 0; +  pq->less = less; +} + +void nghttp3_pq_free(nghttp3_pq *pq) { +  if (!pq) { +    return; +  } + +  nghttp3_mem_free(pq->mem, pq->q); +} + +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) { +    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_size(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, item->index); + +  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) { +  assert(pq->length); + +  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/contrib/libs/nghttp3/lib/nghttp3_pq.h b/contrib/libs/nghttp3/lib/nghttp3_pq.h new file mode 100644 index 00000000000..3813b529473 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_pq.h @@ -0,0 +1,137 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_PQ_H +#define NGHTTP3_PQ_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +/* Implementation of priority queue */ + +/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates +   that an entry is not queued.  Assigning this value to +   nghttp3_pq_entry.index can check that the entry is queued or +   not. */ +#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX + +typedef struct nghttp3_pq_entry { +  size_t index; +} nghttp3_pq_entry; + +/* nghttp3_pq_less is a "less" function, that returns nonzero if |lhs| +   is considered to be less than |rhs|. */ +typedef int (*nghttp3_pq_less)(const nghttp3_pq_entry *lhs, +                               const nghttp3_pq_entry *rhs); + +typedef struct nghttp3_pq { +  /* q is a pointer to an array that stores the items. */ +  nghttp3_pq_entry **q; +  /* mem is a memory allocator. */ +  const nghttp3_mem *mem; +  /* length is the number of items stored. */ +  size_t length; +  /* capacity is the maximum number of items this queue can store. +     This is automatically extended when length is reached to this +     limit. */ +  size_t capacity; +  /* less is the less function to compare items. */ +  nghttp3_pq_less less; +} nghttp3_pq; + +/* + * nghttp3_pq_init initializes |pq| with compare function |cmp|. + */ +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_pq_less less, +                     const nghttp3_mem *mem); + +/* + * nghttp3_pq_free deallocates any resources allocated for |pq|.  The + * stored items are not freed by this function. + */ +void nghttp3_pq_free(nghttp3_pq *pq); + +/* + * nghttp3_pq_push adds |item| to |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); + +/* + * nghttp3_pq_top returns item at the top of |pq|.  It is undefined if + * |pq| is empty. + */ +nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq); + +/* + * nghttp3_pq_pop pops item at the top of |pq|.  The popped item is + * not freed by this function.  It is undefined if |pq| is empty. + */ +void nghttp3_pq_pop(nghttp3_pq *pq); + +/* + * nghttp3_pq_empty returns nonzero if |pq| is empty. + */ +int nghttp3_pq_empty(const nghttp3_pq *pq); + +/* + * nghttp3_pq_size returns the number of items |pq| contains. + */ +size_t nghttp3_pq_size(const nghttp3_pq *pq); + +typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); + +/* + * nghttp3_pq_each applies |fun| to each item in |pq|.  The |arg| is + * passed as arg parameter to callback function.  This function must + * not change the ordering key.  If the return value from callback is + * nonzero, this function returns 1 immediately without iterating + * remaining items.  Otherwise this function returns 0. + */ +int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg); + +/* + * nghttp3_pq_remove removes |item| from |pq|.  |pq| must contain + * |item| otherwise the behavior is undefined. + */ +void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item); + +/* + * nghttp3_pq_clear removes all items from |pq|. + */ +void nghttp3_pq_clear(nghttp3_pq *pq); + +#endif /* !defined(NGHTTP3_PQ_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_qpack.c b/contrib/libs/nghttp3/lib/nghttp3_qpack.c new file mode 100644 index 00000000000..a1c7e7487c1 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_qpack.c @@ -0,0 +1,4164 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_qpack.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_str.h" +#include "nghttp3_macro.h" +#include "nghttp3_debug.h" +#include "nghttp3_unreachable.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, (uint8_t *)(N), sizeof((N)) - 1, -1},                               \ +    {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1},                               \ +    T,                                                                         \ +  } + +static nghttp3_qpack_static_header stable[] = { +  MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY), +  MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH), +  MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE), +  MAKE_STATIC_HD("content-disposition", "", +                 NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION), +  MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH), +  MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE), +  MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE), +  MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG), +  MAKE_STATIC_HD("if-modified-since", "", +                 NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE), +  MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH), +  MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED), +  MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK), +  MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION), +  MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER), +  MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE), +  MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD), +  MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME), +  MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME), +  MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT), +  MAKE_STATIC_HD("accept", "application/dns-message", +                 NGHTTP3_QPACK_TOKEN_ACCEPT), +  MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br", +                 NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING), +  MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES), +  MAKE_STATIC_HD("access-control-allow-headers", "cache-control", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), +  MAKE_STATIC_HD("access-control-allow-headers", "content-type", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), +  MAKE_STATIC_HD("access-control-allow-origin", "*", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN), +  MAKE_STATIC_HD("cache-control", "max-age=0", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("cache-control", "max-age=2592000", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("cache-control", "max-age=604800", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("cache-control", "no-cache", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("cache-control", "no-store", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("cache-control", "public, max-age=31536000", +                 NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), +  MAKE_STATIC_HD("content-encoding", "br", +                 NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), +  MAKE_STATIC_HD("content-encoding", "gzip", +                 NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), +  MAKE_STATIC_HD("content-type", "application/dns-message", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "application/javascript", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "application/json", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "image/gif", NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "image/jpeg", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "image/png", NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "text/css", NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "text/html; charset=utf-8", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "text/plain", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8", +                 NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), +  MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE), +  MAKE_STATIC_HD("strict-transport-security", "max-age=31536000", +                 NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), +  MAKE_STATIC_HD("strict-transport-security", +                 "max-age=31536000; includesubdomains", +                 NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), +  MAKE_STATIC_HD("strict-transport-security", +                 "max-age=31536000; includesubdomains; preload", +                 NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), +  MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY), +  MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY), +  MAKE_STATIC_HD("x-content-type-options", "nosniff", +                 NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS), +  MAKE_STATIC_HD("x-xss-protection", "1; mode=block", +                 NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION), +  MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS), +  MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE), +  MAKE_STATIC_HD("access-control-allow-credentials", "FALSE", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), +  MAKE_STATIC_HD("access-control-allow-credentials", "TRUE", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), +  MAKE_STATIC_HD("access-control-allow-headers", "*", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), +  MAKE_STATIC_HD("access-control-allow-methods", "get", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), +  MAKE_STATIC_HD("access-control-allow-methods", "get, post, options", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), +  MAKE_STATIC_HD("access-control-allow-methods", "options", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), +  MAKE_STATIC_HD("access-control-expose-headers", "content-length", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS), +  MAKE_STATIC_HD("access-control-request-headers", "content-type", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS), +  MAKE_STATIC_HD("access-control-request-method", "get", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), +  MAKE_STATIC_HD("access-control-request-method", "post", +                 NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), +  MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC), +  MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION), +  MAKE_STATIC_HD("content-security-policy", +                 "script-src 'none'; object-src 'none'; base-uri 'none'", +                 NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY), +  MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA), +  MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT), +  MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED), +  MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE), +  MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN), +  MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE), +  MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER), +  MAKE_STATIC_HD("timing-allow-origin", "*", +                 NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN), +  MAKE_STATIC_HD("upgrade-insecure-requests", "1", +                 NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS), +  MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT), +  MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR), +  MAKE_STATIC_HD("x-frame-options", "deny", +                 NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), +  MAKE_STATIC_HD("x-frame-options", "sameorigin", +                 NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { +  return n == 0 || memcmp(s1, s2, n) == 0; +} + +/* Generated by genlibtokenlookup.py */ +static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) { +  switch (namelen) { +  case 2: +    switch (name[1]) { +    case 'e': +      if (memeq("t", name, 1)) { +        return NGHTTP3_QPACK_TOKEN_TE; +      } +      break; +    } +    break; +  case 3: +    switch (name[2]) { +    case 'e': +      if (memeq("ag", name, 2)) { +        return NGHTTP3_QPACK_TOKEN_AGE; +      } +      break; +    } +    break; +  case 4: +    switch (name[3]) { +    case 'e': +      if (memeq("dat", name, 3)) { +        return NGHTTP3_QPACK_TOKEN_DATE; +      } +      break; +    case 'g': +      if (memeq("eta", name, 3)) { +        return NGHTTP3_QPACK_TOKEN_ETAG; +      } +      break; +    case 'k': +      if (memeq("lin", name, 3)) { +        return NGHTTP3_QPACK_TOKEN_LINK; +      } +      break; +    case 't': +      if (memeq("hos", name, 3)) { +        return NGHTTP3_QPACK_TOKEN_HOST; +      } +      break; +    case 'y': +      if (memeq("var", name, 3)) { +        return NGHTTP3_QPACK_TOKEN_VARY; +      } +      break; +    } +    break; +  case 5: +    switch (name[4]) { +    case 'e': +      if (memeq("rang", name, 4)) { +        return NGHTTP3_QPACK_TOKEN_RANGE; +      } +      break; +    case 'h': +      if (memeq(":pat", name, 4)) { +        return NGHTTP3_QPACK_TOKEN__PATH; +      } +      break; +    } +    break; +  case 6: +    switch (name[5]) { +    case 'e': +      if (memeq("cooki", name, 5)) { +        return NGHTTP3_QPACK_TOKEN_COOKIE; +      } +      break; +    case 'n': +      if (memeq("origi", name, 5)) { +        return NGHTTP3_QPACK_TOKEN_ORIGIN; +      } +      break; +    case 'r': +      if (memeq("serve", name, 5)) { +        return NGHTTP3_QPACK_TOKEN_SERVER; +      } +      break; +    case 't': +      if (memeq("accep", name, 5)) { +        return NGHTTP3_QPACK_TOKEN_ACCEPT; +      } +      break; +    } +    break; +  case 7: +    switch (name[6]) { +    case 'c': +      if (memeq("alt-sv", name, 6)) { +        return NGHTTP3_QPACK_TOKEN_ALT_SVC; +      } +      break; +    case 'd': +      if (memeq(":metho", name, 6)) { +        return NGHTTP3_QPACK_TOKEN__METHOD; +      } +      break; +    case 'e': +      if (memeq(":schem", name, 6)) { +        return NGHTTP3_QPACK_TOKEN__SCHEME; +      } +      if (memeq("purpos", name, 6)) { +        return NGHTTP3_QPACK_TOKEN_PURPOSE; +      } +      if (memeq("upgrad", name, 6)) { +        return NGHTTP3_QPACK_TOKEN_UPGRADE; +      } +      break; +    case 'r': +      if (memeq("refere", name, 6)) { +        return NGHTTP3_QPACK_TOKEN_REFERER; +      } +      break; +    case 's': +      if (memeq(":statu", name, 6)) { +        return NGHTTP3_QPACK_TOKEN__STATUS; +      } +      break; +    } +    break; +  case 8: +    switch (name[7]) { +    case 'e': +      if (memeq("if-rang", name, 7)) { +        return NGHTTP3_QPACK_TOKEN_IF_RANGE; +      } +      break; +    case 'n': +      if (memeq("locatio", name, 7)) { +        return NGHTTP3_QPACK_TOKEN_LOCATION; +      } +      break; +    case 'y': +      if (memeq("priorit", name, 7)) { +        return NGHTTP3_QPACK_TOKEN_PRIORITY; +      } +      break; +    } +    break; +  case 9: +    switch (name[8]) { +    case 'd': +      if (memeq("forwarde", name, 8)) { +        return NGHTTP3_QPACK_TOKEN_FORWARDED; +      } +      break; +    case 'l': +      if (memeq(":protoco", name, 8)) { +        return NGHTTP3_QPACK_TOKEN__PROTOCOL; +      } +      break; +    case 't': +      if (memeq("expect-c", name, 8)) { +        return NGHTTP3_QPACK_TOKEN_EXPECT_CT; +      } +      break; +    } +    break; +  case 10: +    switch (name[9]) { +    case 'a': +      if (memeq("early-dat", name, 9)) { +        return NGHTTP3_QPACK_TOKEN_EARLY_DATA; +      } +      break; +    case 'e': +      if (memeq("keep-aliv", name, 9)) { +        return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE; +      } +      if (memeq("set-cooki", name, 9)) { +        return NGHTTP3_QPACK_TOKEN_SET_COOKIE; +      } +      break; +    case 'n': +      if (memeq("connectio", name, 9)) { +        return NGHTTP3_QPACK_TOKEN_CONNECTION; +      } +      break; +    case 't': +      if (memeq("user-agen", name, 9)) { +        return NGHTTP3_QPACK_TOKEN_USER_AGENT; +      } +      break; +    case 'y': +      if (memeq(":authorit", name, 9)) { +        return NGHTTP3_QPACK_TOKEN__AUTHORITY; +      } +      break; +    } +    break; +  case 12: +    switch (name[11]) { +    case 'e': +      if (memeq("content-typ", name, 11)) { +        return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE; +      } +      break; +    } +    break; +  case 13: +    switch (name[12]) { +    case 'd': +      if (memeq("last-modifie", name, 12)) { +        return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED; +      } +      break; +    case 'h': +      if (memeq("if-none-matc", name, 12)) { +        return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH; +      } +      break; +    case 'l': +      if (memeq("cache-contro", name, 12)) { +        return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL; +      } +      break; +    case 'n': +      if (memeq("authorizatio", name, 12)) { +        return NGHTTP3_QPACK_TOKEN_AUTHORIZATION; +      } +      break; +    case 's': +      if (memeq("accept-range", name, 12)) { +        return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES; +      } +      break; +    } +    break; +  case 14: +    switch (name[13]) { +    case 'h': +      if (memeq("content-lengt", name, 13)) { +        return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH; +      } +      break; +    } +    break; +  case 15: +    switch (name[14]) { +    case 'e': +      if (memeq("accept-languag", name, 14)) { +        return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE; +      } +      break; +    case 'g': +      if (memeq("accept-encodin", name, 14)) { +        return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING; +      } +      break; +    case 'r': +      if (memeq("x-forwarded-fo", name, 14)) { +        return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR; +      } +      break; +    case 's': +      if (memeq("x-frame-option", name, 14)) { +        return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS; +      } +      break; +    } +    break; +  case 16: +    switch (name[15]) { +    case 'g': +      if (memeq("content-encodin", name, 15)) { +        return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING; +      } +      break; +    case 'n': +      if (memeq("proxy-connectio", name, 15)) { +        return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION; +      } +      if (memeq("x-xss-protectio", name, 15)) { +        return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION; +      } +      break; +    } +    break; +  case 17: +    switch (name[16]) { +    case 'e': +      if (memeq("if-modified-sinc", name, 16)) { +        return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE; +      } +      break; +    case 'g': +      if (memeq("transfer-encodin", name, 16)) { +        return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING; +      } +      break; +    } +    break; +  case 19: +    switch (name[18]) { +    case 'n': +      if (memeq("content-dispositio", name, 18)) { +        return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION; +      } +      if (memeq("timing-allow-origi", name, 18)) { +        return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN; +      } +      break; +    } +    break; +  case 22: +    switch (name[21]) { +    case 's': +      if (memeq("x-content-type-option", name, 21)) { +        return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS; +      } +      break; +    } +    break; +  case 23: +    switch (name[22]) { +    case 'y': +      if (memeq("content-security-polic", name, 22)) { +        return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY; +      } +      break; +    } +    break; +  case 25: +    switch (name[24]) { +    case 's': +      if (memeq("upgrade-insecure-request", name, 24)) { +        return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS; +      } +      break; +    case 'y': +      if (memeq("strict-transport-securit", name, 24)) { +        return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY; +      } +      break; +    } +    break; +  case 27: +    switch (name[26]) { +    case 'n': +      if (memeq("access-control-allow-origi", name, 26)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; +      } +      break; +    } +    break; +  case 28: +    switch (name[27]) { +    case 's': +      if (memeq("access-control-allow-header", name, 27)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS; +      } +      if (memeq("access-control-allow-method", name, 27)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS; +      } +      break; +    } +    break; +  case 29: +    switch (name[28]) { +    case 'd': +      if (memeq("access-control-request-metho", name, 28)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD; +      } +      break; +    case 's': +      if (memeq("access-control-expose-header", name, 28)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS; +      } +      break; +    } +    break; +  case 30: +    switch (name[29]) { +    case 's': +      if (memeq("access-control-request-header", name, 29)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS; +      } +      break; +    } +    break; +  case 32: +    switch (name[31]) { +    case 's': +      if (memeq("access-control-allow-credential", name, 31)) { +        return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS; +      } +      break; +    } +    break; +  } +  return -1; +} + +static size_t table_space(size_t namelen, size_t valuelen) { +  return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen; +} + +static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { +  return a->name->len == b->namelen && +         memeq(a->name->base, b->name, b->namelen); +} + +static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { +  return a->value->len == b->valuelen && +         memeq(a->value->base, b->value, b->valuelen); +} + +static void qpack_map_init(nghttp3_qpack_map *map) { +  memset(map, 0, sizeof(nghttp3_qpack_map)); +} + +static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { +  nghttp3_qpack_entry **bucket; + +  bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + +  if (*bucket == NULL) { +    *bucket = ent; +    return; +  } + +  /* larger absidx is linked near the root */ +  ent->map_next = *bucket; +  *bucket = ent; +} + +static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { +  nghttp3_qpack_entry **dst; + +  dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + +  for (; *dst; dst = &(*dst)->map_next) { +    if (*dst != ent) { +      continue; +    } + +    *dst = ent->map_next; +    ent->map_next = NULL; +    return; +  } +} + +/* + * qpack_context_can_reference returns nonzero if dynamic table entry + * at |absidx| can be referenced.  In other words, it is within + * ctx->max_dtable_capacity. + */ +static int qpack_context_can_reference(nghttp3_qpack_context *ctx, +                                       uint64_t absidx) { +  nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); +  return ctx->dtable_sum - ent->sum <= ctx->max_dtable_capacity; +} + +/* |*ppb_match| (post-base match), if it is not NULL, is always exact +     match. */ +static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, +                                   int *exact_match, +                                   nghttp3_qpack_entry **pmatch, +                                   nghttp3_qpack_entry **ppb_match, +                                   const nghttp3_nv *nv, int32_t token, +                                   uint32_t hash, uint64_t krcnt, +                                   int allow_blocking, int name_only) { +  nghttp3_qpack_entry *p; + +  *exact_match = 0; +  *pmatch = NULL; +  *ppb_match = NULL; + +  for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p; +       p = p->map_next) { +    if (token != p->nv.token || +        (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) || +        !qpack_context_can_reference(&encoder->ctx, p->absidx)) { +      continue; +    } +    if (allow_blocking || p->absidx + 1 <= krcnt) { +      if (!*pmatch) { +        *pmatch = p; +        if (name_only) { +          return; +        } +      } +      if (qpack_nv_value_eq(&p->nv, nv)) { +        *pmatch = p; +        *exact_match = 1; +        return; +      } +    } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) { +      *ppb_match = p; +    } +  } +} + +/* + * qpack_context_init initializes |ctx|.  |hard_max_dtable_capacity| + * is the upper bound of the dynamic table capacity.  |mem| is a + * memory allocator. + * + * The maximum dynamic table size is governed by + * ctx->max_dtable_capacity and it is initialized to 0. + * |hard_max_dtable_capacity| is the upper bound of + * ctx->max_dtable_capacity. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int qpack_context_init(nghttp3_qpack_context *ctx, +                              size_t hard_max_dtable_capacity, +                              size_t max_blocked_streams, +                              const nghttp3_mem *mem) { +  int rv; +  size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; +  size_t len2; + +  for (len2 = 1; len2 < len; len2 <<= 1) +    ; + +  rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *), +                            mem); +  if (rv != 0) { +    return rv; +  } + +  ctx->mem = mem; +  ctx->dtable_size = 0; +  ctx->dtable_sum = 0; +  ctx->hard_max_dtable_capacity = hard_max_dtable_capacity; +  ctx->max_dtable_capacity = 0; +  ctx->max_blocked_streams = max_blocked_streams; +  ctx->next_absidx = 0; +  ctx->bad = 0; + +  return 0; +} + +static void qpack_context_free(nghttp3_qpack_context *ctx) { +  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 nghttp3_blocked_streams_key { +  uint64_t max_cnt; +  uint64_t id; +} nghttp3_blocked_streams_key; + +static int max_cnt_greater(const nghttp3_ksl_key *lhs, +                           const nghttp3_ksl_key *rhs) { +  const nghttp3_blocked_streams_key *a = lhs; +  const nghttp3_blocked_streams_key *b = rhs; +  return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id); +} + +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, +                               size_t hard_max_dtable_capacity, +                               const nghttp3_mem *mem) { +  int rv; + +  rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); +  if (rv != 0) { +    return rv; +  } + +  nghttp3_map_init(&encoder->streams, mem); + +  nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, +                   sizeof(nghttp3_blocked_streams_key), mem); + +  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->uninterrupted_decoderlen = 0; +  encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; + +  nghttp3_qpack_read_state_reset(&encoder->rstate); + +  return 0; +} + +static int map_stream_free(void *data, void *ptr) { +  const nghttp3_mem *mem = ptr; +  nghttp3_qpack_stream *stream = data; +  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(&encoder->streams, map_stream_free, +                   (void *)encoder->ctx.mem); +  nghttp3_map_free(&encoder->streams); +  qpack_context_free(&encoder->ctx); +} + +void nghttp3_qpack_encoder_set_max_dtable_capacity( +  nghttp3_qpack_encoder *encoder, size_t max_dtable_capacity) { +  max_dtable_capacity = nghttp3_min_size(max_dtable_capacity, +                                         encoder->ctx.hard_max_dtable_capacity); + +  if (encoder->ctx.max_dtable_capacity == max_dtable_capacity) { +    return; +  } + +  encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; + +  if (encoder->min_dtable_update > max_dtable_capacity) { +    encoder->min_dtable_update = max_dtable_capacity; +    encoder->ctx.max_dtable_capacity = max_dtable_capacity; +  } +  encoder->last_max_dtable_update = max_dtable_capacity; +} + +void nghttp3_qpack_encoder_set_max_blocked_streams( +  nghttp3_qpack_encoder *encoder, size_t max_blocked_streams) { +  encoder->ctx.max_blocked_streams = max_blocked_streams; +} + +uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { +  assert(!nghttp3_pq_empty(&encoder->min_cnts)); + +  return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts), +                           nghttp3_qpack_header_block_ref, min_cnts_pe) +    ->min_cnt; +} + +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { +  nghttp3_ringbuf *dtable = &encoder->ctx.dtable; +  const nghttp3_mem *mem = encoder->ctx.mem; +  uint64_t min_cnt = UINT64_MAX; +  size_t len; +  nghttp3_qpack_entry *ent; + +  if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_capacity) { +    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_capacity;) { +    len = nghttp3_ringbuf_len(dtable); +    ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); +    if (ent->absidx + 1 == min_cnt) { +      return; +    } + +    encoder->ctx.dtable_size -= +      table_space(ent->nv.name->len, ent->nv.value->len); + +    nghttp3_ringbuf_pop_back(dtable); +    qpack_map_remove(&encoder->dtable_map, ent); + +    nghttp3_qpack_entry_free(ent); +    nghttp3_mem_free(mem, ent); +  } +} + +/* + * qpack_encoder_add_stream_ref adds another dynamic table reference + * to a stream denoted by |stream_id|.  |stream| must be NULL if no + * stream object is not found for the given stream ID.  |max_cnt| and + * |min_cnt| is the maximum and minimum insert count it references + * respectively. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, +                                        int64_t stream_id, +                                        nghttp3_qpack_stream *stream, +                                        uint64_t max_cnt, uint64_t min_cnt) { +  nghttp3_qpack_header_block_ref *ref; +  const nghttp3_mem *mem = encoder->ctx.mem; +  uint64_t prev_max_cnt = 0; +  int rv; + +  if (stream == NULL) { +    rv = nghttp3_qpack_stream_new(&stream, stream_id, mem); +    if (rv != 0) { +      assert(rv == NGHTTP3_ERR_NOMEM); +      return rv; +    } +    rv = nghttp3_map_insert(&encoder->streams, +                            (nghttp3_map_key_type)stream->stream_id, stream); +    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, +                     (nghttp3_map_key_type)stream->stream_id); + +  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 ensures that |buf| contains at least |extra_size| of + * free space.  In other words, if this function succeeds, + * nghttp3_buf_left(buf) >= extra_size holds. + * + * 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(nghttp3_buf *buf, size_t extra_size, +                       const nghttp3_mem *mem) { +  size_t left = nghttp3_buf_left(buf); +  size_t n = 32; + +  if (left >= extra_size) { +    return 0; +  } + +  n = nghttp3_max_size(n, nghttp3_buf_cap(buf) + extra_size - left); + +  /* Check whether we are requesting too much memory */ +  if (n > (1u << 31)) { +    return NGHTTP3_ERR_NOMEM; +  } + +#ifndef WIN32 +  n = 1u << (32 - __builtin_clz((uint32_t)n - 1)); +#else  /* defined(WIN32) */ +  /* Round up to the next highest power of 2 from Bit Twiddling +     Hacks */ +  --n; +  n |= n >> 1; +  n |= n >> 2; +  n |= n >> 4; +  n |= n >> 8; +  n |= n >> 16; +  ++n; +#endif /* defined(WIN32) */ + +  return nghttp3_buf_reserve(buf, n, mem); +} + +int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, +                                 nghttp3_buf *pbuf, nghttp3_buf *rbuf, +                                 nghttp3_buf *ebuf, int64_t stream_id, +                                 const nghttp3_nv *nva, size_t nvlen) { +  size_t i; +  uint64_t max_cnt = 0, min_cnt = UINT64_MAX; +  uint64_t base; +  int rv = 0; +  int allow_blocking; +  int blocked_stream; +  nghttp3_qpack_stream *stream; + +  if (encoder->ctx.bad) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf); +  if (rv != 0) { +    goto fail; +  } + +  base = encoder->ctx.next_absidx; + +  stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); +  blocked_stream = +    stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); +  allow_blocking = +    blocked_stream || encoder->ctx.max_blocked_streams > +                        nghttp3_ksl_len(&encoder->blocked_streams); + +  DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, +         blocked_stream, allow_blocking); + +  for (i = 0; i < nvlen; ++i) { +    rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf, +                                         ebuf, &nva[i], base, allow_blocking); +    if (rv != 0) { +      goto fail; +    } +  } + +  nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt, +                                                   base); + +  encoder->uninterrupted_decoderlen = 0; + +  /* TODO If max_cnt == 0, no reference is made to dtable. */ +  if (!max_cnt) { +    return 0; +  } + +  rv = +    qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt, min_cnt); +  if (rv != 0) { +    goto fail; +  } + +  return 0; + +fail: +  encoder->ctx.bad = 1; +  return rv; +} + +/* + * qpack_write_number writes variable integer to |rbuf|.  |num| is an + * integer to write.  |prefix| is a prefix of variable integer + * encoding. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num, +                              size_t prefix, const nghttp3_mem *mem) { +  int rv; +  size_t len = nghttp3_qpack_put_varint_len(num, prefix); +  uint8_t *p; + +  rv = reserve_buf(rbuf, len, mem); +  if (rv != 0) { +    return rv; +  } + +  p = rbuf->last; + +  *p = fb; +  p = nghttp3_qpack_put_varint(p, num, prefix); + +  assert((size_t)(p - rbuf->last) == len); + +  rbuf->last = p; + +  return 0; +} + +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, +                                                nghttp3_buf *ebuf) { +  int rv; + +  nghttp3_qpack_encoder_shrink_dtable(encoder); + +  if (encoder->ctx.max_dtable_capacity < 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_capacity = 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) { +  return nghttp3_map_find(&encoder->streams, (nghttp3_map_key_type)stream_id); +} + +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 -1: +  case NGHTTP3_QPACK_TOKEN__PATH: +  case NGHTTP3_QPACK_TOKEN_AGE: +  case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: +  case NGHTTP3_QPACK_TOKEN_ETAG: +  case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE: +  case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH: +  case NGHTTP3_QPACK_TOKEN_LOCATION: +  case NGHTTP3_QPACK_TOKEN_SET_COOKIE: +    if (nv->flags & NGHTTP3_NV_FLAG_TRY_INDEX) { +      break; +    } + +    return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; +  case NGHTTP3_QPACK_TOKEN_HOST: +  case NGHTTP3_QPACK_TOKEN_TE: +  case NGHTTP3_QPACK_TOKEN__PROTOCOL: +  case NGHTTP3_QPACK_TOKEN_PRIORITY: +    break; +  default: +    if (nv->flags & NGHTTP3_NV_FLAG_TRY_INDEX) { +      break; +    } + +    if (token >= 1000) { +      return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; +    } +  } + +  if (table_space(nv->namelen, nv->valuelen) > +      encoder->ctx.max_dtable_capacity * 3 / 4) { +    return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; +  } + +  return NGHTTP3_QPACK_INDEXING_MODE_STORE; +} + +/* + * qpack_encoder_can_index returns nonzero if an entry which occupies + * |need| bytes can be inserted into dynamic table.  |min_cnt| is the + * minimum insert count which blocked stream requires. + */ +static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, +                                   uint64_t min_cnt) { +  size_t avail = 0; +  size_t len; +  uint64_t gmin_cnt; +  nghttp3_qpack_entry *min_ent, *last_ent; +  nghttp3_ringbuf *dtable = &encoder->ctx.dtable; + +  if (encoder->ctx.max_dtable_capacity > encoder->ctx.dtable_size) { +    avail = encoder->ctx.max_dtable_capacity - 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_uint64(min_cnt, gmin_cnt); +  } + +  if (min_cnt == UINT64_MAX) { +    return encoder->ctx.max_dtable_capacity >= need; +  } + +  min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); + +  len = nghttp3_ringbuf_len(&encoder->ctx.dtable); +  assert(len); +  last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); + +  if (min_ent == last_ent) { +    return 0; +  } + +  return avail + min_ent->sum - last_ent->sum >= need; +} + +/* + * qpack_encoder_can_index_nv returns nonzero if header field |nv| can + * be inserted into dynamic table.  |min_cnt| is the minimum insert + * count which blocked stream requires. + */ +static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder, +                                      const nghttp3_nv *nv, uint64_t min_cnt) { +  return qpack_encoder_can_index( +    encoder, table_space(nv->namelen, nv->valuelen), min_cnt); +} + +/* + * qpack_encoder_can_index_duplicate returns nonzero if an entry at + * |absidx| in dynamic table can be inserted to dynamic table as + * duplicate.  |min_cnt| is the minimum insert count which blocked + * stream requires. + */ +static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, +                                             uint64_t absidx, +                                             uint64_t min_cnt) { +  nghttp3_qpack_entry *ent = +    nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + +  return qpack_encoder_can_index( +    encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt); +} + +/* + * qpack_context_check_draining returns nonzero if an entry at + * |absidx| in dynamic table is one of draining entries. + */ +static int qpack_context_check_draining(nghttp3_qpack_context *ctx, +                                        uint64_t absidx) { +  const size_t safe = ctx->max_dtable_capacity - +                      nghttp3_min_size(512, ctx->max_dtable_capacity * 1 / 8); +  nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); + +  return ctx->dtable_sum - ent->sum > safe; +} + +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, +                                    uint64_t *pmax_cnt, uint64_t *pmin_cnt, +                                    nghttp3_buf *rbuf, nghttp3_buf *ebuf, +                                    const nghttp3_nv *nv, uint64_t base, +                                    int allow_blocking) { +  uint32_t hash = 0; +  int32_t token; +  nghttp3_qpack_indexing_mode indexing_mode; +  nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1}; +  nghttp3_qpack_entry *new_ent = NULL; +  int static_entry; +  int just_index = 0; +  int rv; + +  token = qpack_lookup_token(nv->name, nv->namelen); +  static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable); + +  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 (static_entry) { +    hash = token_stable[token].hash; +  } else { +    switch (token) { +    case NGHTTP3_QPACK_TOKEN_HOST: +      hash = 2952701295u; +      break; +    case NGHTTP3_QPACK_TOKEN_TE: +      hash = 1011170994u; +      break; +    case NGHTTP3_QPACK_TOKEN__PROTOCOL: +      hash = 1128642621u; +      break; +    case NGHTTP3_QPACK_TOKEN_PRIORITY: +      hash = 2498028297u; +      break; +    default: +      hash = qpack_hash_name(nv); +    } +  } + +  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_uint64(*pmax_cnt, (uint64_t)(dres.index + 1)); +    *pmin_cnt = nghttp3_min_uint64(*pmin_cnt, (uint64_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_uint64(*pmax_cnt, new_ent->absidx + 1); +        *pmin_cnt = nghttp3_min_uint64(*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_uint64((uint64_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_uint64(*pmin_cnt, (uint64_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_uint64(*pmax_cnt, new_ent->absidx + 1); +        *pmin_cnt = nghttp3_min_uint64(*pmin_cnt, new_ent->absidx + 1); + +        return nghttp3_qpack_encoder_write_dynamic_indexed( +          encoder, rbuf, new_ent->absidx, base); +      } +    } + +    *pmax_cnt = nghttp3_max_uint64(*pmax_cnt, (uint64_t)(dres.index + 1)); +    *pmin_cnt = nghttp3_min_uint64(*pmin_cnt, (uint64_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_uint64(*pmax_cnt, new_ent->absidx + 1); +      *pmin_cnt = nghttp3_min_uint64(*pmin_cnt, new_ent->absidx + 1); + +      return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf, +                                                         new_ent->absidx, base); +    } +  } + +  return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv); +} + +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, +                            nghttp3_qpack_indexing_mode indexing_mode) { +  nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx, +                                     0, -1}; +  nghttp3_qpack_static_entry *ent; +  nghttp3_qpack_static_header *hdr; +  size_t i; + +  assert(token >= 0); + +  if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) { +    return res; +  } + +  for (i = (size_t)token; +       i < nghttp3_arraylen(token_stable) && token_stable[i].token == token; +       ++i) { +    ent = &token_stable[i]; +    hdr = &stable[ent->absidx]; +    if (hdr->value.len == nv->valuelen && +        memeq(hdr->value.base, nv->value, nv->valuelen)) { +      res.index = (nghttp3_ssize)ent->absidx; +      res.name_value_match = 1; +      return res; +    } +  } +  return res; +} + +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( +  nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, +  uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, +  int allow_blocking) { +  nghttp3_qpack_lookup_result res = {-1, 0, -1}; +  int exact_match = 0; +  nghttp3_qpack_entry *match, *pb_match; + +  encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token, +                         hash, krcnt, allow_blocking, +                         indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER); +  if (match) { +    res.index = (nghttp3_ssize)match->absidx; +    res.name_value_match = exact_match; +  } +  if (pb_match) { +    res.pb_index = (nghttp3_ssize)pb_match->absidx; +  } + +  return res; +} + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, +                                       uint64_t max_cnt, uint64_t min_cnt, +                                       const nghttp3_mem *mem) { +  nghttp3_qpack_header_block_ref *ref = +    nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref)); + +  if (ref == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; +  ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; +  ref->max_cnt = max_cnt; +  ref->min_cnt = min_cnt; + +  *pref = ref; + +  return 0; +} + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, +                                        const nghttp3_mem *mem) { +  nghttp3_mem_free(mem, ref); +} + +static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, +                               const nghttp3_pq_entry *rhsx) { +  const nghttp3_qpack_header_block_ref *lhs = +    nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); +  const nghttp3_qpack_header_block_ref *rhs = +    nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); + +  return lhs->max_cnt > rhs->max_cnt; +} + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, +                             const nghttp3_mem *mem) { +  int rv; +  nghttp3_qpack_stream *stream; + +  stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream)); +  if (stream == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  rv = nghttp3_ringbuf_init(&stream->refs, 4, +                            sizeof(nghttp3_qpack_header_block_ref *), mem); +  if (rv != 0) { +    nghttp3_mem_free(mem, stream); +    return rv; +  } + +  nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); + +  stream->stream_id = stream_id; + +  *pstream = stream; + +  return 0; +} + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, +                              const nghttp3_mem *mem) { +  nghttp3_qpack_header_block_ref *ref; +  size_t i, len; + +  if (stream == NULL) { +    return; +  } + +  nghttp3_pq_free(&stream->max_cnts); + +  len = nghttp3_ringbuf_len(&stream->refs); +  for (i = 0; i < len; ++i) { +    ref = +      *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, i); +    nghttp3_qpack_header_block_ref_del(ref, mem); +  } + +  nghttp3_ringbuf_free(&stream->refs); + +  nghttp3_mem_free(mem, stream); +} + +uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) { +  nghttp3_qpack_header_block_ref *ref; + +  if (nghttp3_pq_empty(&stream->max_cnts)) { +    return 0; +  } + +  ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), +                          nghttp3_qpack_header_block_ref, max_cnts_pe); +  return ref->max_cnt; +} + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, +                                 nghttp3_qpack_header_block_ref *ref) { +  nghttp3_qpack_header_block_ref **dest; +  int rv; + +  if (nghttp3_ringbuf_full(&stream->refs)) { +    rv = nghttp3_ringbuf_reserve(&stream->refs, +                                 nghttp3_ringbuf_len(&stream->refs) * 2); +    if (rv != 0) { +      return rv; +    } +  } + +  dest = nghttp3_ringbuf_push_back(&stream->refs); +  *dest = ref; + +  return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); +} + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { +  nghttp3_qpack_header_block_ref *ref; + +  assert(nghttp3_ringbuf_len(&stream->refs)); + +  ref = +    *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + +  assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + +  nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe); + +  nghttp3_ringbuf_pop_front(&stream->refs); +} + +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *rbuf, +                                               uint64_t absidx) { +  DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n", +         absidx); +  return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, +                                                nghttp3_buf *rbuf, +                                                uint64_t absidx, +                                                uint64_t base) { +  DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64 +         " base=%" PRIu64 "\n", +         absidx, base); + +  if (absidx < base) { +    return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6, +                              encoder->ctx.mem); +  } + +  return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem); +} + +/* + * qpack_encoder_write_indexed_name writes generic indexed name.  |fb| + * is the first byte.  |nameidx| is an index of referenced name. + * |prefix| is a prefix of variable integer encoding.  |nv| is a + * header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, +                                            nghttp3_buf *buf, uint8_t fb, +                                            uint64_t nameidx, size_t prefix, +                                            const nghttp3_nv *nv) { +  int rv; +  size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix); +  uint8_t *p; +  size_t hlen; +  int h = 0; + +  hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); +  if (hlen * 4 < nv->valuelen * 3) { +    h = 1; +    len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; +  } else { +    len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; +  } + +  rv = reserve_buf(buf, len, encoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = buf->last; + +  *p = fb; +  p = nghttp3_qpack_put_varint(p, nameidx, prefix); + +  if (h) { +    *p = 0x80; +    p = nghttp3_qpack_put_varint(p, hlen, 7); +    p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); +  } else { +    *p = 0; +    p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); +    if (nv->valuelen) { +      p = nghttp3_cpymem(p, nv->value, nv->valuelen); +    } +  } + +  assert((size_t)(p - buf->last) == len); + +  buf->last = p; + +  return 0; +} + +int nghttp3_qpack_encoder_write_static_indexed_name( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, +  const nghttp3_nv *nv) { +  uint8_t fb = +    (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); + +  DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) " +         "absidx=%" PRIu64 " never=%d\n", +         absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); +  return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed_name( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, +  uint64_t base, const nghttp3_nv *nv) { +  uint8_t fb; + +  DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) " +         "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n", +         absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); + +  if (absidx < base) { +    fb = +      (uint8_t)(0x40 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); +    return qpack_encoder_write_indexed_name(encoder, rbuf, fb, +                                            base - absidx - 1, 4, nv); +  } + +  fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0; +  return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3, +                                          nv); +} + +/* + * qpack_encoder_write_literal writes generic literal header field + * representation.  |fb| is a first byte.  |prefix| is a prefix of + * variable integer encoding for name length.  |nv| is a header field + * to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, +                                       nghttp3_buf *buf, uint8_t fb, +                                       size_t prefix, const nghttp3_nv *nv) { +  int rv; +  size_t len; +  uint8_t *p; +  size_t nhlen, vhlen; +  int nh = 0, vh = 0; + +  nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); +  if (nhlen * 4 < nv->namelen * 3) { +    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 * 4 < nv->valuelen * 3) { +    vh = 1; +    len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; +  } else { +    len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; +  } + +  rv = reserve_buf(buf, len, encoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = buf->last; + +  *p = fb; +  if (nh) { +    *p |= (uint8_t)(1 << prefix); +    p = nghttp3_qpack_put_varint(p, nhlen, prefix); +    p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen); +  } else { +    p = nghttp3_qpack_put_varint(p, nv->namelen, prefix); +    if (nv->namelen) { +      p = nghttp3_cpymem(p, nv->name, nv->namelen); +    } +  } + +  *p = 0; + +  if (vh) { +    *p |= 0x80; +    p = nghttp3_qpack_put_varint(p, vhlen, 7); +    p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); +  } else { +    p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); +    if (nv->valuelen) { +      p = nghttp3_cpymem(p, nv->value, nv->valuelen); +    } +  } + +  assert((size_t)(p - buf->last) == len); + +  buf->last = p; + +  return 0; +} + +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, +                                        nghttp3_buf *rbuf, +                                        const nghttp3_nv *nv) { +  uint8_t fb = +    (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0)); + +  DEBUGF("qpack::encode: Literal Field Line With Literal Name\n"); +  return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv); +} + +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, +                                              nghttp3_buf *ebuf, +                                              uint64_t absidx, +                                              const nghttp3_nv *nv) { +  DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64 +         "\n", +         absidx); +  return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *ebuf, +                                               uint64_t absidx, +                                               const nghttp3_nv *nv) { +  DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64 +         "\n", +         absidx); +  return qpack_encoder_write_indexed_name( +    encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv); +} + +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, +                                                 nghttp3_buf *ebuf, +                                                 uint64_t absidx) { +  uint64_t idx = encoder->ctx.next_absidx - absidx - 1; +  size_t len = nghttp3_qpack_put_varint_len(idx, 5); +  uint8_t *p; +  int rv; + +  DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx); + +  rv = reserve_buf(ebuf, len, encoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = ebuf->last; + +  *p = 0; +  p = nghttp3_qpack_put_varint(p, idx, 5); + +  assert((size_t)(p - ebuf->last) == len); + +  ebuf->last = p; + +  return 0; +} + +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *ebuf, +                                               const nghttp3_nv *nv) { +  DEBUGF("qpack::encode: Insert With Literal Name\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_capacity); + +  while (ctx->dtable_size + space > ctx->max_dtable_capacity) { +    i = nghttp3_ringbuf_len(&ctx->dtable); +    assert(i); +    ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); + +    ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); + +    nghttp3_ringbuf_pop_back(&ctx->dtable); +    if (dtable_map) { +      qpack_map_remove(dtable_map, ent); +    } + +    nghttp3_qpack_entry_free(ent); +    nghttp3_mem_free(mem, ent); +  } + +  new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry)); +  if (new_ent == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++, +                           hash); + +  if (nghttp3_ringbuf_full(&ctx->dtable)) { +    rv = nghttp3_ringbuf_reserve(&ctx->dtable, +                                 nghttp3_ringbuf_len(&ctx->dtable) * 2); +    if (rv != 0) { +      goto fail; +    } +  } + +  p = nghttp3_ringbuf_push_front(&ctx->dtable); +  *p = new_ent; + +  if (dtable_map) { +    qpack_map_insert(dtable_map, new_ent); +  } + +  ctx->dtable_size += space; +  ctx->dtable_sum += space; + +  return 0; + +fail: +  nghttp3_qpack_entry_free(new_ent); +  nghttp3_mem_free(mem, new_ent); + +  return rv; +} + +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, +                                            uint64_t absidx, +                                            const nghttp3_nv *nv, +                                            uint32_t hash) { +  const nghttp3_qpack_static_header *shd; +  nghttp3_qpack_nv qnv; +  const nghttp3_mem *mem = encoder->ctx.mem; +  int rv; + +  rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); +  if (rv != 0) { +    return rv; +  } + +  assert(nghttp3_arraylen(stable) > absidx); + +  shd = &stable[absidx]; + +  qnv.name = (nghttp3_rcbuf *)&shd->name; +  qnv.token = shd->token; +  qnv.flags = NGHTTP3_NV_FLAG_NONE; + +  rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, +                                        &encoder->dtable_map, hash); + +  nghttp3_rcbuf_decref(qnv.value); + +  return rv; +} + +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, +                                             uint64_t absidx, +                                             const nghttp3_nv *nv, +                                             uint32_t hash) { +  nghttp3_qpack_nv qnv; +  nghttp3_qpack_entry *ent; +  const nghttp3_mem *mem = encoder->ctx.mem; +  int rv; + +  rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); +  if (rv != 0) { +    return rv; +  } + +  ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + +  qnv.name = ent->nv.name; +  qnv.token = ent->nv.token; +  qnv.flags = NGHTTP3_NV_FLAG_NONE; + +  nghttp3_rcbuf_incref(qnv.name); + +  rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, +                                        &encoder->dtable_map, hash); + +  nghttp3_rcbuf_decref(qnv.value); +  nghttp3_rcbuf_decref(qnv.name); + +  return rv; +} + +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, +                                               uint64_t absidx) { +  nghttp3_qpack_nv qnv; +  nghttp3_qpack_entry *ent; +  int rv; + +  ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + +  qnv = ent->nv; +  nghttp3_rcbuf_incref(qnv.name); +  nghttp3_rcbuf_incref(qnv.value); + +  rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, +                                        &encoder->dtable_map, ent->hash); + +  nghttp3_rcbuf_decref(qnv.name); +  nghttp3_rcbuf_decref(qnv.value); + +  return rv; +} + +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, +                                             const nghttp3_nv *nv, +                                             int32_t token, uint32_t hash) { +  nghttp3_qpack_nv qnv; +  const nghttp3_mem *mem = encoder->ctx.mem; +  int rv; + +  rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem); +  if (rv != 0) { +    return rv; +  } + +  rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); +  if (rv != 0) { +    nghttp3_rcbuf_decref(qnv.name); +    return rv; +  } + +  qnv.token = token; +  qnv.flags = NGHTTP3_NV_FLAG_NONE; + +  rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, +                                        &encoder->dtable_map, hash); + +  nghttp3_rcbuf_decref(qnv.value); +  nghttp3_rcbuf_decref(qnv.name); + +  return rv; +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) { +  size_t relidx; + +  assert(ctx->next_absidx > absidx); +  assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable)); + +  relidx = (size_t)(ctx->next_absidx - absidx - 1); + +  return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx); +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) { +  assert(nghttp3_ringbuf_len(&ctx->dtable)); +  return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0); +} + +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, +                              size_t sum, uint64_t absidx, uint32_t hash) { +  ent->nv = *qnv; +  ent->map_next = NULL; +  ent->sum = sum; +  ent->absidx = absidx; +  ent->hash = hash; + +  nghttp3_rcbuf_incref(ent->nv.name); +  nghttp3_rcbuf_incref(ent->nv.value); +} + +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { +  nghttp3_rcbuf_decref(ent->nv.value); +  nghttp3_rcbuf_decref(ent->nv.name); +} + +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, +                                       nghttp3_qpack_stream *stream) { +  nghttp3_blocked_streams_key bsk = { +    nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), +                      nghttp3_qpack_header_block_ref, max_cnts_pe) +      ->max_cnt, +    (uint64_t)stream->stream_id}; + +  return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); +} + +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, +                                          nghttp3_qpack_stream *stream) { +  nghttp3_blocked_streams_key bsk = { +    nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), +                      nghttp3_qpack_header_block_ref, max_cnts_pe) +      ->max_cnt, +    (uint64_t)stream->stream_id}; +  nghttp3_ksl_it it; + +  /* This is purely debugging purpose only */ +  it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); + +  assert(!nghttp3_ksl_it_end(&it)); +  assert(nghttp3_ksl_it_get(&it) == stream); + +  nghttp3_ksl_remove_hint(&encoder->blocked_streams, NULL, &it, &bsk); +} + +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, +                                   uint64_t max_cnt) { +  nghttp3_blocked_streams_key bsk = {max_cnt, 0}; +  nghttp3_ksl_it it; + +  it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); + +  for (; !nghttp3_ksl_it_end(&it);) { +    bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it); +    nghttp3_ksl_remove_hint(&encoder->blocked_streams, &it, &it, &bsk); +  } +} + +int 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) { +    return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; +  } + +  assert(nghttp3_ringbuf_len(&stream->refs)); + +  ref = +    *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + +  DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64 +         " krcnt=%" PRIu64 "\n", +         stream_id, ref->max_cnt, encoder->krcnt); + +  if (encoder->krcnt < ref->max_cnt) { +    encoder->krcnt = ref->max_cnt; + +    nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt); +  } + +  nghttp3_qpack_stream_pop_ref(stream); + +  assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + +  nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); + +  nghttp3_qpack_header_block_ref_del(ref, mem); + +  if (nghttp3_ringbuf_len(&stream->refs)) { +    return 0; +  } + +  qpack_encoder_remove_stream(encoder, stream); + +  nghttp3_qpack_stream_del(stream, mem); + +  return 0; +} + +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n) { +  if (n == 0 || 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(&encoder->streams, map_stream_free, +                   (void *)encoder->ctx.mem); +  nghttp3_map_clear(&encoder->streams); +} + +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_streams(nghttp3_qpack_encoder *encoder) { +  return nghttp3_ksl_len(&encoder->blocked_streams); +} + +int nghttp3_qpack_encoder_write_field_section_prefix( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, +  uint64_t base) { +  size_t max_ents = +    encoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; +  uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; +  int sign = base < ricnt; +  uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt; +  size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) + +               nghttp3_qpack_put_varint_len(delta_base, 7); +  uint8_t *p; +  int rv; + +  DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", +         ricnt, base, encoder->ctx.next_absidx); + +  rv = reserve_buf(pbuf, len, encoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = pbuf->last; + +  p = nghttp3_qpack_put_varint(p, encricnt, 8); +  if (sign) { +    *p = 0x80; +  } else { +    *p = 0; +  } +  p = nghttp3_qpack_put_varint(p, delta_base, 7); + +  assert((size_t)(p - pbuf->last) == len); + +  pbuf->last = p; + +  return 0; +} + +/* + * qpack_read_varint reads |rstate->prefix| prefixed integer stored + * from |begin|.  The |end| represents the 1 beyond the last of the + * valid contiguous memory region from |begin|.  The decoded integer + * must be less than or equal to NGHTTP3_QPACK_INT_MAX. + * + * If the |rstate->left| is nonzero, it is used as an initial value, + * and this function assumes the |begin| starts with intermediate + * data.  |rstate->shift| is used as initial integer shift. + * + * If an entire integer is decoded successfully, the |*fin| is set to + * nonzero. + * + * This function stores the decoded integer in |rstate->left| if it + * succeeds, including partial decoding (in this case, number of shift + * to make in the next call will be stored in |rstate->shift|) and + * returns number of bytes processed, or returns negative error code + * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error. + */ +static nghttp3_ssize qpack_read_varint(int *fin, +                                       nghttp3_qpack_read_state *rstate, +                                       const uint8_t *begin, +                                       const uint8_t *end) { +  uint64_t k = (uint8_t)((1 << rstate->prefix) - 1); +  uint64_t n = rstate->left; +  uint64_t add; +  const uint8_t *p = begin; +  size_t shift = rstate->shift; + +  rstate->shift = 0; +  *fin = 0; + +  if (n == 0) { +    if (((*p) & k) != k) { +      rstate->left = (*p) & k; +      *fin = 1; +      return 1; +    } + +    n = k; + +    if (++p == end) { +      rstate->left = n; +      return (nghttp3_ssize)(p - begin); +    } +  } + +  for (; p != end; ++p, shift += 7) { +    add = (*p) & 0x7f; + +    if (shift > 62) { +      return NGHTTP3_ERR_QPACK_FATAL; +    } + +    if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) { +      return NGHTTP3_ERR_QPACK_FATAL; +    } + +    add <<= shift; + +    if (NGHTTP3_QPACK_INT_MAX - add < n) { +      return NGHTTP3_ERR_QPACK_FATAL; +    } + +    n += add; + +    if (((*p) & (1 << 7)) == 0) { +      break; +    } +  } + +  rstate->shift = shift; + +  if (p == end) { +    rstate->left = n; +    return (nghttp3_ssize)(p - begin); +  } + +  rstate->left = n; +  *fin = 1; +  return (nghttp3_ssize)(p + 1 - begin); +} + +nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, +                                                 const uint8_t *src, +                                                 size_t srclen) { +  const uint8_t *p = src, *end; +  int rv; +  nghttp3_ssize nread; +  int rfin; + +  if (encoder->ctx.bad) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  if (srclen == 0) { +    return 0; +  } + +  encoder->uninterrupted_decoderlen += srclen; +  if (encoder->uninterrupted_decoderlen > NGHTTP3_QPACK_MAX_DECODERLEN) { +    return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; +  } + +  end = src + srclen; + +  for (; p != end;) { +    switch (encoder->state) { +    case NGHTTP3_QPACK_DS_STATE_OPCODE: +      switch ((*p) & 0xc0) { +      case 0x80: +      case 0xc0: +        DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n"); +        encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK; +        encoder->rstate.prefix = 7; + +        break; +      case 0x40: +        DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n"); +        encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL; +        encoder->rstate.prefix = 6; + +        break; +      default: +        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_icnt(encoder, encoder->rstate.left); +        if (rv != 0) { +          goto fail; +        } +        break; +      case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK: +        rv = nghttp3_qpack_encoder_ack_header(encoder, +                                              (int64_t)encoder->rstate.left); +        if (rv != 0) { +          goto fail; +        } +        break; +      case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: +        nghttp3_qpack_encoder_cancel_stream(encoder, +                                            (int64_t)encoder->rstate.left); +        break; +      default: +        nghttp3_unreachable(); +      } + +      encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; +      nghttp3_qpack_read_state_reset(&encoder->rstate); +      break; +    default: +      nghttp3_unreachable(); +    } +  } + +  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 hard_max_dtable_capacity, +                               size_t max_blocked_streams, +                               const nghttp3_mem *mem) { +  int rv; + +  rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, +                          max_blocked_streams, mem); +  if (rv != 0) { +    return rv; +  } + +  decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; +  decoder->opcode = 0; +  decoder->written_icnt = 0; +  decoder->max_concurrent_streams = 0; +  decoder->uninterrupted_encoderlen = 0; + +  nghttp3_qpack_read_state_reset(&decoder->rstate); +  nghttp3_buf_init(&decoder->dbuf); + +  return 0; +} + +void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { +  nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem); +  nghttp3_qpack_read_state_free(&decoder->rstate); +  qpack_context_free(&decoder->ctx); +} + +/* + * qpack_read_huffman_string decodes huffman string in buffer [begin, + * end) and writes the decoded string to |dest|.  This function + * assumes the buffer pointed by |dest| has enough space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_FATAL + *     Could not decode huffman string. + */ +static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate, +                                               nghttp3_buf *dest, +                                               const uint8_t *begin, +                                               const uint8_t *end) { +  nghttp3_ssize nwrite; +  size_t len = (size_t)(end - begin); +  int fin = 0; + +  if (len >= rstate->left) { +    len = (size_t)rstate->left; +    fin = 1; +  } + +  nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin, +                                        len, fin); +  if (nwrite < 0) { +    return nwrite; +  } + +  if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  dest->last += nwrite; +  rstate->left -= len; +  return (nghttp3_ssize)len; +} + +static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, +                                       nghttp3_buf *dest, const uint8_t *begin, +                                       const uint8_t *end) { +  size_t len = (size_t)(end - begin); +  size_t n = (size_t)nghttp3_min_uint64((uint64_t)len, rstate->left); + +  dest->last = nghttp3_cpymem(dest->last, begin, n); + +  rstate->left -= n; +  return (nghttp3_ssize)n; +} + +/* + * qpack_decoder_validate_index checks rstate->absidx is acceptable. + * + * It returns 0 if it succeeds, 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; +  } + +  decoder->uninterrupted_encoderlen += srclen; +  if (decoder->uninterrupted_encoderlen > NGHTTP3_QPACK_MAX_ENCODERLEN) { +    return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +  } + +  end = src + srclen; + +  for (; p != end || busy;) { +    busy = 0; +    switch (decoder->state) { +    case NGHTTP3_QPACK_ES_STATE_OPCODE: +      switch ((*p) & 0xe0) { +      case 0x80: +      case 0xa0: +      case 0xc0: +      case 0xe0: +        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; + +        break; +      case 0x40: +      case 0x60: +        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; + +        break; +      case 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; + +        break; +      default: +        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; +      } +      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) { +        DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", +               decoder->rstate.left); +        rv = nghttp3_qpack_decoder_set_max_dtable_capacity( +          decoder, (size_t)decoder->rstate.left); +        if (rv != 0) { +          rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +          goto fail; +        } + +        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; +      } + +      switch (decoder->opcode) { +      case 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; +      case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: +        decoder->rstate.prefix = 7; +        decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + +        break; +      default: +        nghttp3_unreachable(); +      } + +      break; +    case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN: +      qpack_read_state_check_huffman(&decoder->rstate, *p); +      decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN; +      decoder->rstate.left = 0; +      decoder->rstate.shift = 0; +      /* Fall through */ +    case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN: +      nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        return p - src; +      } + +      if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { +        rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; +        goto fail; +      } + +      if (decoder->rstate.huffman_encoded) { +        decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN; +        nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); +        rv = nghttp3_rcbuf_new(&decoder->rstate.name, +                               (size_t)decoder->rstate.left * 2 + 1, mem); +      } else { +        decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME; +        rv = nghttp3_rcbuf_new(&decoder->rstate.name, +                               (size_t)decoder->rstate.left + 1, mem); +      } +      if (rv != 0) { +        goto fail; +      } + +      nghttp3_buf_wrap_init(&decoder->rstate.namebuf, +                            decoder->rstate.name->base, +                            decoder->rstate.name->len); +      break; +    case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN: +      nread = qpack_read_huffman_string(&decoder->rstate, +                                        &decoder->rstate.namebuf, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +        goto fail; +      } + +      p += nread; + +      if (decoder->rstate.left) { +        return p - src; +      } + +      qpack_read_state_terminate_name(&decoder->rstate); + +      decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; +      decoder->rstate.prefix = 7; +      break; +    case NGHTTP3_QPACK_ES_STATE_READ_NAME: +      nread = +        qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end); +      if (nread < 0) { +        rv = (int)nread; +        goto fail; +      } + +      p += nread; + +      if (decoder->rstate.left) { +        return p - src; +      } + +      qpack_read_state_terminate_name(&decoder->rstate); + +      decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; +      decoder->rstate.prefix = 7; +      break; +    case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN: +      qpack_read_state_check_huffman(&decoder->rstate, *p); +      decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN; +      decoder->rstate.left = 0; +      decoder->rstate.shift = 0; +      /* Fall through */ +    case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN: +      nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        return p - src; +      } + +      if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { +        rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; +        goto fail; +      } + +      if (decoder->rstate.huffman_encoded) { +        decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN; +        nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); +        rv = nghttp3_rcbuf_new(&decoder->rstate.value, +                               (size_t)decoder->rstate.left * 2 + 1, mem); +      } else { +        decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE; +        rv = nghttp3_rcbuf_new(&decoder->rstate.value, +                               (size_t)decoder->rstate.left + 1, mem); +      } +      if (rv != 0) { +        goto fail; +      } + +      nghttp3_buf_wrap_init(&decoder->rstate.valuebuf, +                            decoder->rstate.value->base, +                            decoder->rstate.value->len); + +      /* value might be 0 length */ +      busy = 1; +      break; +    case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN: +      nread = qpack_read_huffman_string(&decoder->rstate, +                                        &decoder->rstate.valuebuf, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +        goto fail; +      } + +      p += nread; + +      if (decoder->rstate.left) { +        return p - src; +      } + +      qpack_read_state_terminate_value(&decoder->rstate); + +      switch (decoder->opcode) { +      case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: +        rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); +        break; +      case NGHTTP3_QPACK_ES_OPCODE_INSERT: +        rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); +        break; +      default: +        nghttp3_unreachable(); +      } +      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: +        nghttp3_unreachable(); +      } +      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; +} + +int nghttp3_qpack_decoder_set_max_dtable_capacity( +  nghttp3_qpack_decoder *decoder, size_t max_dtable_capacity) { +  nghttp3_qpack_entry *ent; +  size_t i; +  nghttp3_qpack_context *ctx = &decoder->ctx; +  const nghttp3_mem *mem = ctx->mem; + +  if (max_dtable_capacity > decoder->ctx.hard_max_dtable_capacity) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  ctx->max_dtable_capacity = max_dtable_capacity; + +  while (ctx->dtable_size > max_dtable_capacity) { +    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); +  } + +  return 0; +} + +int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { +  DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": " +         "value=%*s\n", +         decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx, +         (int)decoder->rstate.value->len, decoder->rstate.value->base); + +  if (decoder->rstate.dynamic) { +    return nghttp3_qpack_decoder_dtable_dynamic_add(decoder); +  } + +  return nghttp3_qpack_decoder_dtable_static_add(decoder); +} + +int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { +  nghttp3_qpack_nv qnv; +  int rv; +  const nghttp3_qpack_static_header *shd; + +  shd = &stable[decoder->rstate.absidx]; + +  if (table_space(shd->name.len, decoder->rstate.value->len) > +      decoder->ctx.max_dtable_capacity) { +    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_capacity) { +    return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +  } + +  qnv.name = ent->nv.name; +  qnv.value = decoder->rstate.value; +  qnv.token = ent->nv.token; +  qnv.flags = NGHTTP3_NV_FLAG_NONE; + +  nghttp3_rcbuf_incref(qnv.name); + +  rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + +  nghttp3_rcbuf_decref(qnv.value); +  nghttp3_rcbuf_decref(qnv.name); + +  return rv; +} + +int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { +  int rv; +  nghttp3_qpack_entry *ent; +  nghttp3_qpack_nv qnv; + +  DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n", +         decoder->rstate.absidx); + +  ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); + +  if (table_space(ent->nv.name->len, ent->nv.value->len) > +      decoder->ctx.max_dtable_capacity) { +    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 With Literal Name: 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_capacity) { +    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_size(decoder->max_concurrent_streams, max_concurrent_streams); +} + +void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, +                                       int64_t stream_id, +                                       const nghttp3_mem *mem) { +  nghttp3_qpack_read_state_reset(&sctx->rstate); + +  sctx->mem = mem; +  sctx->rstate.prefix = 8; +  sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT; +  sctx->opcode = 0; +  sctx->stream_id = stream_id; +  sctx->ricnt = 0; +  sctx->dbase_sign = 0; +  sctx->base = 0; +} + +void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) { +  nghttp3_qpack_read_state_free(&sctx->rstate); +} + +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) { +  nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem); +} + +uint64_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) { +  return sctx->ricnt; +} + +nghttp3_ssize +nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, +                                   nghttp3_qpack_stream_context *sctx, +                                   nghttp3_qpack_nv *nv, uint8_t *pflags, +                                   const uint8_t *src, size_t srclen, int fin) { +  const uint8_t *p = src, *end = src ? src + srclen : src; +  int rv; +  int busy = 0; +  nghttp3_ssize nread; +  int rfin; +  const nghttp3_mem *mem = decoder->ctx.mem; + +  if (decoder->ctx.bad) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE; + +  for (; p != end || busy;) { +    busy = 0; +    switch (sctx->state) { +    case NGHTTP3_QPACK_RS_STATE_RICNT: +      nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        goto almost_ok; +      } + +      rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt, +                                                   sctx->rstate.left); +      if (rv != 0) { +        goto fail; +      } + +      sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN; +      break; +    case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN: +      if ((*p) & 0x80) { +        sctx->dbase_sign = 1; +      } +      sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE; +      sctx->rstate.left = 0; +      sctx->rstate.prefix = 7; +      sctx->rstate.shift = 0; +      /* Fall through */ +    case NGHTTP3_QPACK_RS_STATE_DBASE: +      nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        goto almost_ok; +      } + +      if (sctx->dbase_sign) { +        if (sctx->ricnt <= sctx->rstate.left) { +          rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +          goto fail; +        } +        sctx->base = sctx->ricnt - sctx->rstate.left - 1; +      } else { +        sctx->base = sctx->ricnt + sctx->rstate.left; +      } + +      DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 +             "\n", +             sctx->ricnt, sctx->base, decoder->ctx.next_absidx); + +      if (sctx->ricnt > decoder->ctx.next_absidx) { +        DEBUGF("qpack::decode: stream blocked\n"); +        sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED; +        *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; +        return p - src; +      } + +      sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; +      sctx->rstate.left = 0; +      sctx->rstate.shift = 0; +      break; +    case NGHTTP3_QPACK_RS_STATE_OPCODE: +      assert(sctx->rstate.left == 0); +      assert(sctx->rstate.shift == 0); +      switch ((*p) & 0xf0) { +      case 0x80: +      case 0x90: +      case 0xa0: +      case 0xb0: +      case 0xc0: +      case 0xd0: +      case 0xe0: +      case 0xf0: +        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; + +        break; +      case 0x40: +      case 0x50: +      case 0x60: +      case 0x70: +        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; + +        break; +      case 0x20: +      case 0x30: +        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; + +        break; +      case 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; + +        break; +      default: +        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: +        nghttp3_unreachable(); +      } +      break; +    case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN: +      qpack_read_state_check_huffman(&sctx->rstate, *p); +      sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN; +      sctx->rstate.left = 0; +      sctx->rstate.shift = 0; +      /* Fall through */ +    case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN: +      nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        goto almost_ok; +      } + +      if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { +        rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; +        goto fail; +      } + +      if (sctx->rstate.huffman_encoded) { +        sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN; +        nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); +        rv = nghttp3_rcbuf_new(&sctx->rstate.name, +                               (size_t)sctx->rstate.left * 2 + 1, mem); +      } else { +        sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME; +        rv = nghttp3_rcbuf_new(&sctx->rstate.name, +                               (size_t)sctx->rstate.left + 1, mem); +      } +      if (rv != 0) { +        goto fail; +      } + +      nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base, +                            sctx->rstate.name->len); +      break; +    case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN: +      nread = +        qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (sctx->rstate.left) { +        goto almost_ok; +      } + +      qpack_read_state_terminate_name(&sctx->rstate); + +      sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; +      sctx->rstate.prefix = 7; +      break; +    case NGHTTP3_QPACK_RS_STATE_READ_NAME: +      nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end); +      if (nread < 0) { +        rv = (int)nread; +        goto fail; +      } + +      p += nread; + +      if (sctx->rstate.left) { +        goto almost_ok; +      } + +      qpack_read_state_terminate_name(&sctx->rstate); + +      sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; +      sctx->rstate.prefix = 7; +      break; +    case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN: +      qpack_read_state_check_huffman(&sctx->rstate, *p); +      sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN; +      sctx->rstate.left = 0; +      sctx->rstate.shift = 0; +      /* Fall through */ +    case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN: +      nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (!rfin) { +        goto almost_ok; +      } + +      if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { +        rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; +        goto fail; +      } + +      if (sctx->rstate.huffman_encoded) { +        sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN; +        nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); +        rv = nghttp3_rcbuf_new(&sctx->rstate.value, +                               (size_t)sctx->rstate.left * 2 + 1, mem); +      } else { +        sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE; +        rv = nghttp3_rcbuf_new(&sctx->rstate.value, +                               (size_t)sctx->rstate.left + 1, mem); +      } +      if (rv != 0) { +        goto fail; +      } + +      nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base, +                            sctx->rstate.value->len); + +      /* value might be 0 length */ +      busy = 1; +      break; +    case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN: +      nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf, +                                        p, end); +      if (nread < 0) { +        assert(NGHTTP3_ERR_QPACK_FATAL == nread); +        rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +        goto fail; +      } + +      p += nread; + +      if (sctx->rstate.left) { +        goto almost_ok; +      } + +      qpack_read_state_terminate_value(&sctx->rstate); + +      switch (sctx->opcode) { +      case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: +      case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: +        rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); +        if (rv != 0) { +          goto fail; +        } + +        break; +      case NGHTTP3_QPACK_RS_OPCODE_LITERAL: +        nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); +        break; +      default: +        nghttp3_unreachable(); +      } + +      *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: +        rv = nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); +        if (rv != 0) { +          goto fail; +        } + +        break; +      case NGHTTP3_QPACK_RS_OPCODE_LITERAL: +        nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); +        break; +      default: +        nghttp3_unreachable(); +      } + +      *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + +      sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; +      nghttp3_qpack_read_state_reset(&sctx->rstate); + +      return p - src; +    case NGHTTP3_QPACK_RS_STATE_BLOCKED: +      if (sctx->ricnt > decoder->ctx.next_absidx) { +        DEBUGF("qpack::decode: stream still blocked\n"); +        *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; +        return p - src; +      } +      sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; +      nghttp3_qpack_read_state_reset(&sctx->rstate); +      break; +    } +  } + +almost_ok: +  if (fin) { +    if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) { +      rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +      goto fail; +    } + +    *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL; + +    if (sctx->ricnt) { +      rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx); +      if (rv != 0) { +        goto fail; +      } +    } + +    decoder->uninterrupted_encoderlen = 0; +  } + +  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_size(decoder->max_concurrent_streams, 100); +  /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */ +  return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10; +} + +int nghttp3_qpack_decoder_write_section_ack( +  nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) { +  nghttp3_buf *dbuf = &decoder->dbuf; +  uint8_t *p; +  int rv; + +  if (qpack_decoder_dbuf_overflow(decoder)) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  rv = reserve_buf(dbuf, +                   nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7), +                   decoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = dbuf->last; +  *p = 0x80; +  dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7); + +  if (decoder->written_icnt < sctx->ricnt) { +    decoder->written_icnt = sctx->ricnt; +  } + +  return 0; +} + +size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) { +  uint64_t n; +  size_t len = 0; + +  if (decoder->written_icnt < decoder->ctx.next_absidx) { +    n = decoder->ctx.next_absidx - decoder->written_icnt; +    len = nghttp3_qpack_put_varint_len(n, 6); +  } + +  return nghttp3_buf_len(&decoder->dbuf) + len; +} + +void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, +                                         nghttp3_buf *dbuf) { +  uint8_t *p; +  uint64_t n = 0; +  size_t len = 0; +  (void)len; + +  if (decoder->written_icnt < decoder->ctx.next_absidx) { +    n = decoder->ctx.next_absidx - decoder->written_icnt; +    len = nghttp3_qpack_put_varint_len(n, 6); +  } + +  assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len); + +  if (nghttp3_buf_len(&decoder->dbuf)) { +    dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos, +                                nghttp3_buf_len(&decoder->dbuf)); +  } + +  if (n) { +    p = dbuf->last; +    *p = 0; +    dbuf->last = nghttp3_qpack_put_varint(p, n, 6); + +    decoder->written_icnt = decoder->ctx.next_absidx; +  } + +  nghttp3_buf_reset(&decoder->dbuf); +} + +int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, +                                        int64_t stream_id) { +  uint8_t *p; +  int rv; + +  if (qpack_decoder_dbuf_overflow(decoder)) { +    return NGHTTP3_ERR_QPACK_FATAL; +  } + +  rv = reserve_buf(&decoder->dbuf, +                   nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6), +                   decoder->ctx.mem); +  if (rv != 0) { +    return rv; +  } + +  p = decoder->dbuf.last; +  *p = 0x40; +  decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6); + +  return 0; +} + +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, +                                            uint64_t *dest, uint64_t encricnt) { +  uint64_t max_ents, full, max, max_wrapped, ricnt; + +  if (encricnt == 0) { +    *dest = 0; +    return 0; +  } + +  max_ents = +    decoder->ctx.hard_max_dtable_capacity / NGHTTP3_QPACK_ENTRY_OVERHEAD; +  full = 2 * max_ents; + +  if (encricnt > full) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } + +  max = decoder->ctx.next_absidx + max_ents; +  max_wrapped = max / full * full; +  ricnt = max_wrapped + encricnt - 1; + +  if (ricnt > max) { +    if (ricnt <= full) { +      return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +    } +    ricnt -= full; +  } + +  if (ricnt == 0) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } + +  *dest = ricnt; + +  return 0; +} + +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, +                                  nghttp3_qpack_read_state *rstate) { +  DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n", +         rstate->dynamic, rstate->left, decoder->ctx.next_absidx); + +  if (rstate->dynamic) { +    if (decoder->ctx.next_absidx < rstate->left + 1) { +      return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +    } +    rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1; +  } else { +    rstate->absidx = rstate->left; +  } +  if (qpack_decoder_validate_index(decoder, rstate) != 0) { +    return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; +  } +  return 0; +} + +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, +                                   nghttp3_qpack_stream_context *sctx) { +  nghttp3_qpack_read_state *rstate = &sctx->rstate; + +  DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64 +         " icnt=%" PRIu64 "\n", +         rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx); + +  if (rstate->dynamic) { +    if (sctx->base < rstate->left + 1) { +      return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +    } +    rstate->absidx = sctx->base - rstate->left - 1; + +    if (rstate->absidx >= sctx->ricnt) { +      return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +    } +  } else { +    rstate->absidx = rstate->left; +  } + +  if (qpack_decoder_validate_index(decoder, rstate) != 0) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } +  return 0; +} + +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, +                                    nghttp3_qpack_stream_context *sctx) { +  nghttp3_qpack_read_state *rstate = &sctx->rstate; + +  DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", +         rstate->left, sctx->base, decoder->ctx.next_absidx); + +  assert(rstate->dynamic); + +  rstate->absidx = rstate->left + sctx->base; + +  if (rstate->absidx >= sctx->ricnt) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } + +  if (qpack_decoder_validate_index(decoder, rstate) != 0) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } +  return 0; +} + +static void +qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder, +                                  nghttp3_qpack_stream_context *sctx, +                                  nghttp3_qpack_nv *nv) { +  const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; +  (void)decoder; + +  nv->name = (nghttp3_rcbuf *)&shd->name; +  nv->value = (nghttp3_rcbuf *)&shd->value; +  nv->token = shd->token; +  nv->flags = NGHTTP3_NV_FLAG_NONE; +} + +static void +qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder, +                                   nghttp3_qpack_stream_context *sctx, +                                   nghttp3_qpack_nv *nv) { +  nghttp3_qpack_entry *ent = +    nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + +  *nv = ent->nv; + +  nghttp3_rcbuf_incref(nv->name); +  nghttp3_rcbuf_incref(nv->value); +} + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, +                                        nghttp3_qpack_stream_context *sctx, +                                        nghttp3_qpack_nv *nv) { +  DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n", +         sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx); + +  if (sctx->rstate.dynamic) { +    qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv); +  } else { +    qpack_decoder_emit_static_indexed(decoder, sctx, nv); +  } +} + +static void +qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, +                                       nghttp3_qpack_stream_context *sctx, +                                       nghttp3_qpack_nv *nv) { +  const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; +  (void)decoder; + +  nv->name = (nghttp3_rcbuf *)&shd->name; +  nv->value = sctx->rstate.value; +  nv->token = shd->token; +  nv->flags = +    sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + +  sctx->rstate.value = NULL; +} + +static int +qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, +                                        nghttp3_qpack_stream_context *sctx, +                                        nghttp3_qpack_nv *nv) { +  nghttp3_qpack_entry *ent; + +  /* A broken encoder might change dtable capacity while processing +     request stream instruction.  Check the absidx again. */ +  if (qpack_decoder_validate_index(decoder, &sctx->rstate) != 0) { +    return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; +  } + +  ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + +  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; + +  return 0; +} + +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, +                                            nghttp3_qpack_stream_context *sctx, +                                            nghttp3_qpack_nv *nv) { +  (void)decoder; + +  DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n", +         sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx, +         (int)sctx->rstate.value->len, sctx->rstate.value->base); + +  if (sctx->rstate.dynamic) { +    return qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); +  } + +  qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + +  return 0; +} + +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 hard_max_dtable_capacity, +                              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, hard_max_dtable_capacity, 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 hard_max_dtable_capacity, +                              size_t max_blocked_streams, +                              const nghttp3_mem *mem) { +  int rv; +  nghttp3_qpack_decoder *p; + +  p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); +  if (p == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, +                                  max_blocked_streams, mem); +  if (rv != 0) { +    return rv; +  } + +  *pdecoder = p; + +  return 0; +} + +void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) { +  const nghttp3_mem *mem; + +  if (decoder == NULL) { +    return; +  } + +  mem = decoder->ctx.mem; + +  nghttp3_qpack_decoder_free(decoder); +  nghttp3_mem_free(mem, decoder); +} + +uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) { +  return decoder->ctx.next_absidx; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_qpack.h b/contrib/libs/nghttp3/lib/nghttp3_qpack.h new file mode 100644 index 00000000000..d2bb8a35811 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_qpack.h @@ -0,0 +1,1010 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_QPACK_H +#define NGHTTP3_QPACK_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_rcbuf.h" +#include "nghttp3_map.h" +#include "nghttp3_pq.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_ksl.h" +#include "nghttp3_qpack_huffman.h" + +#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of +   header name this library can decode. */ +#define NGHTTP3_QPACK_MAX_NAMELEN 256 +/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of +   header value this library can decode. */ +#define NGHTTP3_QPACK_MAX_VALUELEN 65536 +/* NGHTTP3_QPACK_MAX_ENCODERLEN is the maximum encoder stream length +   that a decoder accepts without completely processing a single field +   section. */ +#define NGHTTP3_QPACK_MAX_ENCODERLEN (128 * 1024) +/* NGHTTP3_QPACK_MAX_DECODERLEN is the maximum decoder stream length +   that an encoder accepts without completely encoding a single field +   section. */ +#define NGHTTP3_QPACK_MAX_DECODERLEN (4 * 1024) + +/* nghttp3_qpack_indexing_mode is a indexing strategy. */ +typedef enum nghttp3_qpack_indexing_mode { +  /* 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; + +typedef struct nghttp3_qpack_entry nghttp3_qpack_entry; + +struct nghttp3_qpack_entry { +  /* The header field name/value pair */ +  nghttp3_qpack_nv nv; +  /* map_next points to the entry which shares same bucket in hash +     table. */ +  nghttp3_qpack_entry *map_next; +  /* sum is the sum of all entries inserted up to this entry.  This +     value does not contain the space required for this entry. */ +  size_t sum; +  /* absidx is the absolute index of this entry. */ +  uint64_t absidx; +  /* The hash value for header name (nv.name). */ +  uint32_t hash; +}; + +/* The entry used for static table. */ +typedef struct nghttp3_qpack_static_entry { +  uint64_t absidx; +  int32_t token; +  uint32_t hash; +} nghttp3_qpack_static_entry; + +typedef struct nghttp3_qpack_static_header { +  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_qpack_header_block_ref { +  nghttp3_pq_entry max_cnts_pe; +  nghttp3_pq_entry min_cnts_pe; +  /* max_cnt is the required insert count. */ +  uint64_t max_cnt; +  /* min_cnt is the minimum insert count of dynamic table entry it +     refers to.  In other words, this is the minimum absolute index of +     dynamic header table entry this encoded block refers to plus +     1. */ +  uint64_t min_cnt; +} nghttp3_qpack_header_block_ref; + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, +                                       uint64_t max_cnt, uint64_t min_cnt, +                                       const nghttp3_mem *mem); + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, +                                        const nghttp3_mem *mem); + +typedef struct nghttp3_qpack_stream { +  int64_t stream_id; +  /* refs is an array of pointer to nghttp3_qpack_header_block_ref in +     the order of the time they are encoded.  HTTP/3 allows multiple +     header blocks (e.g., non-final response headers, final response +     headers, trailers, and push promises) per stream. */ +  nghttp3_ringbuf refs; +  /* max_cnts is a priority queue sorted by descending order of +     max_cnt of nghttp3_qpack_header_block_ref. */ +  nghttp3_pq max_cnts; +} nghttp3_qpack_stream; + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, +                             const nghttp3_mem *mem); + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, +                              const nghttp3_mem *mem); + +uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream); + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, +                                 nghttp3_qpack_header_block_ref *ref); + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream); + +#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32 + +typedef struct nghttp3_qpack_context { +  /* 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_capacity is the upper bound of +     max_dtable_capacity. */ +  size_t hard_max_dtable_capacity; +  /* max_dtable_capacity is the maximum capacity of the dynamic +     table. */ +  size_t max_dtable_capacity; +  /* max_blocked_streams is the maximum number of stream which can be +     blocked. */ +  size_t max_blocked_streams; +  /* next_absidx is the next absolute index for nghttp3_qpack_entry. +     It is equivalent to insert count. */ +  uint64_t next_absidx; +  /* If inflate/deflate error occurred, this value is set to 1 and +     further invocation of inflate/deflate will fail with +     NGHTTP3_ERR_QPACK_FATAL. */ +  uint8_t bad; +} nghttp3_qpack_context; + +typedef struct nghttp3_qpack_read_state { +  nghttp3_qpack_huffman_decode_context huffman_ctx; +  nghttp3_buf namebuf; +  nghttp3_buf valuebuf; +  nghttp3_rcbuf *name; +  nghttp3_rcbuf *value; +  uint64_t left; +  size_t prefix; +  size_t shift; +  uint64_t absidx; +  int never; +  int dynamic; +  int huffman_encoded; +} nghttp3_qpack_read_state; + +void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate); + +void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate); + +#define NGHTTP3_QPACK_MAP_SIZE 64 + +typedef struct nghttp3_qpack_map { +  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_decoder_stream_state { +  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_decoder_stream_opcode { +  NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT, +  NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK, +  NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL, +} nghttp3_qpack_decoder_stream_opcode; + +/* QPACK encoder flags */ + +/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00u +/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that +   Set Dynamic Table Capacity is required. */ +#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01u + +struct nghttp3_qpack_encoder { +  nghttp3_qpack_context ctx; +  /* dtable_map is a map of hash to nghttp3_qpack_entry to provide +     fast access to an entry in dynamic table. */ +  nghttp3_qpack_map dtable_map; +  /* streams is a map of stream ID to nghttp3_qpack_stream to keep +     track of unacknowledged streams. */ +  nghttp3_map streams; +  /* blocked_streams is an ordered list of nghttp3_qpack_stream, in +     descending order of max_cnt, to search the unblocked streams by +     received known count. */ +  nghttp3_ksl blocked_streams; +  /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref +     sorted by ascending order of min_cnt to know that an entry can be +     evicted from dynamic table.  */ +  nghttp3_pq min_cnts; +  /* krcnt is Known Received Count. */ +  uint64_t krcnt; +  /* state is a current state of reading decoder stream. */ +  nghttp3_qpack_decoder_stream_state state; +  /* opcode is a decoder stream opcode being processed. */ +  nghttp3_qpack_decoder_stream_opcode opcode; +  /* rstate is a set of intermediate state which are used to process +     decoder stream. */ +  nghttp3_qpack_read_state rstate; +  /* min_dtable_update is the minimum dynamic table size required. */ +  size_t min_dtable_update; +  /* last_max_dtable_update is the dynamic table size last +     requested. */ +  size_t last_max_dtable_update; +  /* uninterrupted_decoderlen is the number of bytes read from decoder +     stream without encoding a single field section. */ +  size_t uninterrupted_decoderlen; +  /* flags is bitwise OR of zero or more of +     NGHTTP3_QPACK_ENCODER_FLAG_*. */ +  uint8_t flags; +}; + +/* + * nghttp3_qpack_encoder_init initializes |encoder|. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity.  |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, +                               size_t hard_max_dtable_capacity, +                               const nghttp3_mem *mem); + +/* + * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. + * This function does not free memory pointed by |encoder|. + */ +void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_encode_nv encodes |nv|.  It writes request + * stream into |rbuf| and writes encoder stream into |ebuf|.  |nv| is + * a header field to encode.  |base| is base.  |allow_blocking| is + * nonzero if this stream can be blocked (or it has been blocked + * already). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, +                                    uint64_t *pmax_cnt, uint64_t *pmin_cnt, +                                    nghttp3_buf *rbuf, nghttp3_buf *ebuf, +                                    const nghttp3_nv *nv, uint64_t base, +                                    int allow_blocking); + +/* nghttp3_qpack_lookup_result stores a result of table lookup. */ +typedef struct nghttp3_qpack_lookup_result { +  /* index is an index of matched entry.  -1 if no match is made. */ +  nghttp3_ssize index; +  /* name_value_match is nonzero if both name and value are +     matched. */ +  int name_value_match; +  /* pb_index is the absolute index of matched post-based dynamic +     table entry.  -1 if no such entry exists. */ +  nghttp3_ssize pb_index; +} nghttp3_qpack_lookup_result; + +/* + * nghttp3_qpack_lookup_stable searches |nv| in static table.  |token| + * is a token of nv->name and it is -1 if there is no corresponding + * token defined.  |indexing_mode| provides indexing strategy. + */ +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, +                            nghttp3_qpack_indexing_mode indexing_mode); + +/* + * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table. + * |token| is a token of nv->name and it is -1 if there is no + * corresponding token defined.  |hash| is a hash of nv->name. + * |indexing_mode| provides indexing strategy.  |krcnt| is Known + * Received Count.  |allow_blocking| is nonzero if this stream can be + * blocked (or it has been blocked already). + */ +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( +  nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, +  uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, +  int allow_blocking); + +/* + * nghttp3_qpack_encoder_write_field_section_prefix writes Encoded + * Field Section Prefix into |pbuf|.  |ricnt| is Required Insert + * Count.  |base| is Base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_field_section_prefix( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, +  uint64_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header + * Field to |rbuf|.  |absidx| is an absolute index into static table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *rbuf, +                                               uint64_t absidx); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header + * Field to |rbuf|.  |absidx| is an absolute index into dynamic table. + * |base| is base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, +                                                nghttp3_buf *rbuf, +                                                uint64_t absidx, uint64_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Literal Header + * Field With Name Reference to |rbuf|.  |absidx| is an absolute index + * into static table to reference a name.  |nv| is a header field to + * encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed_name( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, +  const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header + * Field With Name Reference to |rbuf|.  |absidx| is an absolute index + * into dynamic table to reference a name.  |base| is a base.  |nv| is + * a header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed_name( +  nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, +  uint64_t base, const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_literal writes Literal Header Field + * With Literal Name to |rbuf|.  |nv| is a header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, +                                        nghttp3_buf *rbuf, +                                        const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_static_insert writes Insert With Name + * Reference to |ebuf|.  |absidx| is an absolute index into static + * table to reference a name.  |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, +                                              nghttp3_buf *ebuf, +                                              uint64_t absidx, +                                              const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name + * Reference to |ebuf|.  |absidx| is an absolute index into dynamic + * table to reference a name.  |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *ebuf, +                                               uint64_t absidx, +                                               const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to + * |ebuf|.  |absidx| is an absolute index into dynamic table to + * reference an entry. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, +                                                 nghttp3_buf *ebuf, +                                                 uint64_t absidx); + +/* + * nghttp3_qpack_encoder_write_literal_insert writes Insert With + * Literal Name to |ebuf|.  |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *ebuf, +                                               const nghttp3_nv *nv); + +int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, +                                            nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_block_stream blocks |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, +                                       nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock_stream unblocks |stream|. + */ +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, +                                          nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less + * than or equal to |max_cnt|. + */ +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, +                                   uint64_t max_cnt); + +/* + * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is + * |stream_id|.  This function returns NULL if there is no such + * stream. + */ +nghttp3_qpack_stream * +nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, +                                  int64_t stream_id); + +uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that + * the dynamic table size is less than or equal to maximum size. + */ +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_process_dtable_update processes pending + * dynamic table size update.  It might write encoder stream into + * |ebuf|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, +                                                nghttp3_buf *ebuf); + +/* + * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table + * Capacity. to |ebuf|.  |cap| is the capacity of dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, +                                               nghttp3_buf *ebuf, size_t cap); + +/* + * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table.  If + * |ctx| is a part of encoder, |dtable_map| is not NULL.  |hash| is a + * hash value of name. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, +                                     nghttp3_qpack_nv *qnv, +                                     nghttp3_qpack_map *dtable_map, +                                     uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table + * by referencing static table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, +                                            uint64_t absidx, +                                            const nghttp3_nv *nv, +                                            uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table + * by referencing dynamic table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, +                                             uint64_t absidx, +                                             const nghttp3_nv *nv, +                                             uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table + * entry at an absolute index |absidx|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, +                                               uint64_t absidx); + +/* + * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic + * table.  |token| is a token of name and is -1 if it has no token + * value defined.  |hash| is a hash of name. + * + * NGHTTP3_ERR_NOMEM Out of memory. + */ +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, +                                             const nghttp3_nv *nv, +                                             int32_t token, uint32_t hash); + +/* + * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header + * block for a stream denoted by |stream_id| was acknowledged by + * decoder. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + *     Section Acknowledgement for a stream denoted by |stream_id| is + *     unexpected. + */ +int nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, +                                     int64_t stream_id); + +/* + * `nghttp3_qpack_encoder_add_icnt` increments known received count of + * |encoder| by |n|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + *     Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + *     |n| is too large. + */ +int nghttp3_qpack_encoder_add_icnt(nghttp3_qpack_encoder *encoder, uint64_t n); + +/* + * `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()`. + */ +void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, +                                         int64_t stream_id); + +/* + * nghttp3_qpack_context_dtable_get returns dynamic table entry whose + * absolute index is |absidx|.  This function assumes that such entry + * exists. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx); + +/* + * nghttp3_qpack_context_dtable_top returns latest dynamic table + * entry.  This function assumes dynamic table is not empty. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx); + +/* + * nghttp3_qpack_entry_init initializes |ent|.  |qnv| is a header + * field.  |sum| is the sum of table space occupied by all entries + * inserted so far.  It does not include this entry.  |absidx| is an + * absolute index of this entry.  |hash| is a hash of header field + * name.  This function increases reference count of qnv->nv.name and + * qnv->nv.value. + */ +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, +                              size_t sum, uint64_t absidx, uint32_t hash); + +/* + * nghttp3_qpack_entry_free frees memory allocated for |ent|. + */ +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent); + +/* + * nghttp3_qpack_put_varint_len returns the required number of bytes + * to encode |n| with |prefix| bits. + */ +size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix); + +/* + * nghttp3_qpack_put_varint encodes |n| using variable integer + * encoding with |prefix| bits into |buf|.  This function assumes the + * buffer pointed by |buf| has enough space.  This function returns + * the one byte beyond the last write (buf + + * nghttp3_qpack_put_varint_len(n, prefix)). + */ +uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix); + +/* nghttp3_qpack_encoder_stream_state is a set of states for encoder +   stream decoding. */ +typedef enum nghttp3_qpack_encoder_stream_state { +  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_encoder_stream_opcode { +  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_request_stream_state { +  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_request_stream_opcode { +  NGHTTP3_QPACK_RS_OPCODE_INDEXED, +  NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB, +  NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME, +  NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB, +  NGHTTP3_QPACK_RS_OPCODE_LITERAL, +} nghttp3_qpack_request_stream_opcode; + +struct nghttp3_qpack_decoder { +  nghttp3_qpack_context ctx; +  /* state is a current state of reading encoder stream. */ +  nghttp3_qpack_encoder_stream_state state; +  /* opcode is an encoder stream opcode being processed. */ +  nghttp3_qpack_encoder_stream_opcode opcode; +  /* rstate is a set of intermediate state which are used to process +     encoder stream. */ +  nghttp3_qpack_read_state rstate; +  /* dbuf is decoder stream. */ +  nghttp3_buf dbuf; +  /* written_icnt is Insert Count written to decoder stream so far. */ +  uint64_t written_icnt; +  /* max_concurrent_streams is the number of concurrent streams that a +     remote endpoint can open, including both bidirectional and +     unidirectional streams which potentially receives QPACK encoded +     HEADER frame. */ +  size_t max_concurrent_streams; +  /* uninterrupted_encoderlen is the number of bytes read from encoder +     stream without completing a single field section. */ +  size_t uninterrupted_encoderlen; +}; + +/* + * nghttp3_qpack_decoder_init initializes |decoder|. + * |hard_max_dtable_capacity| is the upper bound of the dynamic table + * capacity.  |max_blocked_streams| is the maximum number of stream + * which can be blocked.  |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, +                               size_t hard_max_dtable_capacity, +                               size_t max_blocked_streams, +                               const nghttp3_mem *mem); + +/* + * 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 With Literal Name 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 { +  /* rstate is a set of intermediate state which are used to process +     request stream. */ +  nghttp3_qpack_read_state rstate; +  const nghttp3_mem *mem; +  int64_t stream_id; +  /* ricnt is Required Insert Count to decode this header block. */ +  uint64_t ricnt; +  /* base is Base in Header Block Prefix. */ +  uint64_t base; +  /* state is a current state of reading request stream. */ +  nghttp3_qpack_request_stream_state state; +  /* opcode is a request stream opcode being processed. */ +  nghttp3_qpack_request_stream_opcode opcode; +  /* 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); + +/* + * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required + * Insert Count from the encoded form |encricnt| and stores Required + * Insert Count in |*dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + *     Unable to reconstruct Required Insert Count. + */ +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, +                                            uint64_t *dest, uint64_t encricnt); + +/* + * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left + * received in encoder stream to absolute index and stores it in + * rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + *     Relative index is invalid. + */ +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, +                                  nghttp3_qpack_read_state *rstate); + +/* + * nghttp3_qpack_decoder_brel2abs converts Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + *     Base relative index is invalid. + */ +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, +                                   nghttp3_qpack_stream_context *sctx); + +/* + * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + *     Post-Base relative index is invalid. + */ +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, +                                    nghttp3_qpack_stream_context *sctx); + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, +                                        nghttp3_qpack_stream_context *sctx, +                                        nghttp3_qpack_nv *nv); + +int nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, +                                            nghttp3_qpack_stream_context *sctx, +                                            nghttp3_qpack_nv *nv); + +void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, +                                        nghttp3_qpack_stream_context *sctx, +                                        nghttp3_qpack_nv *nv); + +/* + * nghttp3_qpack_decoder_write_section_ack writes Section + * Acknowledgement to decoder stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + * NGHTTP3_ERR_QPACK_FATAL + *     Decoder stream overflow. + */ +int nghttp3_qpack_decoder_write_section_ack( +  nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx); + +#endif /* !defined(NGHTTP3_QPACK_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman.c b/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman.c new file mode 100644 index 00000000000..3398f3f5080 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman.c @@ -0,0 +1,124 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_qpack_huffman.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_conv.h" + +size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) { +  size_t i; +  size_t nbits = 0; + +  for (i = 0; i < len; ++i) { +    nbits += huffman_sym_table[src[i]].nbits; +  } +  /* pad the prefix of EOS (256) */ +  return (nbits + 7) / 8; +} + +uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, +                                      size_t srclen) { +  const nghttp3_qpack_huffman_sym *sym; +  const uint8_t *end = src + srclen; +  uint64_t code = 0; +  size_t nbits = 0; +  uint32_t x; + +  for (; src != end;) { +    sym = &huffman_sym_table[*src++]; +    code |= (uint64_t)sym->code << (32 - nbits); +    nbits += sym->nbits; +    if (nbits < 32) { +      continue; +    } +    x = htonl((uint32_t)(code >> 32)); +    memcpy(dest, &x, 4); +    dest += 4; +    code <<= 32; +    nbits -= 32; +  } + +  for (; nbits >= 8;) { +    *dest++ = (uint8_t)(code >> 56); +    code <<= 8; +    nbits -= 8; +  } + +  if (nbits) { +    *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)); +  } + +  return dest; +} + +void nghttp3_qpack_huffman_decode_context_init( +  nghttp3_qpack_huffman_decode_context *ctx) { +  ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED; +} + +nghttp3_ssize +nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, +                             uint8_t *dest, const uint8_t *src, size_t srclen, +                             int fin) { +  uint8_t *p = dest; +  const uint8_t *end = src + srclen; +  nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; +  const nghttp3_qpack_huffman_decode_node *t = &node; +  uint8_t c; + +  /* We use the decoding algorithm described in +      - http://graphics.ics.uci.edu/pub/Prefix.pdf [!!! NO LONGER VALID !!!] +      - https://ics.uci.edu/~dan/pubs/Prefix.pdf +      - https://github.com/nghttp2/nghttp2/files/15141264/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/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman.h b/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman.h new file mode 100644 index 00000000000..ab6d82a16a9 --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +typedef struct nghttp3_qpack_huffman_sym { +  /* 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 nghttp3_qpack_huffman_decode_flag { +  /* 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 nghttp3_qpack_huffman_decode_node { +  /* 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 nghttp3_qpack_huffman_decode_context { +  /* 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 /* !defined(NGHTTP3_QPACK_HUFFMAN_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman_data.c b/contrib/libs/nghttp3/lib/nghttp3_qpack_huffman_data.c new file mode 100644 index 00000000000..7c3c230f041 --- /dev/null +++ b/contrib/libs/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/contrib/libs/nghttp3/lib/nghttp3_range.c b/contrib/libs/nghttp3/lib/nghttp3_range.c new file mode 100644 index 00000000000..af810a2c592 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_range.c @@ -0,0 +1,64 @@ +/* + * 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_uint64(a->begin, b->begin); +  uint64_t end = nghttp3_min_uint64(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/contrib/libs/nghttp3/lib/nghttp3_range.h b/contrib/libs/nghttp3/lib/nghttp3_range.h new file mode 100644 index 00000000000..e52e1966b87 --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +/* + * nghttp3_range represents half-closed range [begin, end). + */ +typedef struct nghttp3_range { +  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 /* !defined(NGHTTP3_RANGE_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_rcbuf.c b/contrib/libs/nghttp3/lib/nghttp3_rcbuf.c new file mode 100644 index 00000000000..9e9dab51390 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_rcbuf.c @@ -0,0 +1,108 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_rcbuf.h" + +#include <assert.h> + +#include "nghttp3_mem.h" +#include "nghttp3_str.h" + +int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, +                      const nghttp3_mem *mem) { +  uint8_t *p; + +  p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size); +  if (p == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  *rcbuf_ptr = (void *)p; + +  (*rcbuf_ptr)->mem = mem; +  (*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_free(rcbuf->mem, rcbuf); +} + +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/contrib/libs/nghttp3/lib/nghttp3_rcbuf.h b/contrib/libs/nghttp3/lib/nghttp3_rcbuf.h new file mode 100644 index 00000000000..97f83234ab5 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_rcbuf.h @@ -0,0 +1,81 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_RCBUF_H +#define NGHTTP3_RCBUF_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +struct nghttp3_rcbuf { +  /* mem is the memory allocator that allocates memory for this +     object. */ +  const nghttp3_mem *mem; +  /* 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 /* !defined(NGHTTP3_RCBUF_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_ringbuf.c b/contrib/libs/nghttp3/lib/nghttp3_ringbuf.c new file mode 100644 index 00000000000..7d3ab39bf82 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_ringbuf.c @@ -0,0 +1,154 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_ringbuf.h" + +#include <assert.h> +#include <string.h> +#ifdef WIN32 +#  include <intrin.h> +#endif /* defined(WIN32) */ + +#include "nghttp3_macro.h" + +static int ispow2(size_t n) { +#if defined(_MSC_VER) && !defined(__clang__) &&                                \ +  (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941)) +  return n && !(n & (n - 1)); +#elif defined(WIN32) +  return 1 == __popcnt((unsigned int)n); +#else  /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) ||   \ +          (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +  return 1 == __builtin_popcount((unsigned int)n); +#endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) ||   \ +          (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +} + +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, +                         const nghttp3_mem *mem) { +  if (nmemb) { +    assert(ispow2(nmemb)); + +    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_size(rb->nmemb, rb->len + 1); + +  return (void *)&rb->buf[rb->first * rb->size]; +} + +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) { +  size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); + +  if (rb->len == rb->nmemb) { +    rb->first = (rb->first + 1) & (rb->nmemb - 1); +  } else { +    ++rb->len; +  } + +  return (void *)&rb->buf[offset * rb->size]; +} + +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) { +  rb->first = (rb->first + 1) & (rb->nmemb - 1); +  --rb->len; +} + +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) { +  assert(rb->len); +  --rb->len; +} + +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) { +  assert(len <= rb->nmemb); +  rb->len = len; +} + +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) { +  assert(offset < rb->len); +  offset = (rb->first + offset) & (rb->nmemb - 1); +  return &rb->buf[offset * rb->size]; +} + +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; } + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) { +  uint8_t *buf; + +  if (rb->nmemb >= nmemb) { +    return 0; +  } + +  assert(ispow2(nmemb)); + +  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/contrib/libs/nghttp3/lib/nghttp3_ringbuf.h b/contrib/libs/nghttp3/lib/nghttp3_ringbuf.h new file mode 100644 index 00000000000..b154290a51d --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +typedef struct nghttp3_ringbuf { +  /* buf points to the underlying buffer. */ +  uint8_t *buf; +  const nghttp3_mem *mem; +  /* nmemb is the number of elements that can be stored in this ring +     buffer. */ +  size_t nmemb; +  /* size is the size of each element. */ +  size_t size; +  /* first is the offset to the first element. */ +  size_t first; +  /* len is the number of elements actually stored. */ +  size_t len; +} nghttp3_ringbuf; + +/* + * nghttp3_ringbuf_init initializes |rb|.  |nmemb| is the number of + * elements that can be stored in this buffer.  |size| is the size of + * each element.  |size| must be power of 2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + *     Out of memory. + */ +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, +                         const nghttp3_mem *mem); + +/* + * nghttp3_ringbuf_free frees resources allocated for |rb|.  This + * function does not free the memory pointed by |rb|. + */ +void nghttp3_ringbuf_free(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_front moves the offset to the first element in +   the buffer backward, and returns the pointer to the element. +   Caller can store data to the buffer pointed by the returned +   pointer.  If this action exceeds the capacity of the ring buffer, +   the last element is silently overwritten, and rb->len remains +   unchanged. */ +void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_back moves the offset to the last element in +   the buffer forward, and returns the pointer to the element.  Caller +   can store data to the buffer pointed by the returned pointer.  If +   this action exceeds the capacity of the ring buffer, the first +   element is silently overwritten, and rb->len remains unchanged. */ +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_front removes first element in |rb|. + */ +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_back removes the last element in |rb|. + */ +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_resize changes the number of elements stored.  This +   does not change the capacity of the underlying buffer. */ +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); + +/* nghttp3_ringbuf_get returns the pointer to the element at +   |offset|. */ +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset); + +/* nghttp3_ringbuf_len returns the number of elements stored. */ +#define nghttp3_ringbuf_len(RB) ((RB)->len) + +/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb); + +#endif /* !defined(NGHTTP3_RINGBUF_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_str.c b/contrib/libs/nghttp3/lib/nghttp3_str.c new file mode 100644 index 00000000000..fc131404d13 --- /dev/null +++ b/contrib/libs/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 <string.h> +#include <assert.h> + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) { +  memcpy(dest, src, n); +  return dest + n; +} + +/* Generated by gendowncasetbl.py */ +static const uint8_t DOWNCASE_TBL[] = { +  0 /* NUL  */,   1 /* SOH  */,   2 /* STX  */,   3 /* ETX  */, +  4 /* EOT  */,   5 /* ENQ  */,   6 /* ACK  */,   7 /* BEL  */, +  8 /* BS   */,   9 /* HT   */,   10 /* LF   */,  11 /* VT   */, +  12 /* FF   */,  13 /* CR   */,  14 /* SO   */,  15 /* SI   */, +  16 /* DLE  */,  17 /* DC1  */,  18 /* DC2  */,  19 /* DC3  */, +  20 /* DC4  */,  21 /* NAK  */,  22 /* SYN  */,  23 /* ETB  */, +  24 /* CAN  */,  25 /* EM   */,  26 /* SUB  */,  27 /* ESC  */, +  28 /* FS   */,  29 /* GS   */,  30 /* RS   */,  31 /* US   */, +  32 /* SPC  */,  33 /* !    */,  34 /* "    */,  35 /* #    */, +  36 /* $    */,  37 /* %    */,  38 /* &    */,  39 /* '    */, +  40 /* (    */,  41 /* )    */,  42 /* *    */,  43 /* +    */, +  44 /* ,    */,  45 /* -    */,  46 /* .    */,  47 /* /    */, +  48 /* 0    */,  49 /* 1    */,  50 /* 2    */,  51 /* 3    */, +  52 /* 4    */,  53 /* 5    */,  54 /* 6    */,  55 /* 7    */, +  56 /* 8    */,  57 /* 9    */,  58 /* :    */,  59 /* ;    */, +  60 /* <    */,  61 /* =    */,  62 /* >    */,  63 /* ?    */, +  64 /* @    */,  97 /* A    */,  98 /* B    */,  99 /* C    */, +  100 /* D    */, 101 /* E    */, 102 /* F    */, 103 /* G    */, +  104 /* H    */, 105 /* I    */, 106 /* J    */, 107 /* K    */, +  108 /* L    */, 109 /* M    */, 110 /* N    */, 111 /* O    */, +  112 /* P    */, 113 /* Q    */, 114 /* R    */, 115 /* S    */, +  116 /* T    */, 117 /* U    */, 118 /* V    */, 119 /* W    */, +  120 /* X    */, 121 /* Y    */, 122 /* Z    */, 91 /* [    */, +  92 /* \    */,  93 /* ]    */,  94 /* ^    */,  95 /* _    */, +  96 /* `    */,  97 /* a    */,  98 /* b    */,  99 /* c    */, +  100 /* d    */, 101 /* e    */, 102 /* f    */, 103 /* g    */, +  104 /* h    */, 105 /* i    */, 106 /* j    */, 107 /* k    */, +  108 /* l    */, 109 /* m    */, 110 /* n    */, 111 /* o    */, +  112 /* p    */, 113 /* q    */, 114 /* r    */, 115 /* s    */, +  116 /* t    */, 117 /* u    */, 118 /* v    */, 119 /* w    */, +  120 /* x    */, 121 /* y    */, 122 /* z    */, 123 /* {    */, +  124 /* |    */, 125 /* }    */, 126 /* ~    */, 127 /* DEL  */, +  128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, +  132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, +  136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, +  140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, +  144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, +  148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, +  152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, +  156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, +  160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, +  164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, +  168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, +  172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, +  176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, +  180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, +  184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, +  188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, +  192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, +  196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, +  200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, +  204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, +  208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, +  212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, +  216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, +  220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, +  224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, +  228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, +  232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, +  236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, +  240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, +  244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, +  248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, +  252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp3_downcase(uint8_t *s, size_t len) { +  size_t i; +  for (i = 0; i < len; ++i) { +    s[i] = DOWNCASE_TBL[s[i]]; +  } +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_str.h b/contrib/libs/nghttp3/lib/nghttp3_str.h new file mode 100644 index 00000000000..280749a3a9a --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n); + +void nghttp3_downcase(uint8_t *s, size_t len); + +#endif /* !defined(NGHTTP3_STR_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_stream.c b/contrib/libs/nghttp3/lib/nghttp3_stream.c new file mode 100644 index 00000000000..328cddd488f --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_stream.c @@ -0,0 +1,1247 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_stream.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_conv.h" +#include "nghttp3_macro.h" +#include "nghttp3_frame.h" +#include "nghttp3_conn.h" +#include "nghttp3_str.h" +#include "nghttp3_http.h" +#include "nghttp3_vec.h" +#include "nghttp3_unreachable.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 + +nghttp3_objalloc_def(stream, nghttp3_stream, oplent); + +int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, +                       const nghttp3_stream_callbacks *callbacks, +                       nghttp3_objalloc *out_chunk_objalloc, +                       nghttp3_objalloc *stream_objalloc, +                       const nghttp3_mem *mem) { +  nghttp3_stream *stream = nghttp3_objalloc_stream_get(stream_objalloc); + +  if (stream == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  memset(stream, 0, sizeof(*stream)); + +  stream->out_chunk_objalloc = out_chunk_objalloc; +  stream->stream_objalloc = stream_objalloc; + +  nghttp3_tnode_init(&stream->node, stream_id); + +  nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); +  nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); +  nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); +  nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); + +  nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); + +  stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; +  stream->mem = mem; +  stream->rx.http.status_code = -1; +  stream->rx.http.content_length = -1; +  stream->rx.http.pri.urgency = NGHTTP3_DEFAULT_URGENCY; +  stream->error_code = NGHTTP3_H3_NO_ERROR; + +  if (callbacks) { +    stream->callbacks = *callbacks; +  } + +  *pstream = stream; + +  return 0; +} + +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_out_chunks(nghttp3_ringbuf *chunks, +                              nghttp3_objalloc *out_chunk_objalloc, +                              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); + +    if (nghttp3_buf_cap(buf) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { +      nghttp3_objalloc_chunk_release(out_chunk_objalloc, +                                     (nghttp3_chunk *)(void *)buf->begin); +      continue; +    } + +    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_PRIORITY_UPDATE: +      nghttp3_frame_priority_update_free(&frent->fr.priority_update, 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_out_chunks(&stream->chunks, stream->out_chunk_objalloc, stream->mem); +  delete_frq(&stream->frq, stream->mem); +  nghttp3_tnode_free(&stream->node); + +  nghttp3_objalloc_stream_release(stream->stream_objalloc, 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 *begin, const uint8_t *end, +                                  int fin) { +  const uint8_t *orig_begin = begin; +  size_t len; + +  assert(begin != end); + +  if (rvint->left == 0) { +    assert(rvint->acc == 0); + +    len = nghttp3_get_varintlen(begin); +    if (len <= (size_t)(end - begin)) { +      nghttp3_get_varint(&rvint->acc, begin); +      return (nghttp3_ssize)len; +    } + +    if (fin) { +      return NGHTTP3_ERR_INVALID_ARGUMENT; +    } + +    rvint->acc = nghttp3_get_varint_fb(begin++); +    rvint->left = len - 1; +  } + +  len = nghttp3_min_size(rvint->left, (size_t)(end - begin)); +  end = begin + len; + +  for (; begin != end;) { +    rvint->acc = (rvint->acc << 8) + *begin++; +  } + +  rvint->left -= len; + +  if (fin && rvint->left) { +    return NGHTTP3_ERR_INVALID_ARGUMENT; +  } + +  return (nghttp3_ssize)(begin - orig_begin); +} + +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_size(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) && +         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_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_GOAWAY: +      rv = nghttp3_stream_write_goaway(stream, frent); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGHTTP3_FRAME_PRIORITY_UPDATE: +      rv = nghttp3_stream_write_priority_update(stream, frent); +      if (rv != 0) { +        return rv; +      } +      nghttp3_frame_priority_update_free(&frent->fr.priority_update, +                                         stream->mem); +      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_varintlen((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_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_settings *local_settings = frent->aux.settings.local_settings; + +  fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS; +  fr.settings.niv = 3; +  iv = &fr.settings.iv[0]; + +  iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; +  iv[0].value = local_settings->max_field_section_size; +  iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; +  iv[1].value = local_settings->qpack_max_dtable_capacity; +  iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; +  iv[2].value = local_settings->qpack_blocked_streams; + +  if (local_settings->h3_datagram) { +    iv[fr.settings.niv].id = NGHTTP3_SETTINGS_ID_H3_DATAGRAM; +    iv[fr.settings.niv].value = 1; + +    ++fr.settings.niv; +  } + +  if (local_settings->enable_connect_protocol) { +    iv[fr.settings.niv].id = NGHTTP3_SETTINGS_ID_ENABLE_CONNECT_PROTOCOL; +    iv[fr.settings.niv].value = 1; + +    ++fr.settings.niv; +  } + +  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_goaway(nghttp3_stream *stream, +                                nghttp3_frame_entry *frent) { +  nghttp3_frame_goaway *fr = &frent->fr.goaway; +  size_t len; +  int rv; +  nghttp3_buf *chunk; +  nghttp3_typed_buf tbuf; + +  len = nghttp3_frame_write_goaway_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_goaway(chunk->last, fr); + +  tbuf.buf.last = chunk->last; + +  return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_priority_update(nghttp3_stream *stream, +                                         nghttp3_frame_entry *frent) { +  nghttp3_frame_priority_update *fr = &frent->fr.priority_update; +  size_t len; +  int rv; +  nghttp3_buf *chunk; +  nghttp3_typed_buf tbuf; + +  len = nghttp3_frame_write_priority_update_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_priority_update(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, 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, 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; +  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.id, +                                    nva, nvlen); +  if (rv != 0) { +    goto fail; +  } + +  pbuflen = nghttp3_buf_len(&pbuf); +  rbuflen = nghttp3_buf_len(rbuf); +  ebuflen = nghttp3_buf_len(ebuf); + +  hd.type = frame_type; +  hd.length = (int64_t)(pbuflen + rbuflen); + +  len = nghttp3_frame_write_hd_len(&hd) + 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); + +  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; +  int64_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.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_varint(vec, (size_t)sveccnt); +  if (datalen == -1) { +    return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; +  } + +  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 = 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_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); +  size_t buflen = nghttp3_buf_len(&tbuf->buf); + +  if (buflen > NGHTTP3_MAX_VARINT - stream->tx.offset) { +    return NGHTTP3_ERR_STREAM_DATA_OVERFLOW; +  } + +  stream->tx.offset += buflen; +  stream->unsent_bytes += buflen; + +  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_size(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) +    ; + +  if (n == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { +    p = +      (uint8_t *)nghttp3_objalloc_chunk_len_get(stream->out_chunk_objalloc, n); +  } else { +    p = nghttp3_mem_malloc(stream->mem, n); +  } +  if (p == NULL) { +    return NGHTTP3_ERR_NOMEM; +  } + +  if (nghttp3_ringbuf_full(chunks)) { +    size_t nlen = nghttp3_max_size(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_SHUT_WR) || +         (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED); +} + +int nghttp3_stream_require_schedule(nghttp3_stream *stream) { +  return (!nghttp3_stream_outq_write_done(stream) && +          !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) && +          !(stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR)) || +         (nghttp3_ringbuf_len(&stream->frq) && +          !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); +} + +size_t 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 = stream->outq_idx; +  uint64_t offset = stream->outq_offset; +  size_t buflen; +  nghttp3_vec *vbegin = vec, *vend = vec + veccnt; +  nghttp3_typed_buf *tbuf; + +  assert(veccnt > 0); + +  if (i < len) { +    tbuf = nghttp3_ringbuf_get(outq, i); +    buflen = nghttp3_buf_len(&tbuf->buf); + +    if (offset < buflen) { +      vec->base = tbuf->buf.pos + offset; +      vec->len = (size_t)(buflen - offset); +      ++vec; +    } else { +      /* This is the only case that satisfies offset >= buflen */ +      assert(0 == offset); +      assert(0 == buflen); +    } + +    ++i; + +    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 (size_t)(vec - vbegin); +} + +void nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { +  nghttp3_ringbuf *outq = &stream->outq; +  size_t i; +  size_t len = nghttp3_ringbuf_len(outq); +  uint64_t offset = stream->outq_offset + n; +  size_t buflen; +  nghttp3_typed_buf *tbuf; + +  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; +} + +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 void 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; +  case NGHTTP3_BUF_TYPE_SHARED: +    assert(nghttp3_ringbuf_len(chunks)); + +    chunk = nghttp3_ringbuf_get(chunks, 0); + +    assert(chunk->begin == tbuf->buf.begin); +    assert(chunk->end == tbuf->buf.end); + +    if (chunk->last == tbuf->buf.last) { +      if (nghttp3_buf_cap(chunk) == NGHTTP3_STREAM_MIN_CHUNK_SIZE) { +        nghttp3_objalloc_chunk_release(stream->out_chunk_objalloc, +                                       (nghttp3_chunk *)(void *)chunk->begin); +      } else { +        nghttp3_buf_free(chunk, stream->mem); +      } +      nghttp3_ringbuf_pop_front(chunks); +    } +    break; +  default: +    nghttp3_unreachable(); +  }; + +  nghttp3_ringbuf_pop_front(&stream->outq); +} + +int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { +  nghttp3_ringbuf *outq = &stream->outq; +  size_t buflen; +  size_t npopped = 0; +  uint64_t nack; +  nghttp3_typed_buf *tbuf; +  int rv; + +  for (; nghttp3_ringbuf_len(outq);) { +    tbuf = nghttp3_ringbuf_get(outq, 0); +    buflen = nghttp3_buf_len(&tbuf->buf); + +    /* For NGHTTP3_BUF_TYPE_ALIEN, we never add 0 length buffer. */ +    if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN && stream->ack_offset < offset && +        stream->callbacks.acked_data) { +      nack = nghttp3_min_uint64(offset, stream->ack_base + buflen) - +             stream->ack_offset; + +      rv = stream->callbacks.acked_data(stream, stream->node.id, nack, +                                        stream->user_data); +      if (rv != 0) { +        return NGHTTP3_ERR_CALLBACK_FAILURE; +      } +    } + +    if (offset >= stream->ack_base + buflen) { +      stream_pop_outq_entry(stream, tbuf); + +      stream->ack_base += buflen; +      stream->ack_offset = stream->ack_base; +      ++npopped; + +      if (stream->outq_idx + 1 == npopped) { +        stream->outq_offset = 0; +        break; +      } + +      continue; +    } + +    break; +  } + +  assert(stream->outq_idx + 1 >= npopped); +  if (stream->outq_idx >= npopped) { +    stream->outq_idx -= npopped; +  } else { +    stream->outq_idx = 0; +  } + +  stream->ack_offset = offset; + +  return 0; +} + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data, +                               size_t datalen) { +  nghttp3_ringbuf *inq = &stream->inq; +  size_t len = nghttp3_ringbuf_len(inq); +  nghttp3_buf *buf; +  size_t nwrite; +  uint8_t *rawbuf; +  size_t bufleft; +  int rv; + +  if (len) { +    buf = nghttp3_ringbuf_get(inq, len - 1); +    bufleft = nghttp3_buf_left(buf); +    nwrite = nghttp3_min_size(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_size(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_size(datalen, bufleft); +    buf->last = nghttp3_cpymem(buf->last, data, nwrite); +    data += nwrite; +    datalen -= nwrite; +  } + +  return 0; +} + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) { +  nghttp3_ringbuf *inq = &stream->inq; +  size_t len = nghttp3_ringbuf_len(inq); +  size_t i, n = 0; +  nghttp3_buf *buf; + +  for (i = 0; i < len; ++i) { +    buf = nghttp3_ringbuf_get(inq, i); +    n += nghttp3_buf_len(buf); +  } + +  return n; +} + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, +                                         nghttp3_stream_http_event event) { +  int rv; + +  switch (stream->rx.hstate) { +  case NGHTTP3_HTTP_STATE_NONE: +    return NGHTTP3_ERR_H3_INTERNAL_ERROR; +  case NGHTTP3_HTTP_STATE_REQ_INITIAL: +    switch (event) { +    case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: +      stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; +      return 0; +    default: +      return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; +    } +  case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: +    if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { +      return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; +    } +    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; +      } +      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: +      rv = nghttp3_http_on_remote_end_stream(stream); +      if (rv != 0) { +        return rv; +      } +      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_MALFORMED_HTTP_MESSAGING; +    } +    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; +      } +      stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; +      return 0; +    case NGHTTP3_HTTP_EVENT_MSG_END: +      rv = nghttp3_http_on_remote_end_stream(stream); +      if (rv != 0) { +        return rv; +      } +      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_MALFORMED_HTTP_MESSAGING; +    } +    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; +    } +    rv = nghttp3_http_on_remote_end_stream(stream); +    if (rv != 0) { +      return rv; +    } +    stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; +    return 0; +  case NGHTTP3_HTTP_STATE_REQ_END: +    return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; +  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_MALFORMED_HTTP_MESSAGING; +    } +    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; +      } +      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: +      rv = nghttp3_http_on_remote_end_stream(stream); +      if (rv != 0) { +        return rv; +      } +      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_MALFORMED_HTTP_MESSAGING; +    } +    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; +      } +      stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; +      return 0; +    case NGHTTP3_HTTP_EVENT_MSG_END: +      rv = nghttp3_http_on_remote_end_stream(stream); +      if (rv != 0) { +        return rv; +      } +      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_MALFORMED_HTTP_MESSAGING; +    } +    stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END; +    return 0; +  case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END: +    if (event != NGHTTP3_HTTP_EVENT_MSG_END) { +      return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; +    } +    rv = nghttp3_http_on_remote_end_stream(stream); +    if (rv != 0) { +      return rv; +    } +    stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; +    return 0; +  case NGHTTP3_HTTP_STATE_RESP_END: +    return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; +  default: +    nghttp3_unreachable(); +  } +} + +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_MALFORMED_HTTP_MESSAGING; +  } +} + +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/contrib/libs/nghttp3/lib/nghttp3_stream.h b/contrib/libs/nghttp3/lib/nghttp3_stream.h new file mode 100644 index 00000000000..7d296febf91 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_stream.h @@ -0,0 +1,397 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_STREAM_H +#define NGHTTP3_STREAM_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_map.h" +#include "nghttp3_tnode.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_frame.h" +#include "nghttp3_qpack.h" +#include "nghttp3_objalloc.h" + +#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 + +/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large +   enough to fill outgoing single QUIC packet. */ +#define NGHTTP3_MIN_UNSENT_BYTES 4096 + +/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause +   the stream to reschedule. */ +#define NGHTTP3_STREAM_MIN_WRITELEN 800 + +/* nghttp3_stream_type is unidirectional stream type. */ +typedef uint64_t nghttp3_stream_type; + +#define NGHTTP3_STREAM_TYPE_CONTROL 0x00 +#define NGHTTP3_STREAM_TYPE_PUSH 0x01 +#define NGHTTP3_STREAM_TYPE_QPACK_ENCODER 0x02 +#define NGHTTP3_STREAM_TYPE_QPACK_DECODER 0x03 +#define NGHTTP3_STREAM_TYPE_UNKNOWN UINT64_MAX + +typedef enum nghttp3_ctrl_stream_state { +  NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, +  NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, +  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_PRIORITY_UPDATE_PRI_ELEM_ID, +  NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE, +} nghttp3_ctrl_stream_state; + +typedef enum nghttp3_req_stream_state { +  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_IGN_FRAME, +  NGHTTP3_REQ_STREAM_STATE_IGN_REST, +} nghttp3_req_stream_state; + +typedef struct nghttp3_varint_read_state { +  int64_t acc; +  size_t left; +} nghttp3_varint_read_state; + +typedef struct nghttp3_stream_read_state { +  nghttp3_varint_read_state rvint; +  nghttp3_frame fr; +  int64_t left; +  int state; +} nghttp3_stream_read_state; + +/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_STREAM_FLAG_NONE 0x0000u +/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional +   stream type is identified. */ +#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001u +/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by +   QUIC flow control. */ +#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002u +/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is +   temporarily unable to provide data. */ +#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004u +/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application +   finished to feed outgoing data. */ +#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008u +/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is +   blocked due to QPACK decoding. */ +#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010u +/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent +   fin. */ +#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020u +/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. +   nghttp3_stream object can still alive because it might be blocked +   by QPACK decoder. */ +#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040u +/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write +   operation to a stream is prohibited. */ +#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100u +/* NGHTTP3_STREAM_FLAG_SHUT_RD indicates that a read-side stream is +   closed abruptly and any incoming and pending stream data is just +   discarded for a stream. */ +#define NGHTTP3_STREAM_FLAG_SHUT_RD 0x0200u +/* NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET indicates that server +   overrides stream priority. */ +#define NGHTTP3_STREAM_FLAG_SERVER_PRIORITY_SET 0x0400u +/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server +   received PRIORITY_UPDATE frame for this stream. */ +#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u +/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that +   NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while +   processing incoming HTTP fields. */ +#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u + +typedef enum nghttp3_stream_http_state { +  NGHTTP3_HTTP_STATE_NONE, +  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_stream_http_event { +  NGHTTP3_HTTP_EVENT_DATA_BEGIN, +  NGHTTP3_HTTP_EVENT_DATA_END, +  NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, +  NGHTTP3_HTTP_EVENT_HEADERS_END, +  NGHTTP3_HTTP_EVENT_MSG_END, +} nghttp3_stream_http_event; + +typedef struct nghttp3_stream nghttp3_stream; + +/* + * 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, uint64_t datalen, +                                         void *user_data); + +typedef struct nghttp3_stream_callbacks { +  nghttp3_stream_acked_data acked_data; +} nghttp3_stream_callbacks; + +typedef struct nghttp3_http_state { +  /* 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; +  nghttp3_pri pri; +  /* status_code is HTTP status code received.  This field is used +     if connection is initialized as client. */ +  int32_t status_code; +  uint32_t flags; +} nghttp3_http_state; + +struct nghttp3_stream { +  union { +    struct { +      const nghttp3_mem *mem; +      nghttp3_objalloc *out_chunk_objalloc; +      nghttp3_objalloc *stream_objalloc; +      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 stream. */ +      nghttp3_conn *conn; +      void *user_data; +      /* unsent_bytes is the number of bytes in outq not written yet */ +      uint64_t unsent_bytes; +      /* outq_idx is an index into outq where next write is made. */ +      size_t outq_idx; +      /* outq_offset is write offset relative to the element at outq_idx +         in outq. */ +      uint64_t outq_offset; +      /* ack_base is the number of bytes acknowledged by a remote +         endpoint where the first element in outq is positioned at. */ +      uint64_t ack_base; +      /* ack_offset is the number of bytes acknowledged by a remote +         endpoint so far. */ +      uint64_t ack_offset; +      uint64_t unscheduled_nwrite; +      nghttp3_stream_type type; +      nghttp3_stream_read_state rstate; +      /* error_code indicates the reason of closure of this stream. */ +      uint64_t error_code; + +      struct { +        uint64_t offset; +        nghttp3_stream_http_state hstate; +      } tx; + +      struct { +        nghttp3_stream_http_state hstate; +        nghttp3_http_state http; +      } rx; + +      uint16_t flags; +    }; + +    nghttp3_opl_entry oplent; +  }; +}; + +nghttp3_objalloc_decl(stream, nghttp3_stream, oplent); + +typedef struct nghttp3_frame_entry { +  nghttp3_frame fr; +  union { +    struct { +      nghttp3_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, +                       const nghttp3_stream_callbacks *callbacks, +                       nghttp3_objalloc *out_chunk_objalloc, +                       nghttp3_objalloc *stream_objalloc, +                       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 *begin, const uint8_t *end, +                                  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); + +size_t 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_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_header_block(nghttp3_stream *stream, +                                      nghttp3_qpack_encoder *qenc, +                                      nghttp3_stream *qenc_stream, +                                      nghttp3_buf *rbuf, nghttp3_buf *ebuf, +                                      int64_t frame_type, 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_goaway(nghttp3_stream *stream, +                                nghttp3_frame_entry *frent); + +int nghttp3_stream_write_priority_update(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); + +void 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); + +/* + * nghttp2_stream_update_ack_offset updates the last acknowledged + * offset to |offset|. + */ +int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset); + +/* + * nghttp3_stream_is_active returns nonzero if |stream| is active.  In + * other words, it has something to send.  This function does not take + * into account its descendants. + */ +int nghttp3_stream_is_active(nghttp3_stream *stream); + +/* + * nghttp3_stream_require_schedule returns nonzero if |stream| should + * be scheduled.  In other words, |stream| or its descendants have + * something to send. + */ +int nghttp3_stream_require_schedule(nghttp3_stream *stream); + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src, +                               size_t srclen); + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream); + +int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream); + +void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream); + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, +                                         nghttp3_stream_http_event event); + +int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); + +/* + * nghttp3_stream_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 /* !defined(NGHTTP3_STREAM_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_tnode.c b/contrib/libs/nghttp3/lib/nghttp3_tnode.c new file mode 100644 index 00000000000..eae847e7a92 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_tnode.c @@ -0,0 +1,95 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_tnode.h" + +#include <assert.h> + +#include "nghttp3_macro.h" +#include "nghttp3_stream.h" +#include "nghttp3_conn.h" +#include "nghttp3_conv.h" + +void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id) { +  tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; +  tnode->id = id; +  tnode->cycle = 0; +  tnode->pri.urgency = NGHTTP3_DEFAULT_URGENCY; +  tnode->pri.inc = 0; +} + +void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; } + +static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { +  assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX); + +  nghttp3_pq_remove(pq, &tnode->pe); +  tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; +} + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { +  if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { +    return; +  } + +  tnode_unschedule(tnode, pq); +} + +static uint64_t pq_get_first_cycle(nghttp3_pq *pq) { +  nghttp3_tnode *top; + +  if (nghttp3_pq_empty(pq)) { +    return 0; +  } + +  top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); +  return top->cycle; +} + +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, +                           uint64_t nwrite) { +  uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN; + +  if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { +    tnode->cycle = +      pq_get_first_cycle(pq) + +      ((nwrite == 0 || !tnode->pri.inc) ? 0 : nghttp3_max_uint64(1, penalty)); +  } else if (nwrite > 0) { +    if (!tnode->pri.inc || nghttp3_pq_size(pq) == 1) { +      return 0; +    } + +    nghttp3_pq_remove(pq, &tnode->pe); +    tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; +    tnode->cycle += nghttp3_max_uint64(1, penalty); +  } else { +    return 0; +  } + +  return nghttp3_pq_push(pq, &tnode->pe); +} + +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) { +  return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_tnode.h b/contrib/libs/nghttp3/lib/nghttp3_tnode.h new file mode 100644 index 00000000000..c13af52fdc6 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_tnode.h @@ -0,0 +1,66 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_TNODE_H +#define NGHTTP3_TNODE_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_pq.h" + +#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24) + +typedef struct nghttp3_tnode { +  nghttp3_pq_entry pe; +  size_t num_children; +  int64_t id; +  uint64_t cycle; +  /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ +  nghttp3_pri pri; +} nghttp3_tnode; + +void nghttp3_tnode_init(nghttp3_tnode *tnode, int64_t id); + +void nghttp3_tnode_free(nghttp3_tnode *tnode); + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq); + +/* + * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty. + * If |tnode| has already been scheduled, it is rescheduled by the + * amount of |nwrite|. + */ +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, +                           uint64_t nwrite); + +/* + * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. + */ +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode); + +#endif /* !defined(NGHTTP3_TNODE_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_unreachable.c b/contrib/libs/nghttp3/lib/nghttp3_unreachable.c new file mode 100644 index 00000000000..8adeeb4931d --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_unreachable.c @@ -0,0 +1,72 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_unreachable.h" + +#include <stdio.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#  include <unistd.h> +#endif /* defined(HAVE_UNISTD_H) */ +#include <stdlib.h> +#ifdef WIN32 +#  include <io.h> +#endif /* defined(WIN32) */ + +void nghttp3_unreachable_fail(const char *file, int line, const char *func) { +  char *buf; +  size_t buflen; +  int rv; + +#define NGHTTP3_UNREACHABLE_TEMPLATE "%s:%d %s: Unreachable.\n" + +  rv = snprintf(NULL, 0, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func); +  if (rv < 0) { +    abort(); +  } + +  /* here we explicitly use system malloc */ +  buflen = (size_t)rv + 1; +  buf = malloc(buflen); +  if (buf == NULL) { +    abort(); +  } + +  rv = snprintf(buf, buflen, NGHTTP3_UNREACHABLE_TEMPLATE, file, line, func); +  if (rv < 0) { +    abort(); +  } + +#ifndef WIN32 +  while (write(STDERR_FILENO, buf, (size_t)rv) == -1 && errno == EINTR) +    ; +#else  /* defined(WIN32) */ +  _write(_fileno(stderr), buf, (unsigned int)rv); +#endif /* defined(WIN32) */ + +  free(buf); + +  abort(); +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_unreachable.h b/contrib/libs/nghttp3/lib/nghttp3_unreachable.h new file mode 100644 index 00000000000..c609d7ed72f --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_unreachable.h @@ -0,0 +1,53 @@ +/* + * nghttp3 + * + * Copyright (c) 2022 nghttp3 contributors + * Copyright (c) 2022 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_UNREACHABLE_H +#define NGHTTP3_UNREACHABLE_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +#ifdef __FILE_NAME__ +#  define NGHTTP3_FILE_NAME __FILE_NAME__ +#else /* !defined(__FILE_NAME__) */ +#  define NGHTTP3_FILE_NAME "(file)" +#endif /* !defined(__FILE_NAME__) */ + +#define nghttp3_unreachable()                                                  \ +  nghttp3_unreachable_fail(NGHTTP3_FILE_NAME, __LINE__, __func__) + +#ifdef _MSC_VER +__declspec(noreturn) +#endif /* defined(_MSC_VER) */ +    void nghttp3_unreachable_fail(const char *file, int line, const char *func) +#ifndef _MSC_VER +        __attribute__((noreturn)) +#endif /* !defined(_MSC_VER) */ +        ; + +#endif /* !defined(NGHTTP3_UNREACHABLE_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_vec.c b/contrib/libs/nghttp3/lib/nghttp3_vec.c new file mode 100644 index 00000000000..ab58ff5832b --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_vec.c @@ -0,0 +1,55 @@ +/* + * 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" + +uint64_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { +  size_t i; +  uint64_t res = 0; + +  for (i = 0; i < n; ++i) { +    res += vec[i].len; +  } + +  return res; +} + +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n) { +  uint64_t res = 0; +  size_t len; +  size_t i; + +  for (i = 0; i < n; ++i) { +    len = vec[i].len; +    if (len > NGHTTP3_MAX_VARINT - res) { +      return -1; +    } + +    res += len; +  } + +  return (int64_t)res; +} diff --git a/contrib/libs/nghttp3/lib/nghttp3_vec.h b/contrib/libs/nghttp3/lib/nghttp3_vec.h new file mode 100644 index 00000000000..f36eabc1052 --- /dev/null +++ b/contrib/libs/nghttp3/lib/nghttp3_vec.h @@ -0,0 +1,41 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_VEC_H +#define NGHTTP3_VEC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +/* + * nghttp3_vec_len_varint is similar to nghttp3_vec_len, but it + * returns -1 if the sum of the length exceeds NGHTTP3_MAX_VARINT. + */ +int64_t nghttp3_vec_len_varint(const nghttp3_vec *vec, size_t n); + +#endif /* !defined(NGHTTP3_VEC_H) */ diff --git a/contrib/libs/nghttp3/lib/nghttp3_version.c b/contrib/libs/nghttp3/lib/nghttp3_version.c new file mode 100644 index 00000000000..939821d84ea --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <nghttp3/nghttp3.h> + +static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, +                               NGHTTP3_VERSION}; + +const nghttp3_info *nghttp3_version(int least_version) { +  if (least_version > NGHTTP3_VERSION_NUM) { +    return NULL; +  } +  return &version; +} diff --git a/contrib/libs/nghttp3/lib/sfparse/COPYING b/contrib/libs/nghttp3/lib/sfparse/COPYING new file mode 100644 index 00000000000..8212d82d83a --- /dev/null +++ b/contrib/libs/nghttp3/lib/sfparse/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2023 sfparse contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/contrib/libs/nghttp3/lib/sfparse/sfparse.c b/contrib/libs/nghttp3/lib/sfparse/sfparse.c new file mode 100644 index 00000000000..d0328cf40c2 --- /dev/null +++ b/contrib/libs/nghttp3/lib/sfparse/sfparse.c @@ -0,0 +1,1517 @@ +/* + * sfparse + * + * Copyright (c) 2023 sfparse contributors + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "sfparse.h" + +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#define SF_STATE_DICT 0x08u +#define SF_STATE_LIST 0x10u +#define SF_STATE_ITEM 0x18u + +#define SF_STATE_INNER_LIST 0x04u + +#define SF_STATE_BEFORE 0x00u +#define SF_STATE_BEFORE_PARAMS 0x01u +#define SF_STATE_PARAMS 0x02u +#define SF_STATE_AFTER 0x03u + +#define SF_STATE_OP_MASK 0x03u + +#define SF_SET_STATE_AFTER(NAME) (SF_STATE_##NAME | SF_STATE_AFTER) +#define SF_SET_STATE_BEFORE_PARAMS(NAME)                                       \ +  (SF_STATE_##NAME | SF_STATE_BEFORE_PARAMS) +#define SF_SET_STATE_INNER_LIST_BEFORE(NAME)                                   \ +  (SF_STATE_##NAME | SF_STATE_INNER_LIST | SF_STATE_BEFORE) + +#define SF_STATE_DICT_AFTER SF_SET_STATE_AFTER(DICT) +#define SF_STATE_DICT_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(DICT) +#define SF_STATE_DICT_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(DICT) + +#define SF_STATE_LIST_AFTER SF_SET_STATE_AFTER(LIST) +#define SF_STATE_LIST_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(LIST) +#define SF_STATE_LIST_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(LIST) + +#define SF_STATE_ITEM_AFTER SF_SET_STATE_AFTER(ITEM) +#define SF_STATE_ITEM_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(ITEM) +#define SF_STATE_ITEM_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(ITEM) + +#define SF_STATE_INITIAL 0x00u + +#define DIGIT_CASES                                                            \ +  case '0':                                                                    \ +  case '1':                                                                    \ +  case '2':                                                                    \ +  case '3':                                                                    \ +  case '4':                                                                    \ +  case '5':                                                                    \ +  case '6':                                                                    \ +  case '7':                                                                    \ +  case '8':                                                                    \ +  case '9' + +#define LCALPHA_CASES                                                          \ +  case 'a':                                                                    \ +  case 'b':                                                                    \ +  case 'c':                                                                    \ +  case 'd':                                                                    \ +  case 'e':                                                                    \ +  case 'f':                                                                    \ +  case 'g':                                                                    \ +  case 'h':                                                                    \ +  case 'i':                                                                    \ +  case 'j':                                                                    \ +  case 'k':                                                                    \ +  case 'l':                                                                    \ +  case 'm':                                                                    \ +  case 'n':                                                                    \ +  case 'o':                                                                    \ +  case 'p':                                                                    \ +  case 'q':                                                                    \ +  case 'r':                                                                    \ +  case 's':                                                                    \ +  case 't':                                                                    \ +  case 'u':                                                                    \ +  case 'v':                                                                    \ +  case 'w':                                                                    \ +  case 'x':                                                                    \ +  case 'y':                                                                    \ +  case 'z' + +#define UCALPHA_CASES                                                          \ +  case 'A':                                                                    \ +  case 'B':                                                                    \ +  case 'C':                                                                    \ +  case 'D':                                                                    \ +  case 'E':                                                                    \ +  case 'F':                                                                    \ +  case 'G':                                                                    \ +  case 'H':                                                                    \ +  case 'I':                                                                    \ +  case 'J':                                                                    \ +  case 'K':                                                                    \ +  case 'L':                                                                    \ +  case 'M':                                                                    \ +  case 'N':                                                                    \ +  case 'O':                                                                    \ +  case 'P':                                                                    \ +  case 'Q':                                                                    \ +  case 'R':                                                                    \ +  case 'S':                                                                    \ +  case 'T':                                                                    \ +  case 'U':                                                                    \ +  case 'V':                                                                    \ +  case 'W':                                                                    \ +  case 'X':                                                                    \ +  case 'Y':                                                                    \ +  case 'Z' + +#define ALPHA_CASES                                                            \ +  UCALPHA_CASES:                                                               \ +  LCALPHA_CASES + +#define TOKEN_CASES                                                            \ +  case '!':                                                                    \ +  case '#':                                                                    \ +  case '$':                                                                    \ +  case '%':                                                                    \ +  case '&':                                                                    \ +  case '\'':                                                                   \ +  case '*':                                                                    \ +  case '+':                                                                    \ +  case '-':                                                                    \ +  case '.':                                                                    \ +  case '/':                                                                    \ +  DIGIT_CASES:                                                                 \ +  case ':':                                                                    \ +  UCALPHA_CASES:                                                               \ +  case '^':                                                                    \ +  case '_':                                                                    \ +  case '`':                                                                    \ +  LCALPHA_CASES:                                                               \ +  case '|':                                                                    \ +  case '~' + +#define LCHEXALPHA_CASES                                                       \ +  case 'a':                                                                    \ +  case 'b':                                                                    \ +  case 'c':                                                                    \ +  case 'd':                                                                    \ +  case 'e':                                                                    \ +  case 'f' + +#define X00_1F_CASES                                                           \ +  case 0x00:                                                                   \ +  case 0x01:                                                                   \ +  case 0x02:                                                                   \ +  case 0x03:                                                                   \ +  case 0x04:                                                                   \ +  case 0x05:                                                                   \ +  case 0x06:                                                                   \ +  case 0x07:                                                                   \ +  case 0x08:                                                                   \ +  case 0x09:                                                                   \ +  case 0x0a:                                                                   \ +  case 0x0b:                                                                   \ +  case 0x0c:                                                                   \ +  case 0x0d:                                                                   \ +  case 0x0e:                                                                   \ +  case 0x0f:                                                                   \ +  case 0x10:                                                                   \ +  case 0x11:                                                                   \ +  case 0x12:                                                                   \ +  case 0x13:                                                                   \ +  case 0x14:                                                                   \ +  case 0x15:                                                                   \ +  case 0x16:                                                                   \ +  case 0x17:                                                                   \ +  case 0x18:                                                                   \ +  case 0x19:                                                                   \ +  case 0x1a:                                                                   \ +  case 0x1b:                                                                   \ +  case 0x1c:                                                                   \ +  case 0x1d:                                                                   \ +  case 0x1e:                                                                   \ +  case 0x1f + +#define X20_21_CASES                                                           \ +  case ' ':                                                                    \ +  case '!' + +#define X23_5B_CASES                                                           \ +  case '#':                                                                    \ +  case '$':                                                                    \ +  case '%':                                                                    \ +  case '&':                                                                    \ +  case '\'':                                                                   \ +  case '(':                                                                    \ +  case ')':                                                                    \ +  case '*':                                                                    \ +  case '+':                                                                    \ +  case ',':                                                                    \ +  case '-':                                                                    \ +  case '.':                                                                    \ +  case '/':                                                                    \ +  DIGIT_CASES:                                                                 \ +  case ':':                                                                    \ +  case ';':                                                                    \ +  case '<':                                                                    \ +  case '=':                                                                    \ +  case '>':                                                                    \ +  case '?':                                                                    \ +  case '@':                                                                    \ +  UCALPHA_CASES:                                                               \ +  case '[' + +#define X5D_7E_CASES                                                           \ +  case ']':                                                                    \ +  case '^':                                                                    \ +  case '_':                                                                    \ +  case '`':                                                                    \ +  LCALPHA_CASES:                                                               \ +  case '{':                                                                    \ +  case '|':                                                                    \ +  case '}':                                                                    \ +  case '~' + +#define X7F_FF_CASES                                                           \ +  case 0x7f:                                                                   \ +  case 0x80:                                                                   \ +  case 0x81:                                                                   \ +  case 0x82:                                                                   \ +  case 0x83:                                                                   \ +  case 0x84:                                                                   \ +  case 0x85:                                                                   \ +  case 0x86:                                                                   \ +  case 0x87:                                                                   \ +  case 0x88:                                                                   \ +  case 0x89:                                                                   \ +  case 0x8a:                                                                   \ +  case 0x8b:                                                                   \ +  case 0x8c:                                                                   \ +  case 0x8d:                                                                   \ +  case 0x8e:                                                                   \ +  case 0x8f:                                                                   \ +  case 0x90:                                                                   \ +  case 0x91:                                                                   \ +  case 0x92:                                                                   \ +  case 0x93:                                                                   \ +  case 0x94:                                                                   \ +  case 0x95:                                                                   \ +  case 0x96:                                                                   \ +  case 0x97:                                                                   \ +  case 0x98:                                                                   \ +  case 0x99:                                                                   \ +  case 0x9a:                                                                   \ +  case 0x9b:                                                                   \ +  case 0x9c:                                                                   \ +  case 0x9d:                                                                   \ +  case 0x9e:                                                                   \ +  case 0x9f:                                                                   \ +  case 0xa0:                                                                   \ +  case 0xa1:                                                                   \ +  case 0xa2:                                                                   \ +  case 0xa3:                                                                   \ +  case 0xa4:                                                                   \ +  case 0xa5:                                                                   \ +  case 0xa6:                                                                   \ +  case 0xa7:                                                                   \ +  case 0xa8:                                                                   \ +  case 0xa9:                                                                   \ +  case 0xaa:                                                                   \ +  case 0xab:                                                                   \ +  case 0xac:                                                                   \ +  case 0xad:                                                                   \ +  case 0xae:                                                                   \ +  case 0xaf:                                                                   \ +  case 0xb0:                                                                   \ +  case 0xb1:                                                                   \ +  case 0xb2:                                                                   \ +  case 0xb3:                                                                   \ +  case 0xb4:                                                                   \ +  case 0xb5:                                                                   \ +  case 0xb6:                                                                   \ +  case 0xb7:                                                                   \ +  case 0xb8:                                                                   \ +  case 0xb9:                                                                   \ +  case 0xba:                                                                   \ +  case 0xbb:                                                                   \ +  case 0xbc:                                                                   \ +  case 0xbd:                                                                   \ +  case 0xbe:                                                                   \ +  case 0xbf:                                                                   \ +  case 0xc0:                                                                   \ +  case 0xc1:                                                                   \ +  case 0xc2:                                                                   \ +  case 0xc3:                                                                   \ +  case 0xc4:                                                                   \ +  case 0xc5:                                                                   \ +  case 0xc6:                                                                   \ +  case 0xc7:                                                                   \ +  case 0xc8:                                                                   \ +  case 0xc9:                                                                   \ +  case 0xca:                                                                   \ +  case 0xcb:                                                                   \ +  case 0xcc:                                                                   \ +  case 0xcd:                                                                   \ +  case 0xce:                                                                   \ +  case 0xcf:                                                                   \ +  case 0xd0:                                                                   \ +  case 0xd1:                                                                   \ +  case 0xd2:                                                                   \ +  case 0xd3:                                                                   \ +  case 0xd4:                                                                   \ +  case 0xd5:                                                                   \ +  case 0xd6:                                                                   \ +  case 0xd7:                                                                   \ +  case 0xd8:                                                                   \ +  case 0xd9:                                                                   \ +  case 0xda:                                                                   \ +  case 0xdb:                                                                   \ +  case 0xdc:                                                                   \ +  case 0xdd:                                                                   \ +  case 0xde:                                                                   \ +  case 0xdf:                                                                   \ +  case 0xe0:                                                                   \ +  case 0xe1:                                                                   \ +  case 0xe2:                                                                   \ +  case 0xe3:                                                                   \ +  case 0xe4:                                                                   \ +  case 0xe5:                                                                   \ +  case 0xe6:                                                                   \ +  case 0xe7:                                                                   \ +  case 0xe8:                                                                   \ +  case 0xe9:                                                                   \ +  case 0xea:                                                                   \ +  case 0xeb:                                                                   \ +  case 0xec:                                                                   \ +  case 0xed:                                                                   \ +  case 0xee:                                                                   \ +  case 0xef:                                                                   \ +  case 0xf0:                                                                   \ +  case 0xf1:                                                                   \ +  case 0xf2:                                                                   \ +  case 0xf3:                                                                   \ +  case 0xf4:                                                                   \ +  case 0xf5:                                                                   \ +  case 0xf6:                                                                   \ +  case 0xf7:                                                                   \ +  case 0xf8:                                                                   \ +  case 0xf9:                                                                   \ +  case 0xfa:                                                                   \ +  case 0xfb:                                                                   \ +  case 0xfc:                                                                   \ +  case 0xfd:                                                                   \ +  case 0xfe:                                                                   \ +  case 0xff + +static int is_ws(uint8_t c) { +  switch (c) { +  case ' ': +  case '\t': +    return 1; +  default: +    return 0; +  } +} + +static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; } + +static void parser_discard_ows(sf_parser *sfp) { +  for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) +    ; +} + +static void parser_discard_sp(sf_parser *sfp) { +  for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos) +    ; +} + +static void parser_set_op_state(sf_parser *sfp, uint32_t op) { +  sfp->state &= ~SF_STATE_OP_MASK; +  sfp->state |= op; +} + +static void parser_unset_inner_list_state(sf_parser *sfp) { +  sfp->state &= ~SF_STATE_INNER_LIST; +} + +static int parser_key(sf_parser *sfp, sf_vec *dest) { +  const uint8_t *base; + +  switch (*sfp->pos) { +  case '*': +  LCALPHA_CASES: +    break; +  default: +    return SF_ERR_PARSE_ERROR; +  } + +  base = sfp->pos++; + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    case '_': +    case '-': +    case '.': +    case '*': +    DIGIT_CASES: +    LCALPHA_CASES: +      continue; +    } + +    break; +  } + +  if (dest) { +    dest->base = (uint8_t *)base; +    dest->len = (size_t)(sfp->pos - dest->base); +  } + +  return 0; +} + +static int parser_number(sf_parser *sfp, sf_value *dest) { +  int sign = 1; +  int64_t value = 0; +  size_t len = 0; +  size_t fpos = 0; + +  if (*sfp->pos == '-') { +    ++sfp->pos; +    if (parser_eof(sfp)) { +      return SF_ERR_PARSE_ERROR; +    } + +    sign = -1; +  } + +  assert(!parser_eof(sfp)); + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    DIGIT_CASES: +      if (++len > 15) { +        return SF_ERR_PARSE_ERROR; +      } + +      value *= 10; +      value += *sfp->pos - '0'; + +      continue; +    } + +    break; +  } + +  if (len == 0) { +    return SF_ERR_PARSE_ERROR; +  } + +  if (parser_eof(sfp) || *sfp->pos != '.') { +    if (dest) { +      dest->type = SF_TYPE_INTEGER; +      dest->flags = SF_VALUE_FLAG_NONE; +      dest->integer = value * sign; +    } + +    return 0; +  } + +  /* decimal */ + +  if (len > 12) { +    return SF_ERR_PARSE_ERROR; +  } + +  fpos = len; + +  ++sfp->pos; + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    DIGIT_CASES: +      if (++len > 15) { +        return SF_ERR_PARSE_ERROR; +      } + +      value *= 10; +      value += *sfp->pos - '0'; + +      continue; +    } + +    break; +  } + +  if (fpos == len || len - fpos > 3) { +    return SF_ERR_PARSE_ERROR; +  } + +  if (dest) { +    dest->type = SF_TYPE_DECIMAL; +    dest->flags = SF_VALUE_FLAG_NONE; +    dest->decimal.numer = value * sign; + +    switch (len - fpos) { +    case 1: +      dest->decimal.denom = 10; + +      break; +    case 2: +      dest->decimal.denom = 100; + +      break; +    case 3: +      dest->decimal.denom = 1000; + +      break; +    } +  } + +  return 0; +} + +static int parser_date(sf_parser *sfp, sf_value *dest) { +  int rv; +  sf_value val; + +  /* The first byte has already been validated by the caller. */ +  assert('@' == *sfp->pos); + +  ++sfp->pos; + +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  rv = parser_number(sfp, &val); +  if (rv != 0) { +    return rv; +  } + +  if (val.type != SF_TYPE_INTEGER) { +    return SF_ERR_PARSE_ERROR; +  } + +  if (dest) { +    *dest = val; +    dest->type = SF_TYPE_DATE; +  } + +  return 0; +} + +static int parser_string(sf_parser *sfp, sf_value *dest) { +  const uint8_t *base; +  uint32_t flags = SF_VALUE_FLAG_NONE; + +  /* The first byte has already been validated by the caller. */ +  assert('"' == *sfp->pos); + +  base = ++sfp->pos; + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    X20_21_CASES: +    X23_5B_CASES: +    X5D_7E_CASES: +      break; +    case '\\': +      ++sfp->pos; +      if (parser_eof(sfp)) { +        return SF_ERR_PARSE_ERROR; +      } + +      switch (*sfp->pos) { +      case '"': +      case '\\': +        flags = SF_VALUE_FLAG_ESCAPED_STRING; + +        break; +      default: +        return SF_ERR_PARSE_ERROR; +      } + +      break; +    case '"': +      if (dest) { +        dest->type = SF_TYPE_STRING; +        dest->flags = flags; +        dest->vec.len = (size_t)(sfp->pos - base); +        dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; +      } + +      ++sfp->pos; + +      return 0; +    default: +      return SF_ERR_PARSE_ERROR; +    } +  } + +  return SF_ERR_PARSE_ERROR; +} + +static int parser_token(sf_parser *sfp, sf_value *dest) { +  const uint8_t *base; + +  /* The first byte has already been validated by the caller. */ +  base = sfp->pos++; + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    TOKEN_CASES: +      continue; +    } + +    break; +  } + +  if (dest) { +    dest->type = SF_TYPE_TOKEN; +    dest->flags = SF_VALUE_FLAG_NONE; +    dest->vec.base = (uint8_t *)base; +    dest->vec.len = (size_t)(sfp->pos - base); +  } + +  return 0; +} + +static int parser_byteseq(sf_parser *sfp, sf_value *dest) { +  const uint8_t *base; + +  /* The first byte has already been validated by the caller. */ +  assert(':' == *sfp->pos); + +  base = ++sfp->pos; + +  for (; !parser_eof(sfp); ++sfp->pos) { +    switch (*sfp->pos) { +    case '+': +    case '/': +    DIGIT_CASES: +    ALPHA_CASES: +      continue; +    case '=': +      switch ((sfp->pos - base) & 0x3) { +      case 0: +      case 1: +        return SF_ERR_PARSE_ERROR; +      case 2: +        ++sfp->pos; + +        if (parser_eof(sfp)) { +          return SF_ERR_PARSE_ERROR; +        } + +        if (*sfp->pos == '=') { +          ++sfp->pos; +        } + +        break; +      case 3: +        ++sfp->pos; + +        break; +      } + +      if (parser_eof(sfp) || *sfp->pos != ':') { +        return SF_ERR_PARSE_ERROR; +      } + +      goto fin; +    case ':': +      if (((sfp->pos - base) & 0x3) == 1) { +        return SF_ERR_PARSE_ERROR; +      } + +      goto fin; +    default: +      return SF_ERR_PARSE_ERROR; +    } +  } + +  return SF_ERR_PARSE_ERROR; + +fin: +  if (dest) { +    dest->type = SF_TYPE_BYTESEQ; +    dest->flags = SF_VALUE_FLAG_NONE; +    dest->vec.len = (size_t)(sfp->pos - base); +    dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; +  } + +  ++sfp->pos; + +  return 0; +} + +static int parser_boolean(sf_parser *sfp, sf_value *dest) { +  int b; + +  /* The first byte has already been validated by the caller. */ +  assert('?' == *sfp->pos); + +  ++sfp->pos; + +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  switch (*sfp->pos) { +  case '0': +    b = 0; + +    break; +  case '1': +    b = 1; + +    break; +  default: +    return SF_ERR_PARSE_ERROR; +  } + +  ++sfp->pos; + +  if (dest) { +    dest->type = SF_TYPE_BOOLEAN; +    dest->flags = SF_VALUE_FLAG_NONE; +    dest->boolean = b; +  } + +  return 0; +} + +static int pctdecode(uint8_t *pc, const uint8_t **ppos) { +  uint8_t c, b = **ppos; + +  switch (b) { +  DIGIT_CASES: +    c = (uint8_t)((b - '0') << 4); + +    break; +  LCHEXALPHA_CASES: +    c = (uint8_t)((b - 'a' + 10) << 4); + +    break; +  default: +    return -1; +  } + +  b = *++*ppos; + +  switch (b) { +  DIGIT_CASES: +    c |= (uint8_t)(b - '0'); + +    break; +  LCHEXALPHA_CASES: +    c |= (uint8_t)(b - 'a' + 10); + +    break; +  default: +    return -1; +  } + +  *pc = c; +  ++*ppos; + +  return 0; +} + +/* Start of utf8 dfa */ +/* Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]> + * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + * + * Copyright (c) 2008-2009 Bjoern Hoehrmann <[email protected]> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 12 + +/* clang-format off */ +static const uint8_t utf8d[] = { +  /* +   * The first part of the table maps bytes to character classes that +   * to reduce the size of the transition table and create bitmasks. +   */ +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, +   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, +   8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, +  10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + +   /* +    * The second part is a transition table that maps a combination +    * of a state of the automaton and a character class to a state. +    */ +   0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, +  12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, +  12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, +  12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, +  12,36,12,12,12,12,12,12,12,12,12,12, +}; +/* clang-format on */ + +static void utf8_decode(uint32_t *state, uint8_t byte) { +  *state = utf8d[256 + *state + utf8d[byte]]; +} + +/* End of utf8 dfa */ + +static int parser_dispstring(sf_parser *sfp, sf_value *dest) { +  const uint8_t *base; +  uint8_t c; +  uint32_t utf8state = UTF8_ACCEPT; + +  assert('%' == *sfp->pos); + +  ++sfp->pos; + +  if (parser_eof(sfp) || *sfp->pos != '"') { +    return SF_ERR_PARSE_ERROR; +  } + +  base = ++sfp->pos; + +  for (; !parser_eof(sfp);) { +    switch (*sfp->pos) { +    X00_1F_CASES: +    X7F_FF_CASES: +      return SF_ERR_PARSE_ERROR; +    case '%': +      ++sfp->pos; + +      if (sfp->pos + 2 > sfp->end) { +        return SF_ERR_PARSE_ERROR; +      } + +      if (pctdecode(&c, &sfp->pos) != 0) { +        return SF_ERR_PARSE_ERROR; +      } + +      utf8_decode(&utf8state, c); +      if (utf8state == UTF8_REJECT) { +        return SF_ERR_PARSE_ERROR; +      } + +      break; +    case '"': +      if (utf8state != UTF8_ACCEPT) { +        return SF_ERR_PARSE_ERROR; +      } + +      if (dest) { +        dest->type = SF_TYPE_DISPSTRING; +        dest->flags = SF_VALUE_FLAG_NONE; +        dest->vec.len = (size_t)(sfp->pos - base); +        dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; +      } + +      ++sfp->pos; + +      return 0; +    default: +      if (utf8state != UTF8_ACCEPT) { +        return SF_ERR_PARSE_ERROR; +      } + +      ++sfp->pos; +    } +  } + +  return SF_ERR_PARSE_ERROR; +} + +static int parser_bare_item(sf_parser *sfp, sf_value *dest) { +  switch (*sfp->pos) { +  case '"': +    return parser_string(sfp, dest); +  case '-': +  DIGIT_CASES: +    return parser_number(sfp, dest); +  case '@': +    return parser_date(sfp, dest); +  case ':': +    return parser_byteseq(sfp, dest); +  case '?': +    return parser_boolean(sfp, dest); +  case '*': +  ALPHA_CASES: +    return parser_token(sfp, dest); +  case '%': +    return parser_dispstring(sfp, dest); +  default: +    return SF_ERR_PARSE_ERROR; +  } +} + +static int parser_skip_inner_list(sf_parser *sfp); + +int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { +  int rv; + +  switch (sfp->state & SF_STATE_OP_MASK) { +  case SF_STATE_BEFORE: +    rv = parser_skip_inner_list(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_BEFORE_PARAMS: +    parser_set_op_state(sfp, SF_STATE_PARAMS); + +    break; +  case SF_STATE_PARAMS: +    break; +  default: +    assert(0); +    abort(); +  } + +  if (parser_eof(sfp) || *sfp->pos != ';') { +    parser_set_op_state(sfp, SF_STATE_AFTER); + +    return SF_ERR_EOF; +  } + +  ++sfp->pos; + +  parser_discard_sp(sfp); +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  rv = parser_key(sfp, dest_key); +  if (rv != 0) { +    return rv; +  } + +  if (parser_eof(sfp) || *sfp->pos != '=') { +    if (dest_value) { +      dest_value->type = SF_TYPE_BOOLEAN; +      dest_value->flags = SF_VALUE_FLAG_NONE; +      dest_value->boolean = 1; +    } + +    return 0; +  } + +  ++sfp->pos; + +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  return parser_bare_item(sfp, dest_value); +} + +static int parser_skip_params(sf_parser *sfp) { +  int rv; + +  for (;;) { +    rv = sf_parser_param(sfp, NULL, NULL); +    switch (rv) { +    case 0: +      break; +    case SF_ERR_EOF: +      return 0; +    case SF_ERR_PARSE_ERROR: +      return rv; +    default: +      assert(0); +      abort(); +    } +  } +} + +int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { +  int rv; + +  switch (sfp->state & SF_STATE_OP_MASK) { +  case SF_STATE_BEFORE: +    parser_discard_sp(sfp); +    if (parser_eof(sfp)) { +      return SF_ERR_PARSE_ERROR; +    } + +    break; +  case SF_STATE_BEFORE_PARAMS: +    rv = parser_skip_params(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* Technically, we are entering SF_STATE_AFTER, but we will set +       another state without reading the state. */ +    /* parser_set_op_state(sfp, SF_STATE_AFTER); */ + +    /* fall through */ +  case SF_STATE_AFTER: +    if (parser_eof(sfp)) { +      return SF_ERR_PARSE_ERROR; +    } + +    switch (*sfp->pos) { +    case ' ': +      parser_discard_sp(sfp); +      if (parser_eof(sfp)) { +        return SF_ERR_PARSE_ERROR; +      } + +      break; +    case ')': +      break; +    default: +      return SF_ERR_PARSE_ERROR; +    } + +    break; +  default: +    assert(0); +    abort(); +  } + +  if (*sfp->pos == ')') { +    ++sfp->pos; + +    parser_unset_inner_list_state(sfp); +    parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); + +    return SF_ERR_EOF; +  } + +  rv = parser_bare_item(sfp, dest); +  if (rv != 0) { +    return rv; +  } + +  parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); + +  return 0; +} + +static int parser_skip_inner_list(sf_parser *sfp) { +  int rv; + +  for (;;) { +    rv = sf_parser_inner_list(sfp, NULL); +    switch (rv) { +    case 0: +      break; +    case SF_ERR_EOF: +      return 0; +    case SF_ERR_PARSE_ERROR: +      return rv; +    default: +      assert(0); +      abort(); +    } +  } +} + +static int parser_next_key_or_item(sf_parser *sfp) { +  parser_discard_ows(sfp); + +  if (parser_eof(sfp)) { +    return SF_ERR_EOF; +  } + +  if (*sfp->pos != ',') { +    return SF_ERR_PARSE_ERROR; +  } + +  ++sfp->pos; + +  parser_discard_ows(sfp); +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  return 0; +} + +static int parser_dict_value(sf_parser *sfp, sf_value *dest) { +  int rv; + +  if (parser_eof(sfp) || *(sfp->pos) != '=') { +    /* Boolean true */ +    if (dest) { +      dest->type = SF_TYPE_BOOLEAN; +      dest->flags = SF_VALUE_FLAG_NONE; +      dest->boolean = 1; +    } + +    sfp->state = SF_STATE_DICT_BEFORE_PARAMS; + +    return 0; +  } + +  ++sfp->pos; + +  if (parser_eof(sfp)) { +    return SF_ERR_PARSE_ERROR; +  } + +  if (*sfp->pos == '(') { +    if (dest) { +      dest->type = SF_TYPE_INNER_LIST; +      dest->flags = SF_VALUE_FLAG_NONE; +    } + +    ++sfp->pos; + +    sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE; + +    return 0; +  } + +  rv = parser_bare_item(sfp, dest); +  if (rv != 0) { +    return rv; +  } + +  sfp->state = SF_STATE_DICT_BEFORE_PARAMS; + +  return 0; +} + +int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { +  int rv; + +  switch (sfp->state) { +  case SF_STATE_DICT_INNER_LIST_BEFORE: +    rv = parser_skip_inner_list(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_DICT_BEFORE_PARAMS: +    rv = parser_skip_params(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_DICT_AFTER: +    rv = parser_next_key_or_item(sfp); +    if (rv != 0) { +      return rv; +    } + +    break; +  case SF_STATE_INITIAL: +    parser_discard_sp(sfp); + +    if (parser_eof(sfp)) { +      return SF_ERR_EOF; +    } + +    break; +  default: +    assert(0); +    abort(); +  } + +  rv = parser_key(sfp, dest_key); +  if (rv != 0) { +    return rv; +  } + +  return parser_dict_value(sfp, dest_value); +} + +int sf_parser_list(sf_parser *sfp, sf_value *dest) { +  int rv; + +  switch (sfp->state) { +  case SF_STATE_LIST_INNER_LIST_BEFORE: +    rv = parser_skip_inner_list(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_LIST_BEFORE_PARAMS: +    rv = parser_skip_params(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_LIST_AFTER: +    rv = parser_next_key_or_item(sfp); +    if (rv != 0) { +      return rv; +    } + +    break; +  case SF_STATE_INITIAL: +    parser_discard_sp(sfp); + +    if (parser_eof(sfp)) { +      return SF_ERR_EOF; +    } + +    break; +  default: +    assert(0); +    abort(); +  } + +  if (*sfp->pos == '(') { +    if (dest) { +      dest->type = SF_TYPE_INNER_LIST; +      dest->flags = SF_VALUE_FLAG_NONE; +    } + +    ++sfp->pos; + +    sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE; + +    return 0; +  } + +  rv = parser_bare_item(sfp, dest); +  if (rv != 0) { +    return rv; +  } + +  sfp->state = SF_STATE_LIST_BEFORE_PARAMS; + +  return 0; +} + +int sf_parser_item(sf_parser *sfp, sf_value *dest) { +  int rv; + +  switch (sfp->state) { +  case SF_STATE_INITIAL: +    parser_discard_sp(sfp); + +    if (parser_eof(sfp)) { +      return SF_ERR_PARSE_ERROR; +    } + +    break; +  case SF_STATE_ITEM_INNER_LIST_BEFORE: +    rv = parser_skip_inner_list(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_ITEM_BEFORE_PARAMS: +    rv = parser_skip_params(sfp); +    if (rv != 0) { +      return rv; +    } + +    /* fall through */ +  case SF_STATE_ITEM_AFTER: +    parser_discard_sp(sfp); + +    if (!parser_eof(sfp)) { +      return SF_ERR_PARSE_ERROR; +    } + +    return SF_ERR_EOF; +  default: +    assert(0); +    abort(); +  } + +  if (*sfp->pos == '(') { +    if (dest) { +      dest->type = SF_TYPE_INNER_LIST; +      dest->flags = SF_VALUE_FLAG_NONE; +    } + +    ++sfp->pos; + +    sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE; + +    return 0; +  } + +  rv = parser_bare_item(sfp, dest); +  if (rv != 0) { +    return rv; +  } + +  sfp->state = SF_STATE_ITEM_BEFORE_PARAMS; + +  return 0; +} + +void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { +  if (datalen == 0) { +    sfp->pos = sfp->end = NULL; +  } else { +    sfp->pos = data; +    sfp->end = data + datalen; +  } + +  sfp->state = SF_STATE_INITIAL; +} + +void sf_unescape(sf_vec *dest, const sf_vec *src) { +  const uint8_t *p, *q; +  uint8_t *o; +  size_t len, slen; + +  if (src->len == 0) { +    dest->len = 0; + +    return; +  } + +  o = dest->base; +  p = src->base; +  len = src->len; + +  for (;;) { +    q = memchr(p, '\\', len); +    if (q == NULL) { +      memcpy(o, p, len); +      o += len; + +      dest->len = (size_t)(o - dest->base); + +      return; +    } + +    slen = (size_t)(q - p); +    memcpy(o, p, slen); +    o += slen; + +    p = q + 1; +    *o++ = *p++; +    len -= slen + 2; +  } +} + +void sf_base64decode(sf_vec *dest, const sf_vec *src) { +  static const int index_tbl[] = { +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, +      58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0,  1,  2,  3,  4,  5,  6, +      7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +      25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, +      37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +      -1, -1, -1, -1}; +  uint8_t *o; +  const uint8_t *p, *end; +  uint32_t n; +  size_t i, left; +  int idx; + +  if (src->len == 0) { +    dest->len = 0; + +    return; +  } + +  o = dest->base; +  p = src->base; +  left = src->len & 0x3; +  if (left == 0 && src->base[src->len - 1] == '=') { +    left = 4; +  } +  end = src->base + src->len - left; + +  for (; p != end;) { +    n = 0; + +    for (i = 1; i <= 4; ++i, ++p) { +      idx = index_tbl[*p]; + +      assert(idx != -1); + +      n += (uint32_t)(idx << (24 - i * 6)); +    } + +    *o++ = (uint8_t)(n >> 16); +    *o++ = (n >> 8) & 0xffu; +    *o++ = n & 0xffu; +  } + +  switch (left) { +  case 0: +    goto fin; +  case 1: +    assert(0); +    abort(); +  case 3: +    if (src->base[src->len - 1] == '=') { +      left = 2; +    } + +    break; +  case 4: +    assert('=' == src->base[src->len - 1]); + +    if (src->base[src->len - 2] == '=') { +      left = 2; +    } else { +      left = 3; +    } + +    break; +  } + +  switch (left) { +  case 2: +    *o = (uint8_t)(index_tbl[*p++] << 2); +    *o++ |= (uint8_t)(index_tbl[*p++] >> 4); + +    break; +  case 3: +    n = (uint32_t)(index_tbl[*p++] << 10); +    n += (uint32_t)(index_tbl[*p++] << 4); +    n += (uint32_t)(index_tbl[*p++] >> 2); +    *o++ = (n >> 8) & 0xffu; +    *o++ = n & 0xffu; + +    break; +  } + +fin: +  dest->len = (size_t)(o - dest->base); +} + +void sf_pctdecode(sf_vec *dest, const sf_vec *src) { +  const uint8_t *p, *q; +  uint8_t *o; +  size_t len, slen; + +  if (src->len == 0) { +    dest->len = 0; + +    return; +  } + +  o = dest->base; +  p = src->base; +  len = src->len; + +  for (;;) { +    q = memchr(p, '%', len); +    if (q == NULL) { +      memcpy(o, p, len); +      o += len; + +      dest->len = (size_t)(o - dest->base); + +      return; +    } + +    slen = (size_t)(q - p); +    memcpy(o, p, slen); +    o += slen; + +    p = q + 1; + +    pctdecode(o++, &p); + +    len -= slen + 3; +  } +} diff --git a/contrib/libs/nghttp3/lib/sfparse/sfparse.h b/contrib/libs/nghttp3/lib/sfparse/sfparse.h new file mode 100644 index 00000000000..01cc947d4d6 --- /dev/null +++ b/contrib/libs/nghttp3/lib/sfparse/sfparse.h @@ -0,0 +1,428 @@ +/* + * sfparse + * + * Copyright (c) 2023 sfparse contributors + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SFPARSE_H +#define SFPARSE_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 + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 +   compliant.  See compiler macros and version number in +   https://sourceforge.net/p/predef/wiki/Compilers/ */ +#  include <stdint.h> +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#  include <inttypes.h> +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include <sys/types.h> +#include <stddef.h> + +/** + * @enum + * + * :type:`sf_type` defines value type. + */ +typedef enum sf_type { +  /** +   * :enum:`SF_TYPE_BOOLEAN` indicates boolean type. +   */ +  SF_TYPE_BOOLEAN, +  /** +   * :enum:`SF_TYPE_INTEGER` indicates integer type. +   */ +  SF_TYPE_INTEGER, +  /** +   * :enum:`SF_TYPE_DECIMAL` indicates decimal type. +   */ +  SF_TYPE_DECIMAL, +  /** +   * :enum:`SF_TYPE_STRING` indicates string type. +   */ +  SF_TYPE_STRING, +  /** +   * :enum:`SF_TYPE_TOKEN` indicates token type. +   */ +  SF_TYPE_TOKEN, +  /** +   * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type. +   */ +  SF_TYPE_BYTESEQ, +  /** +   * :enum:`SF_TYPE_INNER_LIST` indicates inner list type. +   */ +  SF_TYPE_INNER_LIST, +  /** +   * :enum:`SF_TYPE_DATE` indicates date type. +   */ +  SF_TYPE_DATE, +  /** +   * :enum:`SF_TYPE_DISPSTRING` indicates display string type. +   */ +  SF_TYPE_DISPSTRING +} sf_type; + +/** + * @macro + * + * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has + * occurred, and it is not possible to continue the processing. + */ +#define SF_ERR_PARSE_ERROR -1 + +/** + * @macro + * + * :macro:`SF_ERR_EOF` indicates that there is nothing left to read. + * The context of this error varies depending on the function that + * returns this error code. + */ +#define SF_ERR_EOF -2 + +/** + * @struct + * + * :type:`sf_vec` stores sequence of bytes. + */ +typedef struct sf_vec { +  /** +   * :member:`base` points to the beginning of the sequence of bytes. +   */ +  uint8_t *base; +  /** +   * :member:`len` is the number of bytes contained in this sequence. +   */ +  size_t len; +} sf_vec; + +/** + * @macro + * + * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set. + */ +#define SF_VALUE_FLAG_NONE 0x0u + +/** + * @macro + * + * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string + * contains escaped character(s). + */ +#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u + +/** + * @struct + * + * :type:`sf_decimal` contains decimal value. + */ +typedef struct sf_decimal { +  /** +   * :member:`numer` contains numerator of the decimal value. +   */ +  int64_t numer; +  /** +   * :member:`denom` contains denominator of the decimal value. +   */ +  int64_t denom; +} sf_decimal; + +/** + * @struct + * + * :type:`sf_value` stores a Structured Field item.  For Inner List, + * only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`.  In order + * to read the items contained in an inner list, call + * `sf_parser_inner_list`. + */ +typedef struct sf_value { +  /** +   * :member:`type` is the type of the value contained in this +   * particular object. +   */ +  sf_type type; +  /** +   * :member:`flags` is bitwise OR of one or more of +   * :macro:`SF_VALUE_FLAG_* <SF_VALUE_FLAG_NONE>`. +   */ +  uint32_t flags; +  /** +   * @anonunion_start +   * +   * @sf_value_value +   */ +  union { +    /** +     * :member:`boolean` contains boolean value if :member:`type` == +     * :enum:`sf_type.SF_TYPE_BOOLEAN`.  1 indicates true, and 0 +     * indicates false. +     */ +    int boolean; +    /** +     * :member:`integer` contains integer value if :member:`type` is +     * either :enum:`sf_type.SF_TYPE_INTEGER` or +     * :enum:`sf_type.SF_TYPE_DATE`. +     */ +    int64_t integer; +    /** +     * :member:`decimal` contains decimal value if :member:`type` == +     * :enum:`sf_type.SF_TYPE_DECIMAL`. +     */ +    sf_decimal decimal; +    /** +     * :member:`vec` contains sequence of bytes if :member:`type` is +     * either :enum:`sf_type.SF_TYPE_STRING`, +     * :enum:`sf_type.SF_TYPE_TOKEN`, :enum:`sf_type.SF_TYPE_BYTESEQ`, +     * or :enum:`sf_type.SF_TYPE_DISPSTRING`. +     * +     * For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or +     * more escaped characters if :member:`flags` has +     * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set.  To unescape the +     * string, use `sf_unescape`. +     * +     * For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64 +     * encoded string.  To decode this byte string, use +     * `sf_base64decode`. +     * +     * For :enum:`sf_type.SF_TYPE_DISPSTRING`, this field may contain +     * percent-encoded UTF-8 byte sequences.  To decode it, use +     * `sf_pctdecode`. +     * +     * If :member:`vec.len <sf_vec.len>` == 0, :member:`vec.base +     * <sf_vec.base>` is guaranteed to be NULL. +     */ +    sf_vec vec; +    /** +     * @anonunion_end +     */ +  }; +} sf_value; + +/** + * @struct + * + * :type:`sf_parser` is the Structured Field Values parser.  Use + * `sf_parser_init` to initialize it. + */ +typedef struct sf_parser { +  /* all fields are private */ +  const uint8_t *pos; +  const uint8_t *end; +  uint32_t state; +} sf_parser; + +/** + * @function + * + * `sf_parser_init` initializes |sfp| with the given buffer pointed by + * |data| of length |datalen|. + */ +void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen); + +/** + * @function + * + * `sf_parser_param` reads a parameter.  If this function returns 0, + * it stores parameter key and value in |dest_key| and |dest_value| + * respectively, if they are not NULL. + * + * This function does no effort to find duplicated keys.  Same key may + * be reported more than once. + * + * Caller should keep calling this function until it returns negative + * error code.  If it returns :macro:`SF_ERR_EOF`, all parameters have + * read, and caller can continue to read rest of the values.  If it + * returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error + * while parsing field value. + */ +int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); + +/** + * @function + * + * `sf_parser_dict` reads the next dictionary key and value pair.  If + * this function returns 0, it stores the key and value in |dest_key| + * and |dest_value| respectively, if they are not NULL. + * + * Caller can optionally read parameters attached to the pair by + * calling `sf_parser_param`. + * + * This function does no effort to find duplicated keys.  Same key may + * be reported more than once. + * + * Caller should keep calling this function until it returns negative + * error code.  If it returns :macro:`SF_ERR_EOF`, all key and value + * pairs have been read, and there is nothing left to read. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`SF_ERR_EOF` + *     All values in the dictionary have read. + * :macro:`SF_ERR_PARSE_ERROR` + *     It encountered fatal error while parsing field value. + */ +int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); + +/** + * @function + * + * `sf_parser_list` reads the next list item.  If this function + * returns 0, it stores the item in |dest| if it is not NULL. + * + * Caller can optionally read parameters attached to the item by + * calling `sf_parser_param`. + * + * Caller should keep calling this function until it returns negative + * error code.  If it returns :macro:`SF_ERR_EOF`, all values in the + * list have been read, and there is nothing left to read. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`SF_ERR_EOF` + *     All values in the list have read. + * :macro:`SF_ERR_PARSE_ERROR` + *     It encountered fatal error while parsing field value. + */ +int sf_parser_list(sf_parser *sfp, sf_value *dest); + +/** + * @function + * + * `sf_parser_item` reads a single item.  If this function returns 0, + * it stores the item in |dest| if it is not NULL. + * + * This function is only used for the field value that consists of a + * single item. + * + * Caller can optionally read parameters attached to the item by + * calling `sf_parser_param`. + * + * Caller should call this function again to make sure that there is + * nothing left to read.  If this 2nd function call returns + * :macro:`SF_ERR_EOF`, all data have been processed successfully. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`SF_ERR_EOF` + *     There is nothing left to read. + * :macro:`SF_ERR_PARSE_ERROR` + *     It encountered fatal error while parsing field value. + */ +int sf_parser_item(sf_parser *sfp, sf_value *dest); + +/** + * @function + * + * `sf_parser_inner_list` reads the next inner list item.  If this + * function returns 0, it stores the item in |dest| if it is not NULL. + * + * Caller can optionally read parameters attached to the item by + * calling `sf_parser_param`. + * + * Caller should keep calling this function until it returns negative + * error code.  If it returns :macro:`SF_ERR_EOF`, all values in this + * inner list have been read, and caller can optionally read + * parameters attached to this inner list by calling + * `sf_parser_param`.  Then caller can continue to read rest of the + * values. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`SF_ERR_EOF` + *     All values in the inner list have read. + * :macro:`SF_ERR_PARSE_ERROR` + *     It encountered fatal error while parsing field value. + */ +int sf_parser_inner_list(sf_parser *sfp, sf_value *dest); + +/** + * @function + * + * `sf_unescape` copies |src| to |dest| by removing escapes (``\``). + * |src| should be the pointer to :member:`sf_value.vec` of type + * :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`, + * `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or + * `sf_parser_param`, otherwise the behavior is undefined. + * + * :member:`dest->base <sf_vec.base>` must point to the buffer that + * has sufficient space to store the unescaped string. + * + * This function sets the length of unescaped string to + * :member:`dest->len <sf_vec.len>`. + */ +void sf_unescape(sf_vec *dest, const sf_vec *src); + +/** + * @function + * + * `sf_base64decode` decodes Base64 encoded string |src| and writes + * the result into |dest|.  |src| should be the pointer to + * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ` + * produced by either `sf_parser_dict`, `sf_parser_list`, + * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, + * otherwise the behavior is undefined. + * + * :member:`dest->base <sf_vec.base>` must point to the buffer that + * has sufficient space to store the decoded byte string. + * + * This function sets the length of decoded byte string to + * :member:`dest->len <sf_vec.len>`. + */ +void sf_base64decode(sf_vec *dest, const sf_vec *src); + +/** + * @function + * + * `sf_pctdecode` decodes percent-encoded string |src| and writes the + * result into |dest|.  |src| should be the pointer to + * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_DISPSTRING` + * produced by either `sf_parser_dict`, `sf_parser_list`, + * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, + * otherwise the behavior is undefined. + * + * :member:`dest->base <sf_vec.base>` must point to the buffer that + * has sufficient space to store the decoded byte string. + * + * This function sets the length of decoded byte string to + * :member:`dest->len <sf_vec.len>`. + */ +void sf_pctdecode(sf_vec *dest, const sf_vec *src); + +#ifdef __cplusplus +} +#endif + +#endif /* SFPARSE_H */ diff --git a/contrib/libs/nghttp3/patches/staticlib.patch b/contrib/libs/nghttp3/patches/staticlib.patch new file mode 100644 index 00000000000..aa0e6d56253 --- /dev/null +++ b/contrib/libs/nghttp3/patches/staticlib.patch @@ -0,0 +1,11 @@ +--- a/lib/includes/nghttp3/nghttp3.h ++++ b/lib/includes/nghttp3/nghttp3.h +@@ -55,7 +55,7 @@ extern "C" { +  + #include <nghttp3/version.h> +  +-#ifdef NGHTTP3_STATICLIB ++#if 1 + #  define NGHTTP3_EXTERN + #elif defined(WIN32) + #  ifdef BUILDING_NGHTTP3 diff --git a/contrib/libs/nghttp3/ya.make b/contrib/libs/nghttp3/ya.make new file mode 100644 index 00000000000..486e365e48c --- /dev/null +++ b/contrib/libs/nghttp3/ya.make @@ -0,0 +1,66 @@ +# Generated by devtools/yamaker from nixpkgs 22.11. + +LIBRARY() + +VERSION(1.6.0) + +ORIGINAL_SOURCE(https://github.com/ngtcp2/nghttp3/releases/download/v1.6.0/nghttp3-1.6.0.tar.xz) + +LICENSE( +    FSFAP AND +    MIT +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( +    contrib/libs/nghttp2 +) + +ADDINCL( +    GLOBAL contrib/libs/nghttp3/lib/includes +    contrib/libs/nghttp3 +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( +    -DBUILDING_NGHTTP3 +    -DHAVE_CONFIG_H +    -DNGHTTP3_STATICLIB +) + +SRCS( +    lib/nghttp3_balloc.c +    lib/nghttp3_buf.c +    lib/nghttp3_conn.c +    lib/nghttp3_conv.c +    lib/nghttp3_debug.c +    lib/nghttp3_err.c +    lib/nghttp3_frame.c +    lib/nghttp3_gaptr.c +    lib/nghttp3_http.c +    lib/nghttp3_idtr.c +    lib/nghttp3_ksl.c +    lib/nghttp3_map.c +    lib/nghttp3_mem.c +    lib/nghttp3_objalloc.c +    lib/nghttp3_opl.c +    lib/nghttp3_pq.c +    lib/nghttp3_qpack.c +    lib/nghttp3_qpack_huffman.c +    lib/nghttp3_qpack_huffman_data.c +    lib/nghttp3_range.c +    lib/nghttp3_rcbuf.c +    lib/nghttp3_ringbuf.c +    lib/nghttp3_str.c +    lib/nghttp3_stream.c +    lib/nghttp3_tnode.c +    lib/nghttp3_unreachable.c +    lib/nghttp3_vec.c +    lib/nghttp3_version.c +) + +END() diff --git a/contrib/libs/ngtcp2/.yandex_meta/001_crypto_includes.patch b/contrib/libs/ngtcp2/.yandex_meta/001_crypto_includes.patch new file mode 100644 index 00000000000..c592c47aef2 --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/001_crypto_includes.patch @@ -0,0 +1,28 @@ +diff -Naur ngtcp2-1.7.0/crypto/quictls/quictls.c ngtcp2-1.7.0_/crypto/quictls/quictls.c +--- ngtcp2-1.7.0/crypto/quictls/quictls.c	2024-08-24 08:46:22.370068166 +0300 ++++ ngtcp2-1.7.0_/crypto/quictls/quictls.c	2024-09-04 21:39:15.679675360 +0300 +@@ -37,10 +37,10 @@ + #include <openssl/rand.h> +  + #if OPENSSL_VERSION_NUMBER >= 0x30000000L +-#  include <openssl/core_names.h> ++# error #include <openssl/core_names.h> + #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ +  +-#include "shared.h" ++#include "ngtcp2_crypto_shared.h" +  + #if OPENSSL_VERSION_NUMBER >= 0x30000000L + static int crypto_initialized; +diff -Naur ngtcp2-1.7.0/crypto/shared.c ngtcp2-1.7.0_/crypto/shared.c +--- ngtcp2-1.7.0/crypto/shared.c	2024-08-24 08:46:22.370068166 +0300 ++++ ngtcp2-1.7.0_/crypto/shared.c	2024-09-04 21:39:30.427538339 +0300 +@@ -22,7 +22,7 @@ +  * 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 "ngtcp2_crypto_shared.h" +  + #ifdef WIN32 + #  include <winsock2.h> diff --git a/contrib/libs/ngtcp2/.yandex_meta/__init__.py b/contrib/libs/ngtcp2/.yandex_meta/__init__.py new file mode 100644 index 00000000000..7c849ca99b4 --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/__init__.py @@ -0,0 +1,31 @@ +from devtools.yamaker import fileutil +from devtools.yamaker.project import CMakeNinjaNixProject + + +def post_install(self): +    for fname in ["quictls/quictls.c", "shared.c", "shared.h"]: +        fileutil.copy([f"{self.srcdir}/crypto/{fname}"], f"{self.dstdir}/lib/") +    for fname in ["quictls.c", "shared.c", "shared.h"]: +        fileutil.rename(f"{self.dstdir}/lib/{fname}", f"ngtcp2_crypto_{fname}") +    for fname in [ +        "crypto/includes/ngtcp2/ngtcp2_crypto.h", +        "crypto/includes/ngtcp2/ngtcp2_crypto_quictls.h", +        "lib/ngtcp2_macro.h", +        "lib/ngtcp2_net.h", +    ]: +        fileutil.copy([f"{self.srcdir}/{fname}"], f"{self.dstdir}/lib/includes/ngtcp2/") +    with self.yamakes["."] as m: +        m.PEERDIR.add("contrib/libs/openssl") +        m.SRCS.add("lib/ngtcp2_crypto_quictls.c") +        m.SRCS.add("lib/ngtcp2_crypto_shared.c") + + +ngtcp2 = CMakeNinjaNixProject( +    license="MIT", +    owners=["g:devtools-contrib", "g:yandex-io"], +    nixattr="ngtcp2", +    arcdir="contrib/libs/ngtcp2", +    disable_includes=["openssl/core_names.h"], +    platform_dispatchers=["config.h"], +    post_install=post_install, +) diff --git a/contrib/libs/ngtcp2/.yandex_meta/devtools.copyrights.report b/contrib/libs/ngtcp2/.yandex_meta/devtools.copyrights.report new file mode 100644 index 00000000000..13037df63cb --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/devtools.copyrights.report @@ -0,0 +1,274 @@ +# File format ($ symbol means the beginning of a line): +# +# $ # this message +# $ # ======================= +# $     # comments (all commentaries should starts with some number of spaces and # symbol) +# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits +# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files +# $ # user comments +# $ +# ${action} {license id} {license text hash} +# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make +# ${all_file_action} filename +# $ # user commentaries (many lines) +# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify) +# ${action} {license spdx} {license text hash} +# $BELONGS ./ya/make/file/relative/path/3/ya.make +# ${all_file_action} filename +# $    #    user commentaries +# $ generated description +# $ ... +# +# You can modify action, all_file_action and add commentaries +# Available actions: +# keep - keep license in contrib and use in credits +# skip - skip license +# remove - remove all files with this license +# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file +# +# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory) +# We suppose that that files can contain some license info +# Available all file actions: +# FILE_IGNORE - ignore file (do nothing) +# FILE_INCLUDE - include all file data into licenses text file +# ======================= + +KEEP     COPYRIGHT_SERVICE_LABEL 0badd8dd1e0fa79a05753bbdaf7b9220 +BELONGS ya.make +    License text: +         * Copyright (c) 2018 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_cc.c [4:4] +        lib/ngtcp2_cc.h [4:4] +        lib/ngtcp2_cid.c [4:4] +        lib/ngtcp2_cid.h [4:4] +        lib/ngtcp2_ksl.c [4:4] +        lib/ngtcp2_ksl.h [4:4] +        lib/ngtcp2_log.c [4:4] +        lib/ngtcp2_log.h [4:4] +        lib/ngtcp2_vec.c [4:4] +        lib/ngtcp2_vec.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL 277b7d2c7033dac98effbf39a0fa11c4 +BELONGS ya.make +    License text: +         * Copyright (c) 2023 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_conn_stat.h [4:4] +        lib/ngtcp2_frame_chain.c [4:4] +        lib/ngtcp2_frame_chain.h [4:4] +        lib/ngtcp2_pktns_id.h [4:4] +        lib/ngtcp2_transport_params.c [4:4] +        lib/ngtcp2_transport_params.h [4:4] +        lib/ngtcp2_tstamp.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL 3782c9399af9718cb85783105b7784ae +BELONGS ya.make +    License text: +           Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +        Software Foundation, Inc. +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        INSTALL [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL 45c763ee42f330d97a4b988bd6ff227d +BELONGS ya.make +    License text: +         * Copyright (c) 2022 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/ngtcp2/ngtcp2_net.h [4:4] +        lib/ngtcp2_balloc.c [4:4] +        lib/ngtcp2_balloc.h [4:4] +        lib/ngtcp2_net.h [4:4] +        lib/ngtcp2_objalloc.c [4:4] +        lib/ngtcp2_objalloc.h [4:4] +        lib/ngtcp2_opl.c [4:4] +        lib/ngtcp2_opl.h [4:4] +        lib/ngtcp2_pmtud.c [4:4] +        lib/ngtcp2_pmtud.h [4:4] +        lib/ngtcp2_unreachable.c [4:4] +        lib/ngtcp2_unreachable.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL 4f1d12ee611509dda7cf5d5cbd920f3a +BELONGS ya.make +    License text: +         * Copyright (c) 2021 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_bbr.c [4:4] +        lib/ngtcp2_bbr.h [4:4] +        lib/ngtcp2_window_filter.c [4:4] +        lib/ngtcp2_window_filter.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL 58664e40c9f8424ff9aaf888f8b2814d +BELONGS ya.make +    License text: +        Copyright (c) 2016 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        COPYING [3:3] +        README.rst [355:355] +        lib/includes/ngtcp2/version.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL 94b9ad611aac02e9c9d2f6105f7ab5f2 +BELONGS ya.make +    License text: +         * Copyright (c) 2024 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_settings.c [4:4] +        lib/ngtcp2_settings.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL b9bce4a8047145ca716cda7c1ba12739 +BELONGS ya.make +    License text: +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2017 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/ngtcp2/ngtcp2.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL bc545ae6368aee638646db325e41a41c +BELONGS ya.make +    License text: +         * Copyright (c) 2017 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/ngtcp2/ngtcp2.h [4:5] +        lib/includes/ngtcp2/ngtcp2_macro.h [4:4] +        lib/ngtcp2_acktr.c [4:4] +        lib/ngtcp2_acktr.h [4:4] +        lib/ngtcp2_buf.c [4:4] +        lib/ngtcp2_buf.h [4:4] +        lib/ngtcp2_conn.c [4:4] +        lib/ngtcp2_conn.h [4:4] +        lib/ngtcp2_conv.c [4:4] +        lib/ngtcp2_conv.h [4:4] +        lib/ngtcp2_crypto.c [4:4] +        lib/ngtcp2_crypto.h [4:4] +        lib/ngtcp2_err.c [4:4] +        lib/ngtcp2_err.h [4:4] +        lib/ngtcp2_gaptr.c [4:4] +        lib/ngtcp2_gaptr.h [4:4] +        lib/ngtcp2_idtr.c [4:4] +        lib/ngtcp2_idtr.h [4:4] +        lib/ngtcp2_macro.h [4:4] +        lib/ngtcp2_map.c [4:5] +        lib/ngtcp2_map.h [4:5] +        lib/ngtcp2_mem.c [4:5] +        lib/ngtcp2_mem.h [4:5] +        lib/ngtcp2_pkt.c [4:4] +        lib/ngtcp2_pkt.h [4:4] +        lib/ngtcp2_ppe.c [4:4] +        lib/ngtcp2_ppe.h [4:4] +        lib/ngtcp2_pq.c [4:5] +        lib/ngtcp2_pq.h [4:5] +        lib/ngtcp2_range.c [4:4] +        lib/ngtcp2_range.h [4:4] +        lib/ngtcp2_ringbuf.c [4:4] +        lib/ngtcp2_ringbuf.h [4:4] +        lib/ngtcp2_rob.c [4:4] +        lib/ngtcp2_rob.h [4:4] +        lib/ngtcp2_rtb.c [4:4] +        lib/ngtcp2_rtb.h [4:4] +        lib/ngtcp2_str.c [4:4] +        lib/ngtcp2_str.h [4:4] +        lib/ngtcp2_strm.c [4:4] +        lib/ngtcp2_strm.h [4:4] + +KEEP     COPYRIGHT_SERVICE_LABEL c812bfe524d8bc74ad8cf40a45902020 +BELONGS ya.make +    License text: +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2012 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_map.c [4:5] +        lib/ngtcp2_map.h [4:5] +        lib/ngtcp2_pq.c [4:5] +        lib/ngtcp2_pq.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL d8ca50c11a8cd311a31e6462c4b42555 +BELONGS ya.make +    License text: +         * Copyright (c) 2017 ngtcp2 contributors +         * Copyright (c) 2014 nghttp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_mem.c [4:5] +        lib/ngtcp2_mem.h [4:5] + +KEEP     COPYRIGHT_SERVICE_LABEL ed9787b1511b75f8534d2b25efbe271c +BELONGS ya.make +    License text: +         * // Copyright (c) 2016 The Chromium Authors. All rights reserved. +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/ngtcp2_window_filter.c [31:31] +        lib/ngtcp2_window_filter.h [31:31] + +KEEP     COPYRIGHT_SERVICE_LABEL f467e93f3d8b0ebd3dc4ff1696f09048 +BELONGS ya.make +    License text: +         * Copyright (c) 2019 ngtcp2 contributors +    Scancode info: +        Original SPDX id: COPYRIGHT_SERVICE_LABEL +        Score           : 100.00 +        Match type      : COPYRIGHT +    Files with this license: +        lib/includes/ngtcp2/ngtcp2_crypto.h [4:4] +        lib/includes/ngtcp2/ngtcp2_crypto_quictls.h [4:4] +        lib/ngtcp2_addr.c [4:4] +        lib/ngtcp2_addr.h [4:4] +        lib/ngtcp2_crypto_quictls.c [4:4] +        lib/ngtcp2_crypto_shared.c [4:4] +        lib/ngtcp2_crypto_shared.h [4:4] +        lib/ngtcp2_path.c [4:4] +        lib/ngtcp2_path.h [4:4] +        lib/ngtcp2_pv.c [4:4] +        lib/ngtcp2_pv.h [4:4] +        lib/ngtcp2_qlog.c [4:4] +        lib/ngtcp2_qlog.h [4:4] +        lib/ngtcp2_rcvry.h [4:4] +        lib/ngtcp2_rst.c [4:4] +        lib/ngtcp2_rst.h [4:4] +        lib/ngtcp2_version.c [4:4] diff --git a/contrib/libs/ngtcp2/.yandex_meta/devtools.licenses.report b/contrib/libs/ngtcp2/.yandex_meta/devtools.licenses.report new file mode 100644 index 00000000000..12434f6ac9c --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/devtools.licenses.report @@ -0,0 +1,202 @@ +# File format ($ symbol means the beginning of a line): +# +# $ # this message +# $ # ======================= +# $     # comments (all commentaries should starts with some number of spaces and # symbol) +# $ IGNORE_FILES {file1.ext1} {file2.ext2} - (optional) ignore listed files when generating license macro and credits +# $ RENAME {original license id} TO {new license id} # user comments - (optional) use {new license id} instead {original license id} in ya.make files +# $ # user comments +# $ +# ${action} {license id} {license text hash} +# $BELONGS ./ya/make/file/relative/path/1/ya.make ./ya/make/2/ya.make +# ${all_file_action} filename +# $ # user commentaries (many lines) +# $ generated description - files with this license, license text... (some number of lines that starts with some number of spaces, do not modify) +# ${action} {license spdx} {license text hash} +# $BELONGS ./ya/make/file/relative/path/3/ya.make +# ${all_file_action} filename +# $    #    user commentaries +# $ generated description +# $ ... +# +# You can modify action, all_file_action and add commentaries +# Available actions: +# keep - keep license in contrib and use in credits +# skip - skip license +# remove - remove all files with this license +# rename - save license text/links into licenses texts file, but not store SPDX into LINCENSE macro. You should store correct license id into devtools.license.spdx.txt file +# +# {all file action} records will be generated when license text contains filename that exists on filesystem (in contrib directory) +# We suppose that that files can contain some license info +# Available all file actions: +# FILE_IGNORE - ignore file (do nothing) +# FILE_INCLUDE - include all file data into licenses text file +# ======================= + +KEEP     MIT                  14395d49cccf7dd9c262788c7c3eb609 +BELONGS ya.make +FILE_INCLUDE AUTHORS found in files: COPYING at line 19 +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        COPYING [5:22] + +KEEP     BSD-3-Clause         720be7eb831bfde6b4599190542bfa9a +BELONGS ya.make +    License text: +         * // Use of this source code is governed by a BSD-style license that can be +         * // found in the LICENSE file. +    Scancode info: +        Original SPDX id: BSD-3-Clause +        Score           : 90.00 +        Match type      : NOTICE +        Links           : http://www.opensource.org/licenses/BSD-3-Clause, https://spdx.org/licenses/BSD-3-Clause +    Files with this license: +        lib/ngtcp2_window_filter.c [32:33] +        lib/ngtcp2_window_filter.h [32:33] + +KEEP     MIT                  8c6b397cbef46628bea401075e15b1d5 +BELONGS ya.make +    License text: +        The MIT License +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : REFERENCE +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        COPYING [1:1] + +KEEP     MIT                  a00cb0c2918046e6e350bbe37dc87fff +BELONGS ya.make +    License text: +        License +        ------- +        The MIT License +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : NOTICE +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        README.rst [350:353] + +KEEP     MIT                  a3a8f7feced3937b87cd090ba748e24b +BELONGS ya.make +FILE_INCLUDE AUTHORS found in files: lib/includes/ngtcp2/ngtcp2.h at line 21, lib/includes/ngtcp2/ngtcp2_crypto.h at line 20, lib/includes/ngtcp2/ngtcp2_crypto_quictls.h at line 20, lib/includes/ngtcp2/ngtcp2_macro.h at line 20, lib/includes/ngtcp2/ngtcp2_net.h at line 20, lib/includes/ngtcp2/version.h at line 20, lib/ngtcp2_acktr.c at line 20, lib/ngtcp2_acktr.h at line 20, lib/ngtcp2_addr.c at line 20, lib/ngtcp2_addr.h at line 20, lib/ngtcp2_balloc.c at line 20, lib/ngtcp2_balloc.h at line 20, lib/ngtcp2_bbr.c at line 20, lib/ngtcp2_bbr.h at line 20, lib/ngtcp2_buf.c at line 20, lib/ngtcp2_buf.h at line 20, lib/ngtcp2_cc.c at line 20, lib/ngtcp2_cc.h at line 20, lib/ngtcp2_cid.c at line 20, lib/ngtcp2_cid.h at line 20, lib/ngtcp2_conn.c at line 20, lib/ngtcp2_conn.h at line 20, lib/ngtcp2_conn_stat.h at line 20, lib/ngtcp2_conv.c at line 20, lib/ngtcp2_conv.h at line 20, lib/ngtcp2_crypto.c at line 20, lib/ngtcp2_crypto.h at line 20, lib/ngtcp2_crypto_quictls.c at line 20, lib/ngtcp2_crypto_shared.c at line 20, lib/ngtcp2_crypto_shared.h at line 20, lib/ngtcp2_err.c at line 20, lib/ngtcp2_err.h at line 20, lib/ngtcp2_frame_chain.c at line 20, lib/ngtcp2_frame_chain.h at line 20, lib/ngtcp2_gaptr.c at line 20, lib/ngtcp2_gaptr.h at line 20, lib/ngtcp2_idtr.c at line 20, lib/ngtcp2_idtr.h at line 20, lib/ngtcp2_ksl.c at line 20, lib/ngtcp2_ksl.h at line 20, lib/ngtcp2_log.c at line 20, lib/ngtcp2_log.h at line 20, lib/ngtcp2_macro.h at line 20, lib/ngtcp2_map.c at line 21, lib/ngtcp2_map.h at line 21, lib/ngtcp2_mem.c at line 21, lib/ngtcp2_mem.h at line 21, lib/ngtcp2_net.h at line 20, lib/ngtcp2_objalloc.c at line 20, lib/ngtcp2_objalloc.h at line 20, lib/ngtcp2_opl.c at line 20, lib/ngtcp2_opl.h at line 20, lib/ngtcp2_path.c at line 20, lib/ngtcp2_path.h at line 20, lib/ngtcp2_pkt.c at line 20, lib/ngtcp2_pkt.h at line 20, lib/ngtcp2_pktns_id.h at line 20, lib/ngtcp2_pmtud.c at line 20, lib/ngtcp2_pmtud.h at line 20, lib/ngtcp2_ppe.c at line 20, lib/ngtcp2_ppe.h at line 20, lib/ngtcp2_pq.c at line 21, lib/ngtcp2_pq.h at line 21, lib/ngtcp2_pv.c at line 20, lib/ngtcp2_pv.h at line 20, lib/ngtcp2_qlog.c at line 20, lib/ngtcp2_qlog.h at line 20, lib/ngtcp2_range.c at line 20, lib/ngtcp2_range.h at line 20, lib/ngtcp2_rcvry.h at line 20, lib/ngtcp2_ringbuf.c at line 20, lib/ngtcp2_ringbuf.h at line 20, lib/ngtcp2_rob.c at line 20, lib/ngtcp2_rob.h at line 20, lib/ngtcp2_rst.c at line 20, lib/ngtcp2_rst.h at line 20, lib/ngtcp2_rtb.c at line 20, lib/ngtcp2_rtb.h at line 20, lib/ngtcp2_settings.c at line 20, lib/ngtcp2_settings.h at line 20, lib/ngtcp2_str.c at line 20, lib/ngtcp2_str.h at line 20, lib/ngtcp2_strm.c at line 20, lib/ngtcp2_strm.h at line 20, lib/ngtcp2_transport_params.c at line 20, lib/ngtcp2_transport_params.h at line 20, lib/ngtcp2_tstamp.h at line 20, lib/ngtcp2_unreachable.c at line 20, lib/ngtcp2_unreachable.h at line 20, lib/ngtcp2_vec.c at line 20, lib/ngtcp2_vec.h at line 20, lib/ngtcp2_version.c at line 20, lib/ngtcp2_window_filter.c at line 20, lib/ngtcp2_window_filter.h at line 20 +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: MIT +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://opensource.org/licenses/mit-license.php, https://spdx.org/licenses/MIT +    Files with this license: +        lib/includes/ngtcp2/ngtcp2.h [7:24] +        lib/includes/ngtcp2/ngtcp2_crypto.h [6:23] +        lib/includes/ngtcp2/ngtcp2_crypto_quictls.h [6:23] +        lib/includes/ngtcp2/ngtcp2_macro.h [6:23] +        lib/includes/ngtcp2/ngtcp2_net.h [6:23] +        lib/includes/ngtcp2/version.h [6:23] +        lib/ngtcp2_acktr.c [6:23] +        lib/ngtcp2_acktr.h [6:23] +        lib/ngtcp2_addr.c [6:23] +        lib/ngtcp2_addr.h [6:23] +        lib/ngtcp2_balloc.c [6:23] +        lib/ngtcp2_balloc.h [6:23] +        lib/ngtcp2_bbr.c [6:23] +        lib/ngtcp2_bbr.h [6:23] +        lib/ngtcp2_buf.c [6:23] +        lib/ngtcp2_buf.h [6:23] +        lib/ngtcp2_cc.c [6:23] +        lib/ngtcp2_cc.h [6:23] +        lib/ngtcp2_cid.c [6:23] +        lib/ngtcp2_cid.h [6:23] +        lib/ngtcp2_conn.c [6:23] +        lib/ngtcp2_conn.h [6:23] +        lib/ngtcp2_conn_stat.h [6:23] +        lib/ngtcp2_conv.c [6:23] +        lib/ngtcp2_conv.h [6:23] +        lib/ngtcp2_crypto.c [6:23] +        lib/ngtcp2_crypto.h [6:23] +        lib/ngtcp2_crypto_quictls.c [6:23] +        lib/ngtcp2_crypto_shared.c [6:23] +        lib/ngtcp2_crypto_shared.h [6:23] +        lib/ngtcp2_err.c [6:23] +        lib/ngtcp2_err.h [6:23] +        lib/ngtcp2_frame_chain.c [6:23] +        lib/ngtcp2_frame_chain.h [6:23] +        lib/ngtcp2_gaptr.c [6:23] +        lib/ngtcp2_gaptr.h [6:23] +        lib/ngtcp2_idtr.c [6:23] +        lib/ngtcp2_idtr.h [6:23] +        lib/ngtcp2_ksl.c [6:23] +        lib/ngtcp2_ksl.h [6:23] +        lib/ngtcp2_log.c [6:23] +        lib/ngtcp2_log.h [6:23] +        lib/ngtcp2_macro.h [6:23] +        lib/ngtcp2_map.c [7:24] +        lib/ngtcp2_map.h [7:24] +        lib/ngtcp2_mem.c [7:24] +        lib/ngtcp2_mem.h [7:24] +        lib/ngtcp2_net.h [6:23] +        lib/ngtcp2_objalloc.c [6:23] +        lib/ngtcp2_objalloc.h [6:23] +        lib/ngtcp2_opl.c [6:23] +        lib/ngtcp2_opl.h [6:23] +        lib/ngtcp2_path.c [6:23] +        lib/ngtcp2_path.h [6:23] +        lib/ngtcp2_pkt.c [6:23] +        lib/ngtcp2_pkt.h [6:23] +        lib/ngtcp2_pktns_id.h [6:23] +        lib/ngtcp2_pmtud.c [6:23] +        lib/ngtcp2_pmtud.h [6:23] +        lib/ngtcp2_ppe.c [6:23] +        lib/ngtcp2_ppe.h [6:23] +        lib/ngtcp2_pq.c [7:24] +        lib/ngtcp2_pq.h [7:24] +        lib/ngtcp2_pv.c [6:23] +        lib/ngtcp2_pv.h [6:23] +        lib/ngtcp2_qlog.c [6:23] +        lib/ngtcp2_qlog.h [6:23] +        lib/ngtcp2_range.c [6:23] +        lib/ngtcp2_range.h [6:23] +        lib/ngtcp2_rcvry.h [6:23] +        lib/ngtcp2_ringbuf.c [6:23] +        lib/ngtcp2_ringbuf.h [6:23] +        lib/ngtcp2_rob.c [6:23] +        lib/ngtcp2_rob.h [6:23] +        lib/ngtcp2_rst.c [6:23] +        lib/ngtcp2_rst.h [6:23] +        lib/ngtcp2_rtb.c [6:23] +        lib/ngtcp2_rtb.h [6:23] +        lib/ngtcp2_settings.c [6:23] +        lib/ngtcp2_settings.h [6:23] +        lib/ngtcp2_str.c [6:23] +        lib/ngtcp2_str.h [6:23] +        lib/ngtcp2_strm.c [6:23] +        lib/ngtcp2_strm.h [6:23] +        lib/ngtcp2_transport_params.c [6:23] +        lib/ngtcp2_transport_params.h [6:23] +        lib/ngtcp2_tstamp.h [6:23] +        lib/ngtcp2_unreachable.c [6:23] +        lib/ngtcp2_unreachable.h [6:23] +        lib/ngtcp2_vec.c [6:23] +        lib/ngtcp2_vec.h [6:23] +        lib/ngtcp2_version.c [6:23] +        lib/ngtcp2_window_filter.c [6:23] +        lib/ngtcp2_window_filter.h [6:23] + +KEEP     FSFAP                d02cc4799cbd521d2aa8c3ff19e655f6 +BELONGS ya.make +    Note: matched license text is too long. Read it in the source files. +    Scancode info: +        Original SPDX id: FSFAP +        Score           : 100.00 +        Match type      : TEXT +        Links           : http://www.gnu.org/prep/maintain/html_node/License-Notices-for-Other-Files.html, https://spdx.org/licenses/FSFAP +    Files with this license: +        INSTALL [7:10] diff --git a/contrib/libs/ngtcp2/.yandex_meta/licenses.list.txt b/contrib/libs/ngtcp2/.yandex_meta/licenses.list.txt new file mode 100644 index 00000000000..ec8c3e3f374 --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/licenses.list.txt @@ -0,0 +1,184 @@ +====================BSD-3-Clause==================== + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + + +====================COPYRIGHT==================== +   Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. + + +====================COPYRIGHT==================== + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + + +====================COPYRIGHT==================== + * Copyright (c) 2017 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2018 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2019 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2021 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2022 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2023 ngtcp2 contributors + + +====================COPYRIGHT==================== + * Copyright (c) 2024 ngtcp2 contributors + + +====================COPYRIGHT==================== +Copyright (c) 2016 ngtcp2 contributors + + +====================FSFAP==================== +   Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved.  This file is offered as-is, +without warranty of any kind. + + +====================File: AUTHORS==================== +Alexis La Goutte +Amir Livneh +Anna Henningsen +Atle Solbakken +Bas van den Berg +Billy Robert O'Neal III +Bruno S Marques +Bryan Call +Cheng Zhao +Daan De Meyer +Daiki Ueno +Daniel Bevenius +Daniel Stenberg +Dave Reisner +Don +Don Olmstead +Frédéric Lécaille +Félix Dagenais +Irina Guberman +James M Snell +Jason Rhinelander +Javier Blazquez +Jay Satiro +Jean-Philippe Boivin +Jiawen Geng +Junqi Wang +Karthikdasari0423 +Kazu Yamamoto +Ken-ichi ICHINO +Kenjiro Nakayama +Lars Eggert +Liang Ma +Marin Rukavina +Mark Chiou +Martin Thomson +Michael White +Moritz Buhl +NKTelnet +Natris +Nishant Nori +Patrick Griffis +Peter Wu +Samuel Henrique +Stefan Eissing +Tal Regev +Tatsuhiro Tsujikawa +Tim Gates +Tomas Mraz +Toni Uhlig +Valère Plantevin +Victor Loh +Viktor Szakats +Your Name +Zizhong Zhang +flx413 +hondaxiao +hyunjic +junqiw +msoxzw +nickfajones +rhoxn +scw00 +shibin k v + + +====================MIT==================== + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +====================MIT==================== +License +------- + +The MIT License + + +====================MIT==================== +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +====================MIT==================== +The MIT License diff --git a/contrib/libs/ngtcp2/.yandex_meta/override.nix b/contrib/libs/ngtcp2/.yandex_meta/override.nix new file mode 100644 index 00000000000..3f250b774b8 --- /dev/null +++ b/contrib/libs/ngtcp2/.yandex_meta/override.nix @@ -0,0 +1,20 @@ +pkgs: attrs: with pkgs; rec { +    name = "ngtcp2"; +    version = "1.8.1"; + +    nativeBuildInputs = [ +      autoreconfHook cmake pkg-config autoconf libtool automake openssl +    ]; + +    # without this nix tries to fetch and build quictls +    buildInputs = [ ]; + +    src = fetchurl { +      url = "https://github.com/ngtcp2/ngtcp2/releases/download/v${version}/ngtcp2-${version}.tar.xz"; +      hash = "sha256-rIRKees/FT5Mzc/szt9CxXqzUruKuS7IrF00F6ec+xE="; +    }; + +    patches = [ +        ./001_crypto_includes.patch +    ]; +} diff --git a/contrib/libs/ngtcp2/AUTHORS b/contrib/libs/ngtcp2/AUTHORS new file mode 100644 index 00000000000..7019dbc8e67 --- /dev/null +++ b/contrib/libs/ngtcp2/AUTHORS @@ -0,0 +1,63 @@ +Alexis La Goutte +Amir Livneh +Anna Henningsen +Atle Solbakken +Bas van den Berg +Billy Robert O'Neal III +Bruno S Marques +Bryan Call +Cheng Zhao +Daan De Meyer +Daiki Ueno +Daniel Bevenius +Daniel Stenberg +Dave Reisner +Don +Don Olmstead +Frédéric Lécaille +Félix Dagenais +Irina Guberman +James M Snell +Jason Rhinelander +Javier Blazquez +Jay Satiro +Jean-Philippe Boivin +Jiawen Geng +Junqi Wang +Karthikdasari0423 +Kazu Yamamoto +Ken-ichi ICHINO +Kenjiro Nakayama +Lars Eggert +Liang Ma +Marin Rukavina +Mark Chiou +Martin Thomson +Michael White +Moritz Buhl +NKTelnet +Natris +Nishant Nori +Patrick Griffis +Peter Wu +Samuel Henrique +Stefan Eissing +Tal Regev +Tatsuhiro Tsujikawa +Tim Gates +Tomas Mraz +Toni Uhlig +Valère Plantevin +Victor Loh +Viktor Szakats +Your Name +Zizhong Zhang +flx413 +hondaxiao +hyunjic +junqiw +msoxzw +nickfajones +rhoxn +scw00 +shibin k v diff --git a/contrib/libs/ngtcp2/COPYING b/contrib/libs/ngtcp2/COPYING new file mode 100644 index 00000000000..9b367cdce71 --- /dev/null +++ b/contrib/libs/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/contrib/libs/ngtcp2/ChangeLog b/contrib/libs/ngtcp2/ChangeLog new file mode 100644 index 00000000000..90501bdbef7 --- /dev/null +++ b/contrib/libs/ngtcp2/ChangeLog @@ -0,0 +1,42 @@ +commit 9accf8d7803f874eaf418ec8988a67aa34ed193e +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-17 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-17 + +    Bump package and library versions + +commit 84833ccc39ce7901c7f41d3d05e2d09c3a66e71a +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-12 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-17 + +    Add test for ACK padding + +commit 29209046dc3cbba4ccad36062dfa54ac34b8ffc3 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-11 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-17 + +    Fix minimum packet size computation +     +    The Connection ID used in minimum packet size computation should be +    Connection ID length that a local endpoint is generated and sends to a +    remote endpoint. +     +    Merge ngtcp2_ppe_padding_hp_sample to ngtcp2_ppe_padding_size to +    ensure that minimum packet size is always honored. + +commit 2683ec782e9bdb7a1d6800d611a7a8f87dff8219 +Author:     Tatsuhiro Tsujikawa <[email protected]> +AuthorDate: 2024-10-11 +Commit:     Tatsuhiro Tsujikawa <[email protected]> +CommitDate: 2024-10-17 + +    Do not count PADDING to CWND if it is added to make minimum sized packet +     +    Otherwise, we ended up cwnd-limited situation, and if ACKs from remote +    is lost frequently, local endpoint is unable to send any ack eliciting +    packet. diff --git a/contrib/libs/ngtcp2/INSTALL b/contrib/libs/ngtcp2/INSTALL new file mode 100644 index 00000000000..e82fd21de2e --- /dev/null +++ b/contrib/libs/ngtcp2/INSTALL @@ -0,0 +1,368 @@ +Installation Instructions +************************* + +   Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free +Software Foundation, Inc. + +   Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved.  This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + +   Briefly, the shell command './configure && make && make install' +should configure, build, and install this package.  The following +more-detailed instructions are generic; see the 'README' file for +instructions specific to this package.  Some packages provide this +'INSTALL' file but do not implement all of the features documented +below.  The lack of an optional feature in a given package is not +necessarily a bug.  More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + +   The 'configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation.  It uses +those values to create a 'Makefile' in each directory of the package. +It may also create one or more '.h' files containing system-dependent +definitions.  Finally, it creates a shell script 'config.status' that +you can run in the future to recreate the current configuration, and a +file 'config.log' containing compiler output (useful mainly for +debugging 'configure'). + +   It can also use an optional file (typically called 'config.cache' and +enabled with '--cache-file=config.cache' or simply '-C') that saves the +results of its tests to speed up reconfiguring.  Caching is disabled by +default to prevent problems with accidental use of stale cache files. + +   If you need to do unusual things to compile the package, please try +to figure out how 'configure' could check whether to do them, and mail +diffs or instructions to the address given in the 'README' so they can +be considered for the next release.  If you are using the cache, and at +some point 'config.cache' contains results you don't want to keep, you +may remove or edit it. + +   The file 'configure.ac' (or 'configure.in') is used to create +'configure' by a program called 'autoconf'.  You need 'configure.ac' if +you want to change it or regenerate 'configure' using a newer version of +'autoconf'. + +   The simplest way to compile this package is: + +  1. 'cd' to the directory containing the package's source code and type +     './configure' to configure the package for your system. + +     Running 'configure' might take a while.  While running, it prints +     some messages telling which features it is checking for. + +  2. Type 'make' to compile the package. + +  3. Optionally, type 'make check' to run any self-tests that come with +     the package, generally using the just-built uninstalled binaries. + +  4. Type 'make install' to install the programs and any data files and +     documentation.  When installing into a prefix owned by root, it is +     recommended that the package be configured and built as a regular +     user, and only the 'make install' phase executed with root +     privileges. + +  5. Optionally, type 'make installcheck' to repeat any self-tests, but +     this time using the binaries in their final installed location. +     This target does not install anything.  Running this target as a +     regular user, particularly if the prior 'make install' required +     root privileges, verifies that the installation completed +     correctly. + +  6. You can remove the program binaries and object files from the +     source code directory by typing 'make clean'.  To also remove the +     files that 'configure' created (so you can compile the package for +     a different kind of computer), type 'make distclean'.  There is +     also a 'make maintainer-clean' target, but that is intended mainly +     for the package's developers.  If you use it, you may have to get +     all sorts of other programs in order to regenerate files that came +     with the distribution. + +  7. Often, you can also type 'make uninstall' to remove the installed +     files again.  In practice, not all packages have tested that +     uninstallation works correctly, even though it is required by the +     GNU Coding Standards. + +  8. Some packages, particularly those that use Automake, provide 'make +     distcheck', which can by used by developers to test that all other +     targets like 'make install' and 'make uninstall' work correctly. +     This target is generally not run by end users. + +Compilers and Options +===================== + +   Some systems require unusual options for compilation or linking that +the 'configure' script does not know about.  Run './configure --help' +for details on some of the pertinent environment variables. + +   You can give 'configure' initial values for configuration parameters +by setting variables in the command line or in the environment.  Here is +an example: + +     ./configure CC=c99 CFLAGS=-g LIBS=-lposix + +   *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +   You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory.  To do this, you can use GNU 'make'.  'cd' to the +directory where you want the object files and executables to go and run +the 'configure' script.  'configure' automatically checks for the source +code in the directory that 'configure' is in and in '..'.  This is known +as a "VPATH" build. + +   With a non-GNU 'make', it is safer to compile the package for one +architecture at a time in the source code directory.  After you have +installed the package for one architecture, use 'make distclean' before +reconfiguring for another architecture. + +   On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple '-arch' options to the +compiler but only a single '-arch' option to the preprocessor.  Like +this: + +     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ +                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ +                 CPP="gcc -E" CXXCPP="g++ -E" + +   This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the 'lipo' tool if you have problems. + +Installation Names +================== + +   By default, 'make install' installs the package's commands under +'/usr/local/bin', include files under '/usr/local/include', etc.  You +can specify an installation prefix other than '/usr/local' by giving +'configure' the option '--prefix=PREFIX', where PREFIX must be an +absolute file name. + +   You can specify separate installation prefixes for +architecture-specific files and architecture-independent files.  If you +pass the option '--exec-prefix=PREFIX' to 'configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + +   In addition, if you use an unusual directory layout you can give +options like '--bindir=DIR' to specify different values for particular +kinds of files.  Run 'configure --help' for a list of the directories +you can set and what kinds of files go in them.  In general, the default +for these options is expressed in terms of '${prefix}', so that +specifying just '--prefix' will affect all of the other directory +specifications that were not explicitly provided. + +   The most portable way to affect installation locations is to pass the +correct locations to 'configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +'make install' command line to change installation locations without +having to reconfigure or recompile. + +   The first method involves providing an override variable for each +affected directory.  For example, 'make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +'${prefix}'.  Any directories that were specified during 'configure', +but not in terms of '${prefix}', must each be overridden at install time +for the entire installation to be relocated.  The approach of makefile +variable overrides for each directory variable is required by the GNU +Coding Standards, and ideally causes no recompilation.  However, some +platforms have known limitations with the semantics of shared libraries +that end up requiring recompilation when using this method, particularly +noticeable in packages that use GNU Libtool. + +   The second method involves providing the 'DESTDIR' variable.  For +example, 'make install DESTDIR=/alternate/directory' will prepend +'/alternate/directory' before all installation names.  The approach of +'DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters.  On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of '${prefix}' +at 'configure' time. + +Optional Features +================= + +   If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving 'configure' the +option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. + +   Some packages pay attention to '--enable-FEATURE' options to +'configure', where FEATURE indicates an optional part of the package. +They may also pay attention to '--with-PACKAGE' options, where PACKAGE +is something like 'gnu-as' or 'x' (for the X Window System).  The +'README' should mention any '--enable-' and '--with-' options that the +package recognizes. + +   For packages that use the X Window System, 'configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the 'configure' options '--x-includes=DIR' and +'--x-libraries=DIR' to specify their locations. + +   Some packages offer the ability to configure how verbose the +execution of 'make' will be.  For these packages, running './configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with 'make V=1'; while running './configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with 'make V=0'. + +Particular systems +================== + +   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU CC +is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + +     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + +   HP-UX 'make' updates targets which have the same timestamps as their +prerequisites, which makes it generally unusable when shipped generated +files such as 'configure' are involved.  Use GNU 'make' instead. + +   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its '<wchar.h>' header file.  The option '-nodtk' can be used as a +workaround.  If GNU CC is not installed, it is therefore recommended to +try + +     ./configure CC="cc" + +and if that doesn't work, try + +     ./configure CC="cc -nodtk" + +   On Solaris, don't put '/usr/ucb' early in your 'PATH'.  This +directory contains several dysfunctional programs; working variants of +these programs are available in '/usr/bin'.  So, if you need '/usr/ucb' +in your 'PATH', put it _after_ '/usr/bin'. + +   On Haiku, software installed for all users goes in '/boot/common', +not '/usr/local'.  It is recommended to use the following options: + +     ./configure --prefix=/boot/common + +Specifying the System Type +========================== + +   There may be some features 'configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on.  Usually, assuming the package is built to be run on the +_same_ architectures, 'configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +'--build=TYPE' option.  TYPE can either be a short name for the system +type, such as 'sun4', or a canonical name which has the form: + +     CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + +     OS +     KERNEL-OS + +   See the file 'config.sub' for the possible values of each field.  If +'config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + +   If you are _building_ compiler tools for cross-compiling, you should +use the option '--target=TYPE' to select the type of system they will +produce code for. + +   If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with '--host=TYPE'. + +Sharing Defaults +================ + +   If you want to set default values for 'configure' scripts to share, +you can create a site shell script called 'config.site' that gives +default values for variables like 'CC', 'cache_file', and 'prefix'. +'configure' looks for 'PREFIX/share/config.site' if it exists, then +'PREFIX/etc/config.site' if it exists.  Or, you can set the +'CONFIG_SITE' environment variable to the location of the site script. +A warning: not all 'configure' scripts look for a site script. + +Defining Variables +================== + +   Variables not defined in a site shell script can be set in the +environment passed to 'configure'.  However, some packages may run +configure again during the build, and the customized values of these +variables may be lost.  In order to avoid this problem, you should set +them in the 'configure' command line, using 'VAR=value'.  For example: + +     ./configure CC=/usr/local2/bin/gcc + +causes the specified 'gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an +Autoconf limitation.  Until the limitation is lifted, you can use this +workaround: + +     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +'configure' Invocation +====================== + +   'configure' recognizes the following options to control how it +operates. + +'--help' +'-h' +     Print a summary of all of the options to 'configure', and exit. + +'--help=short' +'--help=recursive' +     Print a summary of the options unique to this package's +     'configure', and exit.  The 'short' variant lists options used only +     in the top level, while the 'recursive' variant lists options also +     present in any nested packages. + +'--version' +'-V' +     Print the version of Autoconf used to generate the 'configure' +     script, and exit. + +'--cache-file=FILE' +     Enable the cache: use and save the results of the tests in FILE, +     traditionally 'config.cache'.  FILE defaults to '/dev/null' to +     disable caching. + +'--config-cache' +'-C' +     Alias for '--cache-file=config.cache'. + +'--quiet' +'--silent' +'-q' +     Do not print messages saying which checks are being made.  To +     suppress all normal output, redirect it to '/dev/null' (any error +     messages will still be shown). + +'--srcdir=DIR' +     Look for the package's source code in directory DIR.  Usually +     'configure' can determine that directory automatically. + +'--prefix=DIR' +     Use DIR as the installation prefix.  *note Installation Names:: for +     more details, including other options available for fine-tuning the +     installation locations. + +'--no-create' +'-n' +     Run the configure checks, but stop before creating any output +     files. + +'configure' also accepts some other, not widely useful, options.  Run +'configure --help' for more details. diff --git a/contrib/libs/ngtcp2/README b/contrib/libs/ngtcp2/README new file mode 100644 index 00000000000..5ccc0ea36b5 --- /dev/null +++ b/contrib/libs/ngtcp2/README @@ -0,0 +1 @@ +See README.rst diff --git a/contrib/libs/ngtcp2/README.rst b/contrib/libs/ngtcp2/README.rst new file mode 100644 index 00000000000..5d6990ff25b --- /dev/null +++ b/contrib/libs/ngtcp2/README.rst @@ -0,0 +1,355 @@ +ngtcp2 +====== + +"Call it TCP/2.  One More Time." + +ngtcp2 project is an effort to implement `RFC9000 +<https://datatracker.ietf.org/doc/html/rfc9000>`_ QUIC protocol. + +Documentation +------------- + +`Online documentation <https://nghttp2.org/ngtcp2/>`_ is available. + +Public test server +------------------ + +The following endpoints are available to try out ngtcp2 +implementation: + +- https://nghttp2.org:4433 +- https://nghttp2.org:4434 (requires address validation token) +- https://nghttp2.org (powered by `nghttpx +  <https://nghttp2.org/documentation/nghttpx.1.html>`_) + +  This endpoints sends Alt-Svc header field to clients if it is +  accessed via HTTP/1.1 or HTTP/2 to tell them that HTTP/3 is +  available at UDP 443. + +Requirements +------------ + +The libngtcp2 C library itself does not depend on any external +libraries.  The example client, and server are written in C++20, and +should compile with the modern C++ compilers (e.g., clang >= 11.0, or +gcc >= 11.0). + +The following packages are required to configure the build system: + +- pkg-config >= 0.20 +- autoconf +- automake +- autotools-dev +- libtool + +To build sources under the examples directory, libev and nghttp3 are +required: + +- libev +- `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ for HTTP/3 + +To enable `TLS Certificate Compression +<https://datatracker.ietf.org/doc/html/rfc8879>`_ in bsslclient and +bsslserver (BoringSSL (aws-lc) examples client and server), the +following library is required: + +- libbrotli-dev >= 1.0.9 + +ngtcp2 crypto helper library, and client and server under examples +directory require at least one of the following TLS backends: + +- `quictls +  <https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic>`_ +- GnuTLS >= 3.7.5 +- BoringSSL (commit 76968bb3d53982560bcf08bcd0ba3e1865fe15cd); +  or aws-lc >= 1.19.0 +- Picotls (commit 89fe56f4d79200a5801a08ed3b6ac8322e01ccd5) +- wolfSSL >= 5.5.0 +- LibreSSL >= v3.9.2 + +Before building from git +------------------------ + +When build from git, run the following command to pull submodules: + +.. code-block:: shell + +   $ git submodule update --init + +Build with wolfSSL +------------------ + +.. code-block:: shell + +   $ git clone --depth 1 -b v5.7.2-stable https://github.com/wolfSSL/wolfssl +   $ cd wolfssl +   $ autoreconf -i +   $ # For wolfSSL < v5.6.6, append --enable-quic. +   $ ./configure --prefix=$PWD/build \ +       --enable-all --enable-aesni --enable-harden --enable-keylog-export \ +       --disable-ech +   $ make -j$(nproc) +   $ make install +   $ cd .. +   $ git clone --recursive https://github.com/ngtcp2/nghttp3 +   $ cd nghttp3 +   $ autoreconf -i +   $ ./configure --prefix=$PWD/build --enable-lib-only +   $ make -j$(nproc) check +   $ make install +   $ cd .. +   $ git clone --recursive https://github.com/ngtcp2/ngtcp2 +   $ cd ngtcp2 +   $ autoreconf -i +   $ # For Mac users who have installed libev with MacPorts, append +   $ # LIBEV_CFLAGS="-I/opt/local/include" LIBEV_LIBS="-L/opt/local/lib -lev" +   $ ./configure PKG_CONFIG_PATH=$PWD/../wolfssl/build/lib/pkgconfig:$PWD/../nghttp3/build/lib/pkgconfig \ +       --with-wolfssl +   $ make -j$(nproc) check + +Build with BoringSSL +-------------------- + +.. code-block:: shell + +   $ git clone https://boringssl.googlesource.com/boringssl +   $ cd boringssl +   $ git checkout 76968bb3d53982560bcf08bcd0ba3e1865fe15cd +   $ cmake -B build -DCMAKE_POSITION_INDEPENDENT_CODE=ON +   $ make -j$(nproc) -C build +   $ cd .. +   $ git clone --recursive https://github.com/ngtcp2/nghttp3 +   $ cd nghttp3 +   $ autoreconf -i +   $ ./configure --prefix=$PWD/build --enable-lib-only +   $ make -j$(nproc) check +   $ make install +   $ cd .. +   $ git clone --recursive  https://github.com/ngtcp2/ngtcp2 +   $ cd ngtcp2 +   $ autoreconf -i +   $ # For Mac users who have installed libev with MacPorts, append +   $ # LIBEV_CFLAGS="-I/opt/local/include" LIBEV_LIBS="-L/opt/local/lib -lev" +   $ ./configure PKG_CONFIG_PATH=$PWD/../nghttp3/build/lib/pkgconfig \ +       BORINGSSL_LIBS="-L$PWD/../boringssl/build/ssl -lssl -L$PWD/../boringssl/build/crypto -lcrypto" \ +       BORINGSSL_CFLAGS="-I$PWD/../boringssl/include" \ +       --with-boringssl +   $ make -j$(nproc) check + +Build with aws-lc +----------------- + +.. code-block:: shell + +   $ git clone --depth 1 -b v1.36.1 https://github.com/aws/aws-lc +   $ cd aws-lc +   $ cmake -B build -DDISABLE_GO=ON +   $ make -j$(nproc) -C build +   $ cd .. +   $ git clone --recursive https://github.com/ngtcp2/nghttp3 +   $ cd nghttp3 +   $ autoreconf -i +   $ ./configure --prefix=$PWD/build --enable-lib-only +   $ make -j$(nproc) check +   $ make install +   $ cd .. +   $ git clone --recursive  https://github.com/ngtcp2/ngtcp2 +   $ cd ngtcp2 +   $ autoreconf -i +   $ # For Mac users who have installed libev with MacPorts, append +   $ # LIBEV_CFLAGS="-I/opt/local/include" LIBEV_LIBS="-L/opt/local/lib -lev" +   $ ./configure PKG_CONFIG_PATH=$PWD/../nghttp3/build/lib/pkgconfig \ +       BORINGSSL_CFLAGS="-I$PWD/../aws-lc/include" \ +       BORINGSSL_LIBS="-L$PWD/../aws-lc/build/ssl -lssl -L$PWD/../aws-lc/build/crypto -lcrypto" \ +       --with-boringssl +   $ make -j$(nproc) check + +Build with libressl +----------------- + +.. code-block:: shell + +   $ git clone --depth 1 -b v3.9.2 https://github.com/libressl/portable.git libressl +   $ cd libressl +   $ ./autogen.sh +   $ ./configure --prefix=$PWD/build +   $ make -j$(nproc) install +   $ cd .. +   $ git clone --recursive https://github.com/ngtcp2/nghttp3 +   $ cd nghttp3 +   $ autoreconf -i +   $ ./configure --prefix=$PWD/build --enable-lib-only +   $ make -j$(nproc) check +   $ make install +   $ cd .. +   $ git clone --recursive  https://github.com/ngtcp2/ngtcp2 +   $ cd ngtcp2 +   $ autoreconf -i +   $ # For Mac users who have installed libev with MacPorts, append +   $ # LIBEV_CFLAGS="-I/opt/homebrew/Cellar/libev/4.33/include" LIBEV_LIBS="-L/opt/homebrew/Cellar/libev/4.33/lib -lev" +   $ ./configure PKG_CONFIG_PATH=$PWD/../nghttp3/build/lib/pkgconfig:$PWD/../libressl/build/lib/pkgconfig +   $ make -j$(nproc) check + +Client/Server +------------- + +After successful build, the client and server executable should be +found under examples directory.  They talk HTTP/3. + +Client +~~~~~~ + +.. code-block:: shell + +   $ examples/wsslclient [OPTIONS] <HOST> <PORT> [<URI>...] + +The notable options are: + +- ``-d``, ``--data=<PATH>``: Read data from <PATH> and send it to a +  peer. + +Server +~~~~~~ + +.. code-block:: shell + +   $ examples/wsslserver [OPTIONS] <ADDR> <PORT> <PRIVATE_KEY_FILE> <CERTIFICATE_FILE> + +The notable options are: + +- ``-V``, ``--validate-addr``: Enforce stateless address validation. + +H09wsslclient/H09wsslserver +--------------------------- + +There are h09wsslclient and h09wsslserver which speak HTTP/0.9.  They +are written just for `quic-interop-runner +<https://github.com/marten-seemann/quic-interop-runner>`_.  They share +the basic functionalities with HTTP/3 client and server but have less +functions (e.g., h09wsslclient does not have a capability to send +request body, and h09wsslserver does not understand numeric request +path, like /1000). + +Resumption and 0-RTT +-------------------- + +In order to resume a session, a session ticket, and a transport +parameters must be fetched from server.  First, run +examples/wsslclient with --session-file, and --tp-file options which +specify a path to session ticket, and transport parameter files +respectively to save them locally. + +Once these files are available, run examples/wsslclient with the same +arguments again.  You will see that session is resumed in your log if +resumption succeeds.  Resuming session makes server's first Handshake +packet pretty small because it does not send its certificates. + +To send 0-RTT data, after making sure that resumption works, use -d +option to specify a file which contains data to send. + +Token (Not something included in Retry packet) +---------------------------------------------- + +QUIC server might send a token to client after connection has been +established.  Client can send this token in subsequent connection to +the server.  Server verifies the token and if it succeeds, the address +validation completes and lifts some restrictions on server which might +speed up transfer.  In order to save and/or load a token, +use --token-file option of examples/wsslclient.  The given file is +overwritten if it already exists when storing a token. + +Crypto helper library +--------------------- + +In order to make TLS stack integration less painful, we provide a +crypto helper library which offers the basic crypto operations. + +The header file exists under crypto/includes/ngtcp2 directory. + +Each library file is built for a particular TLS backend.  The +available crypto helper libraries are: + +- libngtcp2_crypto_quictls: Use quictls and libressl as TLS backend +- libngtcp2_crypto_gnutls: Use GnuTLS as TLS backend +- libngtcp2_crypto_boringssl: Use BoringSSL and aws-lc as TLS backend +- libngtcp2_crypto_picotls: Use Picotls as TLS backend +- libngtcp2_crypto_wolfssl: Use wolfSSL as TLS backend + +Because BoringSSL and Picotls are an unversioned product, we only +tested their particular revision.  See Requirements section above. + +We use Picotls with OpenSSL as crypto backend. + +The examples directory contains client and server that are linked to +those crypto helper libraries and TLS backends.  They are only built +if their corresponding crypto helper library is built: + +- qtlsclient: quictls(libressl) client +- qtlsserver: quictls(libressl) server +- gtlsclient: GnuTLS client +- gtlsserver: GnuTLS server +- bsslclient: BoringSSL(aws-lc) client +- bsslserver: BoringSSL(aws-lc) server +- ptlsclient: Picotls client +- ptlsserver: Picotls server +- wsslclient: wolfSSL client +- wsslserver: wolfSSL server + +QUIC protocol extensions +------------------------- + +The library implements the following QUIC protocol extensions: + +- `An Unreliable Datagram Extension to QUIC +  <https://datatracker.ietf.org/doc/html/rfc9221>`_ +- `Greasing the QUIC Bit +  <https://datatracker.ietf.org/doc/html/rfc9287>`_ +- `Compatible Version Negotiation for QUIC +  <https://datatracker.ietf.org/doc/html/rfc9368>`_ +- `QUIC Version 2 +  <https://datatracker.ietf.org/doc/html/rfc9369>`_ + +Configuring Wireshark for QUIC +------------------------------ + +`Wireshark <https://www.wireshark.org/download.html>`_ can be configured to +analyze QUIC traffic using the following steps: + +1. Set *SSLKEYLOGFILE* environment variable: + +   .. code-block:: shell + +      $ export SSLKEYLOGFILE=quic_keylog_file + +2. Set the port that QUIC uses + +   Go to *Preferences->Protocols->QUIC* and set the port the program +   listens to.  In the case of the example application this would be +   the port specified on the command line. + +3. Set Pre-Master-Secret logfile + +   Go to *Preferences->Protocols->TLS* and set the *Pre-Master-Secret +   log file* to the same value that was specified for *SSLKEYLOGFILE*. + +4. Choose the correct network interface for capturing + +   Make sure you choose the correct network interface for +   capturing. For example, if using localhost choose the *loopback* +   network interface on macos. + +5. Create a filter + +   Create A filter for the udp.port and set the port to the port the +   application is listening to. For example: + +   .. code-block:: text + +      udp.port == 7777 + +License +------- + +The MIT License + +Copyright (c) 2016 ngtcp2 contributors diff --git a/contrib/libs/ngtcp2/config-android.h b/contrib/libs/ngtcp2/config-android.h new file mode 100644 index 00000000000..135bd531fd7 --- /dev/null +++ b/contrib/libs/ngtcp2/config-android.h @@ -0,0 +1,3 @@ +#include "config-linux.h" + +#undef HAVE_EXPLICIT_BZERO diff --git a/contrib/libs/ngtcp2/config-ios.h b/contrib/libs/ngtcp2/config-ios.h new file mode 100644 index 00000000000..06b8ef5651d --- /dev/null +++ b/contrib/libs/ngtcp2/config-ios.h @@ -0,0 +1,4 @@ +#include "config-linux.h" +#include "config-osx.h" + +#undef HAVE_EXPLICIT_BZERO diff --git a/contrib/libs/ngtcp2/config-linux.h b/contrib/libs/ngtcp2/config-linux.h new file mode 100644 index 00000000000..51bc0edb000 --- /dev/null +++ b/contrib/libs/ngtcp2/config-linux.h @@ -0,0 +1,55 @@ + +/* Define to `int' if <sys/types.h> does not define. */ +/* #undef ssize_t */ + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the <netinet/ip.h> header file. */ +#define HAVE_NETINET_IP_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the <sys/endian.h> header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the <endian.h> header file. */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the <byteswap.h> header file. */ +#define HAVE_BYTESWAP_H 1 + +/* Define to 1 if you have the <asm/types.h> header file. */ +#define HAVE_ASM_TYPES_H 1 + +/* Define to 1 if you have the <linux/netlink.h> header file. */ +#define HAVE_LINUX_NETLINK_H 1 + +/* Define to 1 if you have the <linux/rtnetlink.h> header file. */ +#define HAVE_LINUX_RTNETLINK_H 1 + +/* Define to 1 if you have the `be64toh' function, otherwise 0. */ +#define HAVE_DECL_BE64TOH 1 + +/* Define to 1 if you have the `bswap_64' function, otherwise 0. */ +#define HAVE_DECL_BSWAP_64 1 + +/* Define WORDS_BIGENDIAN to 1 if target architecture is big +   endian. */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to 1 if you have `libbrotlienc` and `libbrotlidec` libraries. */ +/* #undef HAVE_LIBBROTLI */ + +/* Define to 1 if you have the `explicit_bzero' function. */ +#define HAVE_EXPLICIT_BZERO 1 + +/* Define to 1 if you have the `memset_s' function. */ +/* #undef HAVE_MEMSET_S */ diff --git a/contrib/libs/ngtcp2/config-osx.h b/contrib/libs/ngtcp2/config-osx.h new file mode 100644 index 00000000000..8dd585cd9d8 --- /dev/null +++ b/contrib/libs/ngtcp2/config-osx.h @@ -0,0 +1,9 @@ +#include "config-linux.h" + +#undef HAVE_ENDIAN_H + +#undef HAVE_BYTESWAP_H + +#undef HAVE_DECL_BE64TOH + +#undef HAVE_DECL_BSWAP_64 diff --git a/contrib/libs/ngtcp2/config-win.h b/contrib/libs/ngtcp2/config-win.h new file mode 100644 index 00000000000..3b2f5e3dbb1 --- /dev/null +++ b/contrib/libs/ngtcp2/config-win.h @@ -0,0 +1,15 @@ +#include "config-linux.h" + +#undef HAVE_ARPA_INET_H + +#undef HAVE_NETINET_IN_H + +#undef HAVE_UNISTD_H + +#undef HAVE_ENDIAN_H + +#undef HAVE_BYTESWAP_H + +#undef HAVE_DECL_BE64TOH + +#undef HAVE_DECL_BSWAP_64 diff --git a/contrib/libs/ngtcp2/config.h b/contrib/libs/ngtcp2/config.h new file mode 100644 index 00000000000..4c1d2fe53fe --- /dev/null +++ b/contrib/libs/ngtcp2/config.h @@ -0,0 +1,13 @@ +#pragma once + +#if defined(__ANDROID__) +#   include "config-android.h" +#elif defined(__IOS__) +#   include "config-ios.h" +#elif defined(__APPLE__) +#   include "config-osx.h" +#elif defined(_MSC_VER) +#   include "config-win.h" +#else +#   include "config-linux.h" +#endif diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h new file mode 100644 index 00000000000..680f5a2eeb0 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -0,0 +1,5915 @@ +/* + * 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 /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ + +#ifdef _MSC_VER +#  pragma warning(push) +#  pragma warning(disable : 4324) +#endif /* defined(_MSC_VER) */ + +#include <stdlib.h> +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 +   compliant.  See compiler macros and version number in +   https://sourceforge.net/p/predef/wiki/Compilers/ */ +#  include <stdint.h> +#else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ +#  include <inttypes.h> +#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ +#include <sys/types.h> +#include <stdarg.h> +#include <stddef.h> + +#ifndef NGTCP2_USE_GENERIC_SOCKADDR +#  ifdef WIN32 +#    ifndef WIN32_LEAN_AND_MEAN +#      define WIN32_LEAN_AND_MEAN +#    endif /* !defined(WIN32_LEAN_AND_MEAN) */ +#    include <ws2tcpip.h> +#  else /* !defined(WIN32) */ +#    include <sys/socket.h> +#    include <netinet/in.h> +#  endif /* !defined(WIN32) */ +#endif   /* !defined(NGTCP2_USE_GENERIC_SOCKADDR) */ + +#include <ngtcp2/version.h> + +#if 1 +#  define NGTCP2_EXTERN +#elif defined(WIN32) +#  ifdef BUILDING_NGTCP2 +#    define NGTCP2_EXTERN __declspec(dllexport) +#  else /* !defined(BUILDING_NGTCP2) */ +#    define NGTCP2_EXTERN __declspec(dllimport) +#  endif /* !defined(BUILDING_NGTCP2) */ +#else    /* !(defined(NGTCP2_STATICLIB) || defined(WIN32)) */ +#  ifdef BUILDING_NGTCP2 +#    define NGTCP2_EXTERN __attribute__((visibility("default"))) +#  else /* !defined(BUILDING_NGTCP2) */ +#    define NGTCP2_EXTERN +#  endif /* !defined(BUILDING_NGTCP2) */ +#endif   /* !(defined(NGTCP2_STATICLIB) || defined(WIN32)) */ + +#ifdef _MSC_VER +#  define NGTCP2_ALIGN(N) __declspec(align(N)) +#else /* !defined(_MSC_VER) */ +#  define NGTCP2_ALIGN(N) __attribute__((aligned(N))) +#endif /* !defined(_MSC_VER) */ + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/** + * @typedef + * + * :type:`ngtcp2_ssize` is signed counterpart of size_t. + */ +typedef ptrdiff_t ngtcp2_ssize; + +/** + * @functypedef + * + * :type:`ngtcp2_malloc` is a custom memory allocator to replace + * :manpage:`malloc(3)`.  The |user_data| is + * :member:`ngtcp2_mem.user_data`. + */ +typedef void *(*ngtcp2_malloc)(size_t size, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_free` is a custom memory allocator to replace + * :manpage:`free(3)`.  The |user_data| is + * :member:`ngtcp2_mem.user_data`. + */ +typedef void (*ngtcp2_free)(void *ptr, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_calloc` is a custom memory allocator to replace + * :manpage:`calloc(3)`.  The |user_data| is the + * :member:`ngtcp2_mem.user_data`. + */ +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_realloc` is a custom memory allocator to replace + * :manpage:`realloc(3)`.  The |user_data| is the + * :member:`ngtcp2_mem.user_data`. + */ +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data); + +/** + * @struct + * + * :type:`ngtcp2_mem` is a custom memory allocator.  The + * :member:`user_data` field is passed to each allocator function. + * This can be used, for example, to achieve per-connection memory + * pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`, + * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively:: + * + *     void *my_malloc_cb(size_t size, void *user_data) { + *       (void)user_data; + *       return my_malloc(size); + *     } + * + *     void my_free_cb(void *ptr, void *user_data) { + *       (void)user_data; + *       my_free(ptr); + *     } + * + *     void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) { + *       (void)user_data; + *       return my_calloc(nmemb, size); + *     } + * + *     void *my_realloc_cb(void *ptr, size_t size, void *user_data) { + *       (void)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 { +  /** +   * :member:`user_data` is an arbitrary user supplied data.  This +   * is passed to each allocator function. +   */ +  void *user_data; +  /** +   * :member:`malloc` is a custom allocator function to replace +   * :manpage:`malloc(3)`. +   */ +  ngtcp2_malloc malloc; +  /** +   * :member:`free` is a custom allocator function to replace +   * :manpage:`free(3)`. +   */ +  ngtcp2_free free; +  /** +   * :member:`calloc` is a custom allocator function to replace +   * :manpage:`calloc(3)`. +   */ +  ngtcp2_calloc calloc; +  /** +   * :member:`realloc` is a custom allocator function to replace +   * :manpage:`realloc(3)`. +   */ +  ngtcp2_realloc realloc; +} ngtcp2_mem; + +/** + * @macrosection + * + * Time related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 + * second. + */ +#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds + * to 1 millisecond. + */ +#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds + * to 1 microsecond. + */ +#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to + * 1 nanosecond. + */ +#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) + +/** + * @macrosection + * + * QUIC protocol version macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1. + */ +#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u) + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V2` is the QUIC version 2.  See + * :rfc:`9369`. + */ +#define NGTCP2_PROTO_VER_V2 ((uint32_t)0x6b3343cfu) + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_MAX` is the highest QUIC version that this + * library supports.  Deprecated since v1.1.0. + */ +#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER_V1 + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_MIN` is the lowest QUIC version that this + * library supports.  Deprecated since v1.1.0. + */ +#define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_V1 + +/** + * @macro + * + * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved + * version. + */ +#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au + +/** + * @macrosection + * + * UDP datagram related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP + * datagram payload size that the local endpoint transmits. + */ +#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200 + +/** + * @macro + * + * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP + * datagram payload size that Path MTU Discovery can discover. + */ +#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452 + +/** + * @macrosection + * + * QUIC specific macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_MAX_VARINT` is the maximum value which can be + * encoded in variable-length integer encoding. + */ +#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1) + +/** + * @macro + * + * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` is the length of Stateless + * Reset Token. + */ +#define NGTCP2_STATELESS_RESET_TOKENLEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length + * of random bytes (Unpredictable Bits) in Stateless Reset packet. + */ +#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 + +/** + * @macro + * + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` is the length of + * PATH_CHALLENGE data. + */ +#define NGTCP2_PATH_CHALLENGE_DATALEN 8 + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V1` is an encryption key to create + * integrity tag of Retry packet.  It is used for QUIC v1. + */ +#define NGTCP2_RETRY_KEY_V1                                                    \ +  "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V1` is nonce used when generating + * integrity tag of Retry packet.  It is used for QUIC v1. + */ +#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V2` is an encryption key to create + * integrity tag of Retry packet.  It is used for QUIC v2.  See + * :rfc:`9369`. + */ +#define NGTCP2_RETRY_KEY_V2                                                    \ +  "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V2` is nonce used when generating + * integrity tag of Retry packet.  It is used for QUIC v2.  See + * :rfc:`9369`. + */ +#define NGTCP2_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a" + +/** + * @macro + * + * :macro:`NGTCP2_HP_MASKLEN` is the length of header protection mask. + */ +#define NGTCP2_HP_MASKLEN 5 + +/** + * @macro + * + * :macro:`NGTCP2_HP_SAMPLELEN` is the number bytes sampled when + * encrypting a packet header. + */ +#define NGTCP2_HP_SAMPLELEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` is a default initial RTT. + */ +#define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS) + +/** + * @macro + * + * :macro:`NGTCP2_MAX_CIDLEN` is the maximum length of Connection ID. + */ +#define NGTCP2_MAX_CIDLEN 20 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_CIDLEN` is the minimum length of Connection ID. + */ +#define NGTCP2_MIN_CIDLEN 1 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN` is the minimum length of + * Destination Connection ID in Client Initial packet if it does not + * bear token from Retry packet. + */ +#define NGTCP2_MIN_INITIAL_DCIDLEN 8 + +/** + * @macrosection + * + * ECN related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_ECN_NOT_ECT` indicates no ECN marking. + */ +#define NGTCP2_ECN_NOT_ECT 0x0 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_ECT_1` is ECT(1) codepoint. + */ +#define NGTCP2_ECN_ECT_1 0x1 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_ECT_0` is ECT(0) codepoint. + */ +#define NGTCP2_ECN_ECT_0 0x2 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_CE` is CE codepoint. + */ +#define NGTCP2_ECN_CE 0x3 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_MASK` is a bit mask to get ECN marking. + */ +#define NGTCP2_ECN_MASK 0x3 + +#define NGTCP2_PKT_INFO_V1 1 +#define NGTCP2_PKT_INFO_VERSION NGTCP2_PKT_INFO_V1 + +/** + * @struct + * + * :type:`ngtcp2_pkt_info` is a packet metadata. + */ +typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info { +  /** +   * :member:`ecn` is ECN marking, and when it is passed to +   * `ngtcp2_conn_read_pkt()`, it should be either +   * :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`, +   * :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`. +   */ +  uint8_t ecn; +} ngtcp2_pkt_info; + +/** + * @macrosection + * + * ngtcp2 library error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` indicates that a passed + * argument is invalid. + */ +#define NGTCP2_ERR_INVALID_ARGUMENT -201 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NOBUF` indicates that a provided buffer does not + * have enough space to store data. + */ +#define NGTCP2_ERR_NOBUF -202 +/** + * @macro + * + * :macro:`NGTCP2_ERR_PROTO` indicates a general protocol error. + */ +#define NGTCP2_ERR_PROTO -203 +/** + * @macro + * + * :macro:`NGTCP2_ERR_INVALID_STATE` indicates that a requested + * operation is not allowed at the current connection state. + */ +#define NGTCP2_ERR_INVALID_STATE -204 +/** + * @macro + * + * :macro:`NGTCP2_ERR_ACK_FRAME` indicates that an invalid ACK frame + * is received. + */ +#define NGTCP2_ERR_ACK_FRAME -205 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` indicates that there is no + * spare stream ID available. + */ +#define NGTCP2_ERR_STREAM_ID_BLOCKED -206 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_IN_USE` indicates that a stream ID is + * already in use. + */ +#define NGTCP2_ERR_STREAM_IN_USE -207 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` indicates that stream data + * cannot be sent because of flow control. + */ +#define NGTCP2_ERR_STREAM_DATA_BLOCKED -208 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FLOW_CONTROL` indicates flow control error. + */ +#define NGTCP2_ERR_FLOW_CONTROL -209 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CONNECTION_ID_LIMIT` indicates that the number + * of received Connection ID exceeds acceptable limit. + */ +#define NGTCP2_ERR_CONNECTION_ID_LIMIT -210 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_LIMIT` indicates that a remote endpoint + * opens more streams that is permitted. + */ +#define NGTCP2_ERR_STREAM_LIMIT -211 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FINAL_SIZE` indicates that inconsistent final + * size of a stream. + */ +#define NGTCP2_ERR_FINAL_SIZE -212 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CRYPTO` indicates crypto (TLS) related error. + */ +#define NGTCP2_ERR_CRYPTO -213 +/** + * @macro + * + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` indicates that packet number + * is exhausted. + */ +#define NGTCP2_ERR_PKT_NUM_EXHAUSTED -214 +/** + * @macro + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` indicates that a + * required transport parameter is missing. + */ +#define NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM -215 +/** + * @macro + * + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` indicates that a + * transport parameter is malformed. + */ +#define NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM -216 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FRAME_ENCODING` indicates there is an error in + * frame encoding. + */ +#define NGTCP2_ERR_FRAME_ENCODING -217 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DECRYPT` indicates a decryption failure. + */ +#define NGTCP2_ERR_DECRYPT -218 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` indicates no more data can be + * sent to a stream. + */ +#define NGTCP2_ERR_STREAM_SHUT_WR -219 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` indicates that a stream was + * not found. + */ +#define NGTCP2_ERR_STREAM_NOT_FOUND -220 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_STATE` indicates that a requested + * operation is not allowed at the current stream state. + */ +#define NGTCP2_ERR_STREAM_STATE -221 +/** + * @macro + * + * :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION` indicates that Version + * Negotiation packet was received. + */ +#define NGTCP2_ERR_RECV_VERSION_NEGOTIATION -222 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CLOSING` indicates that connection is in closing + * state. + */ +#define NGTCP2_ERR_CLOSING -223 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DRAINING` indicates that connection is in + * draining state. + */ +#define NGTCP2_ERR_DRAINING -224 +/** + * @macro + * + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` indicates a general transport + * parameter error. + */ +#define NGTCP2_ERR_TRANSPORT_PARAM -225 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded. + */ +#define NGTCP2_ERR_DISCARD_PKT -226 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` indicates that there is no + * spare Connection ID available. + */ +#define NGTCP2_ERR_CONN_ID_BLOCKED -227 +/** + * @macro + * + * :macro:`NGTCP2_ERR_INTERNAL` indicates an internal error. + */ +#define NGTCP2_ERR_INTERNAL -228 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED` indicates that a crypto + * buffer exceeded. + */ +#define NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED -229 +/** + * @macro + * + * :macro:`NGTCP2_ERR_WRITE_MORE` indicates + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used and a function call + * succeeded. + */ +#define NGTCP2_ERR_WRITE_MORE -230 +/** + * @macro + * + * :macro:`NGTCP2_ERR_RETRY` indicates that server should send Retry + * packet. + */ +#define NGTCP2_ERR_RETRY -231 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DROP_CONN` indicates that an endpoint should + * drop connection immediately. + */ +#define NGTCP2_ERR_DROP_CONN -232 +/** + * @macro + * + * :macro:`NGTCP2_ERR_AEAD_LIMIT_REACHED` indicates AEAD encryption + * limit is reached and key update is not available.  An endpoint + * should drop connection immediately. + */ +#define NGTCP2_ERR_AEAD_LIMIT_REACHED -233 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation + * could not probe that a path is capable of sending UDP datagram + * payload of size at least 1200 bytes. + */ +#define NGTCP2_ERR_NO_VIABLE_PATH -234 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` indicates that server + * should send Version Negotiation packet. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION -235 +/** + * @macro + * + * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` indicates that QUIC + * connection is not established before the specified deadline. + */ +#define NGTCP2_ERR_HANDSHAKE_TIMEOUT -236 +/** + * @macro + * + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the + * version negotiation failed. + */ +#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -237 +/** + * @macro + * + * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be + * closed silently because of idle timeout. + */ +#define NGTCP2_ERR_IDLE_CLOSE -238 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FATAL` indicates that error codes less than this + * value is fatal error.  When this error is returned, an endpoint + * should close connection immediately. + */ +#define NGTCP2_ERR_FATAL -500 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NOMEM` indicates out of memory. + */ +#define NGTCP2_ERR_NOMEM -501 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` indicates that user defined + * callback function failed. + */ +#define NGTCP2_ERR_CALLBACK_FAILURE -502 + +/** + * @macrosection + * + * QUIC packet header flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_PKT_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet + * header. + */ +#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit + * (aka QUIC bit) is not set. + */ +#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set. + */ +#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u + +/** + * @enum + * + * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC + * packet types. + */ +typedef enum ngtcp2_pkt_type { +  /** +   * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2 +   * for convenience. +   */ +  NGTCP2_PKT_VERSION_NEGOTIATION = 0x80, +  /** +   * :enum:`NGTCP2_PKT_STATELESS_RESET` is defined by libngtcp2 for +   * convenience. +   */ +  NGTCP2_PKT_STATELESS_RESET = 0x81, +  /** +   * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet. +   */ +  NGTCP2_PKT_INITIAL = 0x10, +  /** +   * :enum:`NGTCP2_PKT_0RTT` indicates 0-RTT packet. +   */ +  NGTCP2_PKT_0RTT = 0x11, +  /** +   * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet. +   */ +  NGTCP2_PKT_HANDSHAKE = 0x12, +  /** +   * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet. +   */ +  NGTCP2_PKT_RETRY = 0x13, +  /** +   * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience. +   */ +  NGTCP2_PKT_1RTT = 0x40 +} ngtcp2_pkt_type; + +/** + * @macrosection + * + * QUIC transport error code + */ + +/** + * @macro + * + * :macro:`NGTCP2_NO_ERROR` is QUIC transport error code ``NO_ERROR``. + */ +#define NGTCP2_NO_ERROR 0x0u + +/** + * @macro + * + * :macro:`NGTCP2_INTERNAL_ERROR` is QUIC transport error code + * ``INTERNAL_ERROR``. + */ +#define NGTCP2_INTERNAL_ERROR 0x1u + +/** + * @macro + * + * :macro:`NGTCP2_CONNECTION_REFUSED` is QUIC transport error code + * ``CONNECTION_REFUSED``. + */ +#define NGTCP2_CONNECTION_REFUSED 0x2u + +/** + * @macro + * + * :macro:`NGTCP2_FLOW_CONTROL_ERROR` is QUIC transport error code + * ``FLOW_CONTROL_ERROR``. + */ +#define NGTCP2_FLOW_CONTROL_ERROR 0x3u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_LIMIT_ERROR` is QUIC transport error code + * ``STREAM_LIMIT_ERROR``. + */ +#define NGTCP2_STREAM_LIMIT_ERROR 0x4u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_STATE_ERROR` is QUIC transport error code + * ``STREAM_STATE_ERROR``. + */ +#define NGTCP2_STREAM_STATE_ERROR 0x5u + +/** + * @macro + * + * :macro:`NGTCP2_FINAL_SIZE_ERROR` is QUIC transport error code + * ``FINAL_SIZE_ERROR``. + */ +#define NGTCP2_FINAL_SIZE_ERROR 0x6u + +/** + * @macro + * + * :macro:`NGTCP2_FRAME_ENCODING_ERROR` is QUIC transport error code + * ``FRAME_ENCODING_ERROR``. + */ +#define NGTCP2_FRAME_ENCODING_ERROR 0x7u + +/** + * @macro + * + * :macro:`NGTCP2_TRANSPORT_PARAMETER_ERROR` is QUIC transport error + * code ``TRANSPORT_PARAMETER_ERROR``. + */ +#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u + +/** + * @macro + * + * :macro:`NGTCP2_CONNECTION_ID_LIMIT_ERROR` is QUIC transport error + * code ``CONNECTION_ID_LIMIT_ERROR``. + */ +#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u + +/** + * @macro + * + * :macro:`NGTCP2_PROTOCOL_VIOLATION` is QUIC transport error code + * ``PROTOCOL_VIOLATION``. + */ +#define NGTCP2_PROTOCOL_VIOLATION 0xau + +/** + * @macro + * + * :macro:`NGTCP2_INVALID_TOKEN` is QUIC transport error code + * ``INVALID_TOKEN``. + */ +#define NGTCP2_INVALID_TOKEN 0xbu + +/** + * @macro + * + * :macro:`NGTCP2_APPLICATION_ERROR` is QUIC transport error code + * ``APPLICATION_ERROR``. + */ +#define NGTCP2_APPLICATION_ERROR 0xcu + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_BUFFER_EXCEEDED` is QUIC transport error code + * ``CRYPTO_BUFFER_EXCEEDED``. + */ +#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu + +/** + * @macro + * + * :macro:`NGTCP2_KEY_UPDATE_ERROR` is QUIC transport error code + * ``KEY_UPDATE_ERROR``. + */ +#define NGTCP2_KEY_UPDATE_ERROR 0xeu + +/** + * @macro + * + * :macro:`NGTCP2_AEAD_LIMIT_REACHED` is QUIC transport error code + * ``AEAD_LIMIT_REACHED``. + */ +#define NGTCP2_AEAD_LIMIT_REACHED 0xfu + +/** + * @macro + * + * :macro:`NGTCP2_NO_VIABLE_PATH` is QUIC transport error code + * ``NO_VIABLE_PATH``. + */ +#define NGTCP2_NO_VIABLE_PATH 0x10u + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_ERROR` is QUIC transport error code + * ``CRYPTO_ERROR``. + */ +#define NGTCP2_CRYPTO_ERROR 0x100u + +/** + * @macro + * + * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR` is QUIC transport error + * code ``VERSION_NEGOTIATION_ERROR``.  See :rfc:`9368`. + */ +#define NGTCP2_VERSION_NEGOTIATION_ERROR 0x11 + +/** + * @enum + * + * :type:`ngtcp2_path_validation_result` defines path validation + * result code. + */ +typedef enum ngtcp2_path_validation_result { +  /** +   * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS` indicates +   * successful validation. +   */ +  NGTCP2_PATH_VALIDATION_RESULT_SUCCESS, +  /** +   * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates +   * validation failure. +   */ +  NGTCP2_PATH_VALIDATION_RESULT_FAILURE, +  /** +   * :enum:`NGTCP2_PATH_VALIDATION_RESULT_ABORTED` indicates that path +   * validation was aborted. +   */ +  NGTCP2_PATH_VALIDATION_RESULT_ABORTED +} ngtcp2_path_validation_result; + +/** + * @typedef + * + * :type:`ngtcp2_tstamp` is a timestamp with nanosecond resolution. + * ``UINT64_MAX`` is an invalid value, and it is often used to + * indicate that no value is set. + */ +typedef uint64_t ngtcp2_tstamp; + +/** + * @typedef + * + * :type:`ngtcp2_duration` is a period of time in nanosecond + * resolution.  ``UINT64_MAX`` is an invalid value, and it is often + * used to indicate that no value is set. + */ +typedef uint64_t ngtcp2_duration; + +/** + * @struct + * + * :type:`ngtcp2_cid` holds a Connection ID. + */ +typedef struct ngtcp2_cid { +  /** +   * :member:`datalen` is the length of Connection ID. +   */ +  size_t datalen; +  /** +   * :member:`data` is the buffer to store Connection ID. +   */ +  uint8_t data[NGTCP2_MAX_CIDLEN]; +} ngtcp2_cid; + +/** + * @struct + * + * :type:`ngtcp2_vec` is struct iovec compatible structure to + * reference arbitrary array of bytes. + */ +typedef struct ngtcp2_vec { +  /** +   * :member:`base` points to the data. +   */ +  uint8_t *base; +  /** +   * :member:`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 most :macro:`NGTCP2_MAX_CIDLEN`. + */ +NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, +                                   size_t datalen); + +/** + * @function + * + * `ngtcp2_cid_eq` returns nonzero if |a| and |b| share the same + * Connection ID. + */ +NGTCP2_EXTERN int ngtcp2_cid_eq(const ngtcp2_cid *a, const ngtcp2_cid *b); + +/** + * @struct + * + * :type:`ngtcp2_pkt_hd` represents QUIC packet header. + */ +typedef struct ngtcp2_pkt_hd { +  /** +   * :member:`dcid` is Destination Connection ID. +   */ +  ngtcp2_cid dcid; +  /** +   * :member:`scid` is Source Connection ID. +   */ +  ngtcp2_cid scid; +  /** +   * :member:`pkt_num` is a packet number. +   */ +  int64_t pkt_num; +  /** +   * :member:`token` contains token.  Only Initial packet may contain +   * token.  NULL if no token is present. +   */ +  const uint8_t *token; +  /** +   * :member:`tokenlen` is the length of :member:`token`.  0 if no +   * token is present. +   */ +  size_t tokenlen; +  /** +   * :member:`pkt_numlen` is the number of bytes spent to encode +   * :member:`pkt_num`. +   */ +  size_t pkt_numlen; +  /** +   * :member:`len` is the sum of :member:`pkt_numlen` and the length +   * of QUIC packet payload. +   */ +  size_t len; +  /** +   * :member:`version` is QUIC version. +   */ +  uint32_t version; +  /** +   * :member:`type` is a type of QUIC packet.  This field does not +   * have a QUIC packet type defined for a specific QUIC version. +   * Instead, it contains version independent packet type defined by +   * this library.  See :type:`ngtcp2_pkt_type`. +   */ +  uint8_t type; +  /** +   * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_* +   * <NGTCP2_PKT_FLAG_NONE>`. +   */ +  uint8_t flags; +} ngtcp2_pkt_hd; + +/** + * @struct + * + * :type:`ngtcp2_pkt_stateless_reset` represents Stateless Reset. + */ +typedef struct ngtcp2_pkt_stateless_reset { +  /** +   * :member:`stateless_reset_token` contains stateless reset token. +   */ +  uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +  /** +   * :member:`rand` points a buffer which contains random bytes +   * section. +   */ +  const uint8_t *rand; +  /** +   * :member:`randlen` is the number of random bytes. +   */ +  size_t randlen; +} ngtcp2_pkt_stateless_reset; + +/** + * @macrosection + * + * QUIC transport parameters related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` is the default + * value of max_udp_payload_size transport parameter. + */ +#define NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE 65527 + +/** + * @macro + * + * :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 + * + * :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 + * + * :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 + * + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS + * extension type of quic_transport_parameters. + */ +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u + +#ifdef NGTCP2_USE_GENERIC_SOCKADDR +#  ifndef NGTCP2_AF_INET +#    error NGTCP2_AF_INET must be defined +#  endif /* !defined(NGTCP2_AF_INET) */ + +#  ifndef NGTCP2_AF_INET6 +#    error NGTCP2_AF_INET6 must be defined +#  endif /* !defined(NGTCP2_AF_INET6) */ + +typedef unsigned short int ngtcp2_sa_family; +typedef uint16_t ngtcp2_in_port; + +typedef struct ngtcp2_sockaddr { +  ngtcp2_sa_family sa_family; +  uint8_t sa_data[14]; +} ngtcp2_sockaddr; + +typedef struct ngtcp2_in_addr { +  uint32_t s_addr; +} ngtcp2_in_addr; + +typedef struct ngtcp2_sockaddr_in { +  ngtcp2_sa_family sin_family; +  ngtcp2_in_port sin_port; +  ngtcp2_in_addr sin_addr; +  uint8_t sin_zero[8]; +} ngtcp2_sockaddr_in; + +typedef struct ngtcp2_in6_addr { +  uint8_t in6_addr[16]; +} ngtcp2_in6_addr; + +typedef struct ngtcp2_sockaddr_in6 { +  ngtcp2_sa_family sin6_family; +  ngtcp2_in_port sin6_port; +  uint32_t sin6_flowinfo; +  ngtcp2_in6_addr sin6_addr; +  uint32_t sin6_scope_id; +} ngtcp2_sockaddr_in6; + +typedef uint32_t ngtcp2_socklen; +#else /* !defined(NGTCP2_USE_GENERIC_SOCKADDR) */ +#  define NGTCP2_AF_INET AF_INET +#  define NGTCP2_AF_INET6 AF_INET6 + +/** + * @typedef + * + * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr.  If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * the generic struct sockaddr defined in ngtcp2.h. + */ +typedef struct sockaddr ngtcp2_sockaddr; +/** + * @typedef + * + * :type:`ngtcp2_sockaddr_in` is typedefed to struct sockaddr_in.  If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * the generic struct sockaddr_in defined in ngtcp2.h. + */ +typedef struct sockaddr_in ngtcp2_sockaddr_in; +/** + * @typedef + * + * :type:`ngtcp2_sockaddr_in6` is typedefed to struct sockaddr_in6. + * If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed + * to the generic struct sockaddr_in6 defined in ngtcp2.h. + */ +typedef struct sockaddr_in6 ngtcp2_sockaddr_in6; +/** + * @typedef + * + * :type:`ngtcp2_socklen` is typedefed to socklen_t.  If + * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to + * uint32_t. + */ +typedef socklen_t ngtcp2_socklen; +#endif /* !defined(NGTCP2_USE_GENERIC_SOCKADDR) */ + +/** + * @struct + * + * :type:`ngtcp2_sockaddr_union` conveniently includes all supported + * address types. + */ +typedef union ngtcp2_sockaddr_union { +  ngtcp2_sockaddr sa; +  ngtcp2_sockaddr_in in; +  ngtcp2_sockaddr_in6 in6; +} ngtcp2_sockaddr_union; + +/** + * @struct + * + * :type:`ngtcp2_preferred_addr` represents preferred address + * structure. + */ +typedef struct ngtcp2_preferred_addr { +  /** +   * :member:`cid` is a Connection ID. +   */ +  ngtcp2_cid cid; +  /** +   * :member:`ipv4` contains IPv4 address and port. +   */ +  ngtcp2_sockaddr_in ipv4; +  /** +   * :member:`ipv6` contains IPv6 address and port. +   */ +  ngtcp2_sockaddr_in6 ipv6; +  /** +   * :member:`ipv4_present` indicates that :member:`ipv4` contains +   * IPv4 address and port. +   */ +  uint8_t ipv4_present; +  /** +   * :member:`ipv6_present` indicates that :member:`ipv6` contains +   * IPv6 address and port. +   */ +  uint8_t ipv6_present; +  /** +   * :member:`stateless_reset_token` contains stateless reset token. +   */ +  uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_preferred_addr; + +/** + * @struct + * + * :type:`ngtcp2_version_info` represents version_information + * structure.  See :rfc:`9368`. + */ +typedef struct ngtcp2_version_info { +  /** +   * :member:`chosen_version` is the version chosen by the sender. +   */ +  uint32_t chosen_version; +  /** +   * :member:`available_versions` points the wire image of +   * available_versions field.  The each version is therefore in +   * network byte order. +   */ +  const uint8_t *available_versions; +  /** +   * :member:`available_versionslen` is the number of bytes pointed by +   * :member:`available_versions`, not the number of versions +   * included. +   */ +  size_t available_versionslen; +} ngtcp2_version_info; + +#define NGTCP2_TRANSPORT_PARAMS_V1 1 +#define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_V1 + +/** + * @struct + * + * :type:`ngtcp2_transport_params` represents QUIC transport + * parameters. + */ +typedef struct ngtcp2_transport_params { +  /** +   * :member:`preferred_addr` contains preferred address if +   * :member:`preferred_addr_present` is nonzero. +   */ +  ngtcp2_preferred_addr preferred_addr; +  /** +   * :member:`original_dcid` is the Destination Connection ID field +   * from the first Initial packet from client.  Server must specify +   * this field and set :member:`original_dcid_present` to nonzero. +   * It is expected that application knows the original Destination +   * Connection ID even if it sends Retry packet, for example, by +   * including it in retry token.  Otherwise, application should not +   * specify this field. +   */ +  ngtcp2_cid original_dcid; +  /** +   * :member:`initial_scid` is the Source Connection ID field from the +   * first Initial packet the local endpoint sends.  Application +   * should not specify this field.  If :member:`initial_scid_present` +   * is set to nonzero, it indicates this field is set. +   */ +  ngtcp2_cid initial_scid; +  /** +   * :member:`retry_scid` is the Source Connection ID field from Retry +   * packet.  Only server uses this field.  If server application +   * received Initial packet with retry token from client, and server +   * successfully verified its token, server application must set +   * Destination Connection ID field from the Initial packet to this +   * field, and set :member:`retry_scid_present` to nonzero.  Server +   * application must verify that the Destination Connection ID from +   * Initial packet was sent in Retry packet by, for example, +   * including the Connection ID in a token, or including it in AAD +   * when encrypting a token. +   */ +  ngtcp2_cid retry_scid; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`initial_max_data` is the connection level flow control +   * window. +   */ +  uint64_t initial_max_data; +  /** +   * :member:`initial_max_streams_bidi` is the number of concurrent +   * streams that the remote endpoint can create. +   */ +  uint64_t initial_max_streams_bidi; +  /** +   * :member:`initial_max_streams_uni` is the number of concurrent +   * unidirectional streams that the remote endpoint can create. +   */ +  uint64_t initial_max_streams_uni; +  /** +   * :member:`max_idle_timeout` is a duration during which sender +   * allows quiescent.  0 means no idle timeout.  It must not be +   * UINT64_MAX. +   */ +  ngtcp2_duration max_idle_timeout; +  /** +   * :member:`max_udp_payload_size` is the maximum UDP payload size +   * that the local endpoint can receive. +   */ +  uint64_t max_udp_payload_size; +  /** +   * :member:`active_connection_id_limit` is the maximum number of +   * Connection ID that sender can store. +   */ +  uint64_t active_connection_id_limit; +  /** +   * :member:`ack_delay_exponent` is the exponent used in ACK Delay +   * field in ACK frame. +   */ +  uint64_t ack_delay_exponent; +  /** +   * :member:`max_ack_delay` is the maximum acknowledgement delay by +   * which the local endpoint will delay sending acknowledgements.  It +   * must be strictly less than (1 << 14) milliseconds. +   * Sub-millisecond part is dropped when sending it in a QUIC +   * transport parameter. +   */ +  ngtcp2_duration max_ack_delay; +  /** +   * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM +   * frame that the local endpoint willingly receives.  Specifying 0 +   * disables DATAGRAM support.  See :rfc:`9221`. +   */ +  uint64_t max_datagram_frame_size; +  /** +   * :member:`stateless_reset_token_present` is nonzero if +   * :member:`stateless_reset_token` field is set. +   */ +  uint8_t stateless_reset_token_present; +  /** +   * :member:`disable_active_migration` is nonzero if the local +   * endpoint does not support active connection migration. +   */ +  uint8_t disable_active_migration; +  /** +   * :member:`original_dcid_present` is nonzero if +   * :member:`original_dcid` field is set. +   */ +  uint8_t original_dcid_present; +  /** +   * :member:`initial_scid_present` is nonzero if +   * :member:`initial_scid` field is set. +   */ +  uint8_t initial_scid_present; +  /** +   * :member:`retry_scid_present` is nonzero if :member:`retry_scid` +   * field is set. +   */ +  uint8_t retry_scid_present; +  /** +   * :member:`preferred_addr_present` is nonzero if +   * :member:`preferred_address` is set. +   */ +  uint8_t preferred_addr_present; +  /** +   * :member:`stateless_reset_token` contains stateless reset token. +   */ +  uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +  /** +   * :member:`grease_quic_bit` is nonzero if sender supports "Greasing +   * the QUIC Bit" extension.  See :rfc:`9287`. +   */ +  uint8_t grease_quic_bit; +  /** +   * :member:`version_info` contains version_information field if +   * :member:`version_info_present` is nonzero.  Application should +   * not specify this field. +   */ +  ngtcp2_version_info version_info; +  /** +   * :member:`version_info_present` is nonzero if +   * :member:`version_info` is set.  Application should not specify +   * this field. +   */ +  uint8_t version_info_present; +} ngtcp2_transport_params; + +#define NGTCP2_CONN_INFO_V1 1 +#define NGTCP2_CONN_INFO_VERSION NGTCP2_CONN_INFO_V1 + +/** + * @struct + * + * :type:`ngtcp2_conn_info` holds various connection statistics. + */ +typedef struct ngtcp2_conn_info { +  /** +   * :member:`latest_rtt` is the latest RTT sample which is not +   * adjusted by acknowledgement delay. +   */ +  ngtcp2_duration latest_rtt; +  /** +   * :member:`min_rtt` is the minimum RTT seen so far.  It is not +   * adjusted by acknowledgement delay. +   */ +  ngtcp2_duration min_rtt; +  /** +   * :member:`smoothed_rtt` is the smoothed RTT. +   */ +  ngtcp2_duration smoothed_rtt; +  /** +   * :member:`rttvar` is a mean deviation of observed RTT. +   */ +  ngtcp2_duration rttvar; +  /** +   * :member:`cwnd` is the size of congestion window. +   */ +  uint64_t cwnd; +  /** +   * :member:`ssthresh` is slow start threshold. +   */ +  uint64_t ssthresh; +  /** +   * :member:`bytes_in_flight` is the number in bytes of all sent +   * packets which have not been acknowledged. +   */ +  uint64_t bytes_in_flight; +} ngtcp2_conn_info; + +/** + * @enum + * + * :type:`ngtcp2_cc_algo` defines congestion control algorithms. + */ +typedef enum ngtcp2_cc_algo { +  /** +   * :enum:`NGTCP2_CC_ALGO_RENO` represents Reno. +   */ +  NGTCP2_CC_ALGO_RENO = 0x00, +  /** +   * :enum:`NGTCP2_CC_ALGO_CUBIC` represents Cubic. +   */ +  NGTCP2_CC_ALGO_CUBIC = 0x01, +  /** +   * :enum:`NGTCP2_CC_ALGO_BBR` represents BBR v2. +   */ +  NGTCP2_CC_ALGO_BBR = 0x02 +} ngtcp2_cc_algo; + +/** + * @functypedef + * + * :type:`ngtcp2_printf` is a callback function for logging. + * |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, ...); + +/** + * @macrosection + * + * QLog related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u +/** + * @macro + * + * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the + * final call to :type:`ngtcp2_qlog_write` in the current connection. + */ +#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u + +/** + * @struct + * + * :type:`ngtcp2_rand_ctx` is a wrapper around native random number + * generator.  It is opaque to the ngtcp2 library.  This might be + * useful if application needs to specify random number generator per + * thread or per connection. + */ +typedef struct ngtcp2_rand_ctx { +  /** +   * :member:`native_handle` is a pointer to an underlying random +   * number generator. +   */ +  void *native_handle; +} ngtcp2_rand_ctx; + +/** + * @functypedef + * + * :type:`ngtcp2_qlog_write` is a callback function which is called to + * write qlog |data| of length |datalen| bytes.  |flags| is bitwise OR + * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_* + * <NGTCP2_QLOG_WRITE_FLAG_NONE>`.  If + * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0. + */ +typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, +                                  const void *data, size_t datalen); + +/** + * @enum + * + * :type:`ngtcp2_token_type` defines the type of token. + */ +typedef enum ngtcp2_token_type { +  /** +   * :enum:`NGTCP2_TOKEN_TYPE_UNKNOWN` indicates that the type of +   * token is unknown. +   */ +  NGTCP2_TOKEN_TYPE_UNKNOWN, +  /** +   * :enum:`NGTCP2_TOKEN_TYPE_RETRY` indicates that a token comes from +   * Retry packet. +   */ +  NGTCP2_TOKEN_TYPE_RETRY, +  /** +   * :enum:`NGTCP2_TOKEN_TYPE_NEW_TOKEN` indicates that a token comes +   * from NEW_TOKEN frame. +   */ +  NGTCP2_TOKEN_TYPE_NEW_TOKEN +} ngtcp2_token_type; + +#define NGTCP2_SETTINGS_V1 1 +#define NGTCP2_SETTINGS_V2 2 +#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_V2 + +/** + * @struct + * + * :type:`ngtcp2_settings` defines QUIC connection settings. + */ +typedef struct ngtcp2_settings { +  /** +   * :member:`qlog_write` is a callback function to write qlog. +   * Setting ``NULL`` disables qlog. +   */ +  ngtcp2_qlog_write qlog_write; +  /** +   * :member:`cc_algo` specifies congestion control algorithm. +   */ +  ngtcp2_cc_algo cc_algo; +  /** +   * :member:`initial_ts` is an initial timestamp given to the +   * library. +   */ +  ngtcp2_tstamp initial_ts; +  /** +   * :member:`initial_rtt` is an initial RTT. +   */ +  ngtcp2_duration initial_rtt; +  /** +   * :member:`log_printf` is a function that the library uses to write +   * logs.  ``NULL`` means no logging output.  It is nothing to do +   * with qlog. +   */ +  ngtcp2_printf log_printf; +  /** +   * :member:`max_tx_udp_payload_size` is the maximum size of UDP +   * datagram payload that the local endpoint transmits. +   */ +  size_t max_tx_udp_payload_size; +  /** +   * :member:`token` is a token from Retry packet or NEW_TOKEN frame. +   * +   * Server sets this field if it received the token in Client Initial +   * packet and successfully validated.  It should also set +   * :member:`token_type` field. +   * +   * Client sets this field if it intends to send token in its Initial +   * packet. +   * +   * `ngtcp2_conn_server_new` and `ngtcp2_conn_client_new` make a copy +   * of token. +   * +   * Set NULL if there is no token. +   */ +  const uint8_t *token; +  /** +   * :member:`tokenlen` is the length of :member:`token`.  Set 0 if +   * there is no token. +   */ +  size_t tokenlen; +  /** +   * :member:`token_type` is the type of token.  Server application +   * should set this field. +   */ +  ngtcp2_token_type token_type; +  /** +   * :member:`rand_ctx` is an optional random number generator to be +   * passed to :type:`ngtcp2_rand` callback. +   */ +  ngtcp2_rand_ctx rand_ctx; +  /** +   * :member:`max_window` is the maximum connection-level flow control +   * window if connection-level window auto-tuning is enabled.  The +   * connection-level window auto tuning is enabled if nonzero value +   * is specified in this field.  The initial value of window size is +   * :member:`ngtcp2_transport_params.initial_max_data`.  The window +   * size is scaled up to the value specified in this field. +   */ +  uint64_t max_window; +  /** +   * :member:`max_stream_window` is the maximum stream-level flow +   * control window if stream-level window auto-tuning is enabled. +   * The stream-level window auto-tuning is enabled if nonzero value +   * is specified in this field.  The initial value of window size is +   * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote`, +   * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local`, +   * or :member:`ngtcp2_transport_params.initial_max_stream_data_uni`, +   * depending on the type of stream.  The window size is scaled up to +   * the value specified in this field. +   */ +  uint64_t max_stream_window; +  /** +   * :member:`ack_thresh` is the minimum number of the received ACK +   * eliciting packets that trigger the immediate acknowledgement from +   * the local endpoint. +   */ +  size_t ack_thresh; +  /** +   * :member:`no_tx_udp_payload_size_shaping`, if set to nonzero, +   * instructs the library not to limit the UDP payload size to +   * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by +   * Path MTU Discovery), and instead use the minimum size among the +   * given buffer size, :member:`max_tx_udp_payload_size`, and the +   * received max_udp_payload_size QUIC transport parameter. +   */ +  uint8_t no_tx_udp_payload_size_shaping; +  /** +   * :member:`handshake_timeout` is the period of time before giving +   * up QUIC connection establishment.  If QUIC handshake is not +   * complete within this period, `ngtcp2_conn_handle_expiry` returns +   * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` error.  The deadline is +   * :member:`initial_ts` + :member:`handshake_timeout`.  If this +   * field is set to ``UINT64_MAX``, no handshake timeout is set. +   */ +  ngtcp2_duration handshake_timeout; +  /** +   * :member:`preferred_versions` is the array of versions that are +   * preferred by the local endpoint.  All versions set in this array +   * must be supported by the library, and compatible to QUIC v1.  The +   * reserved versions are not allowed.  They are sorted in the order +   * of preference. +   * +   * On compatible version negotiation, server will negotiate one of +   * those versions contained in this array if there is some overlap +   * between these versions and the versions offered by the client. +   * If there is no overlap, but the client chosen version is +   * supported by the library, the server chooses the client chosen +   * version as the negotiated version.  This version set corresponds +   * to Offered Versions described in :rfc:`9368`, and it should be +   * included in Version Negotiation packet. +   * +   * Client uses this field and :member:`original_version` to prevent +   * version downgrade attack if it reacted upon Version Negotiation +   * packet.  If this field is specified, client must include +   * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless +   * |client_chosen_version| is a reserved version. +   */ +  const uint32_t *preferred_versions; +  /** +   * :member:`preferred_versionslen` is the number of versions that +   * are contained in the array pointed by +   * :member:`preferred_versions`. +   */ +  size_t preferred_versionslen; +  /** +   * :member:`available_versions` is the array of versions that are +   * going to be set in :member:`available_versions +   * <ngtcp2_version_info.available_versions>` field of outgoing +   * version_information QUIC transport parameter. +   * +   * For server, this corresponds to Fully-Deployed Versions described +   * in :rfc:`9368`.  If this field is not set, it is set to +   * :member:`preferred_versions` internally if +   * :member:`preferred_versionslen` is not zero.  If this field is +   * not set, and :member:`preferred_versionslen` is zero, this field +   * is set to :macro:`NGTCP2_PROTO_VER_V1` internally. +   * +   * Client must include |client_chosen_version| passed to +   * `ngtcp2_conn_client_new` in this array if this field is set and +   * |client_chosen_version| is not a reserved version.  If this field +   * is not set, |client_chosen_version| passed to +   * `ngtcp2_conn_client_new` will be set in this field internally +   * unless |client_chosen_version| is a reserved version. +   */ +  const uint32_t *available_versions; +  /** +   * :member:`available_versionslen` is the number of versions that +   * are contained in the array pointed by +   * :member:`available_versions`. +   */ +  size_t available_versionslen; +  /** +   * :member:`original_version` is the original version that client +   * initially used to make a connection attempt.  If it is set, and +   * it differs from |client_chosen_version| passed to +   * `ngtcp2_conn_client_new`, the library assumes that client reacted +   * upon Version Negotiation packet.  Server does not use this field. +   */ +  uint32_t original_version; +  /** +   * :member:`no_pmtud`, if set to nonzero, disables Path MTU +   * Discovery. +   */ +  uint8_t no_pmtud; +  /** +   * :member:`initial_pkt_num` is the initial packet number for each +   * packet number space.  It must be in range [0, INT32_MAX], +   * inclusive. +   */ +  uint32_t initial_pkt_num; +  /* The following fields have been added since NGTCP2_SETTINGS_V2. */ +  /** +   * :member:`pmtud_probes` is the array of UDP datagram payload size +   * to probe during Path MTU Discovery.  The discovery is done in the +   * order appeared in this array.  The size must be strictly larger +   * than 1200, otherwise the behavior is undefined.  The maximum +   * value in this array should be set to +   * :member:`max_tx_udp_payload_size`.  If this field is not set, the +   * predefined PMTUD probes are made.  This field has been available +   * since v1.4.0. +   */ +  const uint16_t *pmtud_probes; +  /** +   * :member:`pmtud_probeslen` is the number of elements that are +   * contained in the array pointed by :member:`pmtud_probes`.  This +   * field has been available since v1.4.0. +   */ +  size_t pmtud_probeslen; +} ngtcp2_settings; + +/** + * @struct + * + * :type:`ngtcp2_addr` is the endpoint address. + */ +typedef struct ngtcp2_addr { +  /** +   * :member:`addr` points to the buffer which contains endpoint +   * address.  It must not be ``NULL``. +   */ +  ngtcp2_sockaddr *addr; +  /** +   * :member:`addrlen` is the length of :member:`addr`.  It must not +   * be longer than sizeof(:type:`ngtcp2_sockaddr_union`). +   */ +  ngtcp2_socklen addrlen; +} ngtcp2_addr; + +/** + * @struct + * + * :type:`ngtcp2_path` is the network endpoints where a packet is sent + * and received. + */ +typedef struct ngtcp2_path { +  /** +   * :member:`local` is the address of local endpoint. +   */ +  ngtcp2_addr local; +  /** +   * :member:`remote` is the address of remote endpoint. +   */ +  ngtcp2_addr remote; +  /** +   * :member:`user_data` is an arbitrary data and opaque to the +   * library. +   * +   * Note that :type:`ngtcp2_path` is generally passed to +   * :type:`ngtcp2_conn` by an application, and :type:`ngtcp2_conn` +   * stores their copies.  Unfortunately, there is no way for the +   * application to know when :type:`ngtcp2_conn` finished using a +   * specific :type:`ngtcp2_path` object in mid connection, which +   * means that the application cannot free the data pointed by this +   * field.  Therefore, it is advised to use this field only when the +   * data pointed by this field persists in an entire lifetime of the +   * connection. +   */ +  void *user_data; +} ngtcp2_path; + +/** + * @struct + * + * :type:`ngtcp2_path_storage` is a convenient struct to have buffers + * to store the longest addresses. + */ +typedef struct ngtcp2_path_storage { +  /** +   * :member:`path` stores network path. +   */ +  ngtcp2_path path; +  /** +   * :member:`local_addrbuf` is a buffer to store local address. +   */ +  ngtcp2_sockaddr_union local_addrbuf; +  /** +   * :member:`remote_addrbuf` is a buffer to store remote address. +   */ +  ngtcp2_sockaddr_union remote_addrbuf; +} ngtcp2_path_storage; + +/** + * @struct + * + * :type:`ngtcp2_crypto_md` is a wrapper around native message digest + * object. + */ +typedef struct ngtcp2_crypto_md { +  /** +   * :member:`native_handle` is a pointer to an underlying message +   * digest object. +   */ +  void *native_handle; +} ngtcp2_crypto_md; + +/** + * @struct + * + * :type:`ngtcp2_crypto_aead` is a wrapper around native AEAD object. + */ +typedef struct ngtcp2_crypto_aead { +  /** +   * :member:`native_handle` is a pointer to an underlying AEAD +   * object. +   */ +  void *native_handle; +  /** +   * :member:`max_overhead` is the number of additional bytes which +   * AEAD encryption needs on encryption. +   */ +  size_t max_overhead; +} ngtcp2_crypto_aead; + +/** + * @struct + * + * :type:`ngtcp2_crypto_cipher` is a wrapper around native cipher + * object. + */ +typedef struct ngtcp2_crypto_cipher { +  /** +   * :member:`native_handle` is a pointer to an underlying cipher +   * object. +   */ +  void *native_handle; +} ngtcp2_crypto_cipher; + +/** + * @struct + * + * :type:`ngtcp2_crypto_aead_ctx` is a wrapper around native AEAD + * cipher context object.  It should be initialized with a specific + * key.  ngtcp2 library reuses this context object to encrypt or + * decrypt multiple packets. + */ +typedef struct ngtcp2_crypto_aead_ctx { +  /** +   * :member:`native_handle` is a pointer to an underlying AEAD +   * context object. +   */ +  void *native_handle; +} ngtcp2_crypto_aead_ctx; + +/** + * @struct + * + * :type:`ngtcp2_crypto_cipher_ctx` is a wrapper around native cipher + * context object.  It should be initialized with a specific key. + * ngtcp2 library reuses this context object to encrypt or decrypt + * multiple packet headers. + */ +typedef struct ngtcp2_crypto_cipher_ctx { +  /** +   * :member:`native_handle` is a pointer to an underlying cipher +   * context object. +   */ +  void *native_handle; +} ngtcp2_crypto_cipher_ctx; + +/** + * @struct + * + * :type:`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 1-RTT packets, use + * `ngtcp2_crypto_ctx_tls`.  For 0-RTT packets, use + * `ngtcp2_crypto_ctx_tls_early`. + */ +typedef struct ngtcp2_crypto_ctx { +  /** +   * :member:`aead` is AEAD object. +   */ +  ngtcp2_crypto_aead aead; +  /** +   * :member:`md` is message digest object. +   */ +  ngtcp2_crypto_md md; +  /** +   * :member:`hp` is header protection cipher. +   */ +  ngtcp2_crypto_cipher hp; +  /** +   * :member:`max_encryption` is the number of encryption which this +   * key can be used with. +   */ +  uint64_t max_encryption; +  /** +   * :member:`max_decryption_failure` is the number of decryption +   * failure with this key. +   */ +  uint64_t max_decryption_failure; +} ngtcp2_crypto_ctx; + +/** + * @function + * + * `ngtcp2_transport_params_encode` encodes |params| in |dest| of + * length |destlen|. + * + * If |dest| is NULL, and |destlen| is zero, this function just + * returns the number of bytes required to store the encoded transport + * parameters. + * + * This function returns the number of bytes written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_transport_params_encode_versioned( +  uint8_t *dest, size_t destlen, int transport_params_version, +  const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_transport_params_decode` decodes transport parameters in + * |data| of length |datalen|, and stores the result in the object + * pointed by |params|. + * + * If an optional parameter is missing, the default value is assigned. + * + * The following fields may point to somewhere inside the buffer + * pointed by |data| of length |datalen|: + * + * - :member:`ngtcp2_transport_params.version_info.available_versions + *   <ngtcp2_version_info.available_versions>` + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + *     The input is malformed. + */ +NGTCP2_EXTERN int +ngtcp2_transport_params_decode_versioned(int transport_params_version, +                                         ngtcp2_transport_params *params, +                                         const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_transport_params_decode_new` decodes transport parameters + * in |data| of length |datalen|, and stores the result in the object + * allocated dynamically.  The pointer to the allocated object is + * assigned to |*pparams|.  Unlike `ngtcp2_transport_params_decode`, + * all direct and indirect fields are also allocated dynamically if + * needed. + * + * |mem| is a memory allocator to allocate memory.  If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If the optional parameters are missing, the default value is + * assigned. + * + * `ngtcp2_transport_params_del` frees the memory allocated by this + * function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + *     The input is malformed. + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int +ngtcp2_transport_params_decode_new(ngtcp2_transport_params **pparams, +                                   const uint8_t *data, size_t datalen, +                                   const ngtcp2_mem *mem); + +/** + * @function + * + * `ngtcp2_transport_params_del` frees the |params| which must be + * dynamically allocated by `ngtcp2_transport_params_decode_new`. + * + * |mem| is a memory allocator that allocated |params|.  If |mem| is + * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()` + * is used. + * + * If |params| is ``NULL``, this function does nothing. + */ +NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params, +                                               const ngtcp2_mem *mem); + +/** + * @struct + * + * :type:`ngtcp2_version_cid` is a convenient struct to store the + * result of `ngtcp2_pkt_decode_version_cid`. + */ +typedef struct ngtcp2_version_cid { +  /** +   * :member:`version` stores QUIC version. +   */ +  uint32_t version; +  /** +   * :member:`dcid` points to the Destination Connection ID. +   */ +  const uint8_t *dcid; +  /** +   * :member:`dcidlen` is the length of the Destination Connection ID +   * pointed by :member:`dcid`. +   */ +  size_t dcidlen; +  /** +   * :member:`scid` points to the Source Connection ID. +   */ +  const uint8_t *scid; +  /** +   * :member:`scidlen` is the length of the Source Connection ID +   * pointed by :member:`scid`. +   */ +  size_t scidlen; +} ngtcp2_version_cid; + +/** + * @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 header packet, this function extracts + * the version from the packet, and assigns it to + * :member:`dest->version <ngtcp2_version_cid.version>`.  It also + * extracts the pointer to the Destination Connection ID and its + * length, and assigns them to :member:`dest->dcid + * <ngtcp2_version_cid.dcid>` and :member:`dest->dcidlen + * <ngtcp2_version_cid.dcidlen>` respectively.  Similarly, it extracts + * the pointer to the Source Connection ID and its length, and assigns + * them to :member:`dest->scid <ngtcp2_version_cid.scid>` and + * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` respectively. + * |short_dcidlen| is ignored. + * + * If the given packet is Short header packet, :member:`dest->version + * <ngtcp2_version_cid.version>` will be 0, :member:`dest->scid + * <ngtcp2_version_cid.scid>` will be ``NULL``, and + * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` will be 0. + * Because the Short header 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 :member:`dest->dcid + * <ngtcp2_version_cid.dcid>`.  |short_dcidlen| is assigned to + * :member:`dest->dcidlen <ngtcp2_version_cid.dcidlen>`. + * + * If Version Negotiation is required, this function returns + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`.  Unlike the other error + * cases, all fields of |dest| are assigned as described above. + * + * This function returns 0 if it succeeds.  Otherwise, one of the + * following negative error code: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     The function could not decode the packet header. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` + *     Version Negotiation packet should be sent. + */ +NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, +                                                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 + * :macro:`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_type.NGTCP2_PKT_VERSION_NEGOTIATION` to + * :member:`dest->type <ngtcp2_pkt_hd.type>`, clears + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags + * <ngtcp2_pkt_hd.flags>`, and sets 0 to :member:`dest->len + * <ngtcp2_pkt_hd.len>`.  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: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     Packet is too short; or it is not a long header + */ +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 header in |pkt| of + * length |pktlen|.  Short header packet does not encode the length of + * Connection ID, thus we need the input from the outside.  |dcidlen| + * is the length of Destination Connection ID in packet header.  This + * function only parses the input just before packet number field. + * This function can handle Connection ID up to + * :macro:`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: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     Packet is too short; or it is not a short header + */ +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_RESET_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: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     |randlen| is strictly less than + *     :macro:`NGTCP2_MIN_STATELESS_RESET_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 a + * Connection ID which appeared in a packet as a Source Connection ID + * sent by client which caused version negotiation.  Similarly, |scid| + * is a Connection ID which appeared 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: + * + * :macro:`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); + +/** + * @struct + * + * :type:`ngtcp2_conn` represents a single QUIC connection. + */ +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_install_initial_key`. + * + * This callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +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_install_initial_key`.  |dcid| is the Destination + * Connection ID in Initial packet received from client.  It is used + * to derive initial packet protection keys. + * + * The callback function must return 0 if it succeeds.  If an error + * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the + * library call return immediately. + */ +typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, +                                          const ngtcp2_cid *dcid, +                                          void *user_data); + +/** + * @enum + * + * :type:`ngtcp2_encryption_level` is QUIC encryption level. + */ +typedef enum ngtcp2_encryption_level { +  /** +   * :enum:`NGTCP2_ENCRYPTION_LEVEL_INITIAL` is Initial encryption +   * level. +   */ +  NGTCP2_ENCRYPTION_LEVEL_INITIAL, +  /** +   * :enum:`NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE` is Handshake encryption +   * level. +   */ +  NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE, +  /** +   * :enum:`NGTCP2_ENCRYPTION_LEVEL_1RTT` is 1-RTT encryption level. +   */ +  NGTCP2_ENCRYPTION_LEVEL_1RTT, +  /** +   * :enum:`NGTCP2_ENCRYPTION_LEVEL_0RTT` is 0-RTT encryption level. +   */ +  NGTCP2_ENCRYPTION_LEVEL_0RTT +} ngtcp2_encryption_level; + +/** + * @functypedef + * + * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is + * received.  The received data is pointed 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.  |encryption_level| indicates the encryption level + * where this data is received.  Crypto data can never be received in + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`. + * + * The application should provide the given data to TLS stack. + * + * The callback function must return 0 if it succeeds, or one of the + * following negative error codes: + * + * - :macro:`NGTCP2_ERR_CRYPTO` + * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + * - :macro:`NGTCP2_ERR_PROTO` + * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + * - :macro:`NGTCP2_ERR_NOMEM` + * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * + * If the other value is returned, it is treated as + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + * + * If application encounters fatal error, return + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, +                                       ngtcp2_encryption_level encryption_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 + * :macro:`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 + * :macro:`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 + * :macro:`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 use only. + * + * Application must regenerate packet protection key, IV, and header + * protection key for Initial packets using the Destination Connection + * ID obtained by :member:`hd->scid <ngtcp2_pkt_hd.scid>`, and install + * them by calling `ngtcp2_conn_install_initial_key`. + * + * 0-RTT data accepted by the ngtcp2 library will be automatically + * retransmitted as 0-RTT data by the library. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, +                                 void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the + * application to encrypt packet payload.  The packet payload to + * encrypt is passed as |plaintext| of length |plaintextlen|.  The + * AEAD cipher is |aead|.  |aead_ctx| is the AEAD cipher context + * object which is initialized with the specific encryption key.  The + * nonce is passed as |nonce| of length |noncelen|.  The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. + * + * 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 and any additional AEAD tag data. + * + * |dest| and |plaintext| may point to the same buffer. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                              const ngtcp2_crypto_aead_ctx *aead_ctx, +                              const uint8_t *plaintext, size_t plaintextlen, +                              const uint8_t *nonce, size_t noncelen, +                              const uint8_t *aad, size_t aadlen); + +/** + * @functypedef + * + * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the + * application to decrypt packet payload.  The packet payload to + * decrypt is passed as |ciphertext| of length |ciphertextlen|.  The + * AEAD cipher is |aead|.  |aead_ctx| is the AEAD cipher context + * object which is initialized with the specific decryption key.  The + * nonce is passed as |nonce| of length |noncelen|.  The Additional + * Authenticated Data is passed as |aad| of length |aadlen|. + * + * 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 :macro:`NGTCP2_ERR_DECRYPT`.  For any + * other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which + * makes the library call return immediately. + */ +typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                              const ngtcp2_crypto_aead_ctx *aead_ctx, +                              const uint8_t *ciphertext, size_t ciphertextlen, +                              const uint8_t *nonce, size_t noncelen, +                              const uint8_t *aad, size_t aadlen); + +/** + * @functypedef + * + * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the + * application to produce a mask to encrypt or decrypt packet header. + * The encryption cipher is |hp|.  |hp_ctx| is the cipher context + * object which is initialized with the specific header protection + * key.  The sample is passed as |sample| which is + * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. + * + * 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 + * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`.  The library + * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the + * produced mask.  The buffer pointed by |dest| is guaranteed to have + * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for + * convenience. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, +                              const ngtcp2_crypto_cipher_ctx *hp_ctx, +                              const uint8_t *sample); + +/** + * @macrosection + * + * STREAM frame data flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of + * data is final piece of an incoming stream. + */ +#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_0RTT` indicates that this chunk of + * data contains data received in 0-RTT packet, and the handshake has + * not completed yet, which means that the data might be replayed. + */ +#define NGTCP2_STREAM_DATA_FLAG_0RTT 0x02u + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stream_data` is invoked when stream data is + * received.  The stream is specified by |stream_id|.  |flags| is the + * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_* + * <NGTCP2_STREAM_DATA_FLAG_NONE>`.  If |flags| & + * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of + * the data is the last data in this stream.  |offset| is the offset + * where this data begins.  The library ensures that data is passed to + * the application in the non-decreasing order of |offset| without any + * overlap.  The data is passed as |data| of length |datalen|. + * |datalen| may be 0 if and only if |fin| is nonzero. + * + * If :macro:`NGTCP2_STREAM_DATA_FLAG_0RTT` is set in |flags|, it + * indicates that a part of or whole data was received in 0-RTT + * packet, and a handshake has not completed yet. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags, +                                       int64_t stream_id, uint64_t offset, +                                       const uint8_t *data, size_t datalen, +                                       void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_open` is a callback function which is called + * when remote stream is opened by a remote endpoint.  This function + * is not called if stream is opened by implicitly (we might + * reconsider this behaviour later). + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`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); + +/** + * @macrosection + * + * Stream close flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that + * app_error_code parameter is set. + */ +#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u + +/** + * @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.  |flags| is the bitwise-OR of zero or + * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_* + * <NGTCP2_STREAM_CLOSE_FLAG_NONE>`.  |app_error_code| indicates the + * error code of this closure if + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in + * |flags|.  If it is not set, the stream was closed without any error + * code, which generally means success. + * + * |app_error_code| is the first application error code sent by a + * local endpoint, or received from a remote endpoint.  If a stream is + * closed cleanly, no application error code is exchanged.  Since QUIC + * stack does not know the application error code which indicates "no + * errors", |app_error_code| is set to 0 and + * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is not set in + * |flags| in this case. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, uint32_t flags, +                                   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 :macro:`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 in range [|offset|, |offset| + + * |datalen|) is acknowledged, and application can free the portion of + * data.  For a given |stream_id|, this callback is called + * sequentially in increasing order of |offset| without any overlap. + * |datalen| is normally strictly greater than 0.  One exception is + * that when a STREAM frame has fin flag set and 0 length data, this + * callback is invoked with |datalen| == 0. + * + * If a stream is closed prematurely, and stream data is still + * in-flight, this callback function is not called for those data. + * After :member:`ngtcp2_callbacks.stream_close` is called for a + * particular stream, |conn| does not touch data for the closed stream + * again, and application can free all unacknowledged stream data. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_acked_stream_data_offset)( +  ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, +  void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_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 :macro:`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 + * :macro:`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 + * an endpoint can send on this stream. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`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 random data of + * length |destlen|.  Application must fill random |destlen| bytes to + * the buffer pointed by |dest|.  The generated data is used only in + * non-cryptographic context. + */ +typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen, +                            const ngtcp2_rand_ctx *rand_ctx); + +/** + * @functypedef + * + * :type:`ngtcp2_get_new_connection_id` is a callback function to ask + * an application for new connection ID.  Application must generate + * new unused connection ID with the exact |cidlen| bytes, and store + * it in |cid|.  It also has to generate a stateless reset token, and + * store it in |token|.  The length of stateless reset token is + * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that + * the buffer pointed by |token| has the sufficient space to store the + * token. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`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 a remote endpoint.  This Connection ID was previously offered by + * a local endpoint, and a remote endpoint could use it as Destination + * Connection ID when sending QUIC packet. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn, +                                           const ngtcp2_cid *cid, +                                           void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_update_key` is a callback function which tells the + * application that it must generate new packet protection keying + * materials and AEAD cipher context objects with new keys.  The + * current set of secrets are given as |current_rx_secret| and + * |current_tx_secret| of length |secretlen|.  They are decryption and + * encryption secrets respectively. + * + * The application must generate new secrets and keys for both + * encryption and decryption.  It must write decryption secret and IV + * to the buffer pointed by |rx_secret| and |rx_iv| respectively.  It + * also must create new AEAD cipher context object with new decryption + * key and initialize |rx_aead_ctx| with it.  Similarly, write + * encryption secret and IV to the buffer pointed by |tx_secret| and + * |tx_iv|.  Create new AEAD cipher context object with new encryption + * key and initialize |tx_aead_ctx| with it.  All given buffers have + * the enough capacity to store secret, key and IV. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_update_key)( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, +  ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, +  const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, +  size_t secretlen, void *user_data); + +/** + * @macrosection + * + * Path validation related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the + * validation involving server preferred address.  This flag is only + * set for client. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN` indicates that + * server should send NEW_TOKEN frame for the new remote address. + * This flag is only set for server. + */ +#define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02u + +/** + * @functypedef + * + * :type:`ngtcp2_path_validation` is a callback function which tells + * an application the outcome of path validation.  |flags| is zero or + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`.  |path| is the path that was + * validated.  |old_path| is the path that is previously used before a + * local endpoint has migrated to |path| if |old_path| is not NULL. + * If |res| is + * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, + * the path validation succeeded.  If |res| is + * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, + * the path validation failed. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags, +                                      const ngtcp2_path *path, +                                      const ngtcp2_path *old_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 a network path for a selected preferred address in |dest|. + * More specifically, the selected preferred address must be set to + * :member:`dest->remote <ngtcp2_path.remote>`, a client source + * address must be set to :member:`dest->local <ngtcp2_path.local>`. + * If a client source address does not change for the new server + * address, leave :member:`dest->local <ngtcp2_path.local>` + * unmodified, or copy the value of :member:`local + * <ngtcp2_path.local>` field of the current network path obtained + * from `ngtcp2_conn_get_path()`.  Both :member:`dest->local.addr + * <ngtcp2_addr.addr>` and :member:`dest->remote.addr + * <ngtcp2_addr.addr>` point to buffers which are at least + * sizeof(:type:`ngtcp2_sockaddr_union`) bytes long, respectively.  If + * an application denies the preferred addresses, just leave |dest| + * unmodified (or set :member:`dest->remote.addrlen + * <ngtcp2_addr.addrlen>` to 0), and return 0. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, +                                            ngtcp2_path *dest, +                                            const ngtcp2_preferred_addr *paddr, +                                            void *user_data); + +/** + * @enum + * + * :type:`ngtcp2_connection_id_status_type` defines a set of status + * for Destination Connection ID. + */ +typedef enum ngtcp2_connection_id_status_type { +  /** +   * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE` indicates that +   * a local endpoint starts using new Destination Connection ID. +   */ +  NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, +  /** +   * :enum:`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 Destination 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 + * :type:`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 + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_connection_id_status)( +  ngtcp2_conn *conn, ngtcp2_connection_id_status_type type, uint64_t seq, +  const ngtcp2_cid *cid, const uint8_t *token, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_new_token` is a callback function which is + * called when new token is received from server.  This callback is + * client use only. + * + * |token| is the received token of length |tokenlen| bytes long. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const uint8_t *token, +                                     size_t tokenlen, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which + * must delete the native object pointed by + * :member:`aead_ctx->native_handle + * <ngtcp2_crypto_aead_ctx.native_handle>`. + */ +typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, +                                              ngtcp2_crypto_aead_ctx *aead_ctx, +                                              void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function + * which must delete the native object pointed by + * :member:`cipher_ctx->native_handle + * <ngtcp2_crypto_cipher_ctx.native_handle>`. + */ +typedef void (*ngtcp2_delete_crypto_cipher_ctx)( +  ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); + +/** + * @macrosection + * + * DATAGRAM frame flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_DATAGRAM_FLAG_0RTT` indicates that DATAGRAM frame is + * received in 0-RTT packet, and the handshake has not completed yet, + * which means that the data might be replayed. + */ +#define NGTCP2_DATAGRAM_FLAG_0RTT 0x01u + +/** + * @functypedef + * + * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is + * received.  |flags| is bitwise-OR of zero or more of + * :macro:`NGTCP2_DATAGRAM_FLAG_* <NGTCP2_DATAGRAM_FLAG_NONE>`. + * + * If :macro:`NGTCP2_DATAGRAM_FLAG_0RTT` is set in |flags|, it + * indicates that DATAGRAM frame was received in 0-RTT packet, and a + * handshake has not completed yet. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags, +                                    const uint8_t *data, size_t datalen, +                                    void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_ack_datagram` is invoked when a packet which contains + * DATAGRAM frame which is identified by |dgram_id| is acknowledged. + * |dgram_id| is the valued passed to `ngtcp2_conn_writev_datagram`. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, +                                   void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_lost_datagram` is invoked when a packet which + * contains DATAGRAM frame which is identified by |dgram_id| is + * declared lost.  |dgram_id| is the valued passed to + * `ngtcp2_conn_writev_datagram`.  Note that the loss might be + * spurious, and DATAGRAM frame might be acknowledged later. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id, +                                    void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_get_path_challenge_data` is a callback function to + * ask an application for new data that is sent in PATH_CHALLENGE + * frame.  Application must generate new unpredictable, exactly + * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data, and + * store them into the buffer pointed by |data|. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data, +                                              void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_stop_sending` is invoked when a stream is no + * longer read by a local endpoint before it receives all stream data. + * This function is called at most once per stream.  |app_error_code| + * is the error code passed to `ngtcp2_conn_shutdown_stream_read` or + * `ngtcp2_conn_shutdown_stream`. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id, +                                          uint64_t app_error_code, +                                          void *user_data, +                                          void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_version_negotiation` is invoked when the compatible + * version negotiation takes place.  For client, it is called when it + * sees a change in version field of a long header packet.  This + * callback function might be called multiple times for client.  For + * server, it is called once when the version is negotiated. + * + * The implementation of this callback must install new Initial keys + * for |version| and Destination Connection ID |client_dcid| from + * client.  Use `ngtcp2_conn_install_vneg_initial_key` to install + * keys. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version, +                                          const ngtcp2_cid *client_dcid, +                                          void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_key` is invoked when new key is installed to + * |conn| during QUIC cryptographic handshake. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_encryption_level level, +                               void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_tls_early_data_rejected` is invoked when early data + * was rejected by server during TLS handshake, or client decided not + * to attempt early data. + * + * The callback function must return 0 if it succeeds.  Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_tls_early_data_rejected)(ngtcp2_conn *conn, +                                              void *user_data); + +#define NGTCP2_CALLBACKS_V1 1 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V1 + +/** + * @struct + * + * :type:`ngtcp2_callbacks` holds a set of callback functions. + */ +typedef struct ngtcp2_callbacks { +  /** +   * :member:`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 for +   * a client application. +   */ +  ngtcp2_client_initial client_initial; +  /** +   * :member:`recv_client_initial` is a callback function which is +   * invoked when a server receives the first Initial packet from +   * client.  This callback function must be specified for a server +   * application. +   */ +  ngtcp2_recv_client_initial recv_client_initial; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`encrypt` is a callback function which is invoked to +   * encrypt a QUIC packet.  This callback function must be specified. +   */ +  ngtcp2_encrypt encrypt; +  /** +   * :member:`decrypt` is a callback function which is invoked to +   * decrypt a QUIC packet.  This callback function must be specified. +   */ +  ngtcp2_decrypt decrypt; +  /** +   * :member:`hp_mask` is a callback function which is invoked to get +   * a mask to encrypt or decrypt QUIC packet header.  This callback +   * function must be specified. +   */ +  ngtcp2_hp_mask hp_mask; +  /** +   * :member:`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; +  /** +   * :member:`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 up to that offset.  This +   * callback function is optional. +   */ +  ngtcp2_acked_stream_data_offset acked_stream_data_offset; +  /** +   * :member:`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; +  /** +   * :member:`stream_close` is a callback function which is invoked +   * when a stream is closed.  This callback function is optional. +   */ +  ngtcp2_stream_close stream_close; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`rand` is a callback function which is invoked when the +   * library needs random data.  This callback function must be +   * specified. +   */ +  ngtcp2_rand rand; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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 callback function must be +   * specified. +   */ +  ngtcp2_update_key update_key; +  /** +   * :member:`path_validation` is a callback function which is invoked +   * when path validation completed.  This callback function is +   * optional. +   */ +  ngtcp2_path_validation path_validation; +  /** +   * :member:`select_preferred_addr` is a callback function which is +   * invoked when the library asks a client to select preferred +   * address presented by a server.  If not set, client ignores +   * preferred addresses.  This callback function is optional. +   */ +  ngtcp2_select_preferred_addr select_preferred_addr; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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; +  /** +   * :member:`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.  This +   * callback function is optional. +   */ +  ngtcp2_connection_id_status dcid_status; +  /** +   * :member:`handshake_confirmed` is a callback function which is +   * invoked when both endpoints agree that handshake has finished. +   * This field is ignored by server because +   * :member:`handshake_completed` also indicates the handshake +   * confirmation for server.  This callback function is optional. +   */ +  ngtcp2_handshake_confirmed handshake_confirmed; +  /** +   * :member:`recv_new_token` is a callback function which is invoked +   * when new token is received from server.  This field is ignored by +   * server.  This callback function is optional. +   */ +  ngtcp2_recv_new_token recv_new_token; +  /** +   * :member:`delete_crypto_aead_ctx` is a callback function which +   * deletes a given AEAD cipher context object.  This callback +   * function must be specified. +   */ +  ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx; +  /** +   * :member:`delete_crypto_cipher_ctx` is a callback function which +   * deletes a given cipher context object.  This callback function +   * must be specified. +   */ +  ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx; +  /** +   * :member:`recv_datagram` is a callback function which is invoked +   * when DATAGRAM frame is received.  This callback function is +   * optional. +   */ +  ngtcp2_recv_datagram recv_datagram; +  /** +   * :member:`ack_datagram` is a callback function which is invoked +   * when a QUIC packet containing DATAGRAM frame is acknowledged by a +   * remote endpoint.  This callback function is optional. +   */ +  ngtcp2_ack_datagram ack_datagram; +  /** +   * :member:`lost_datagram` is a callback function which is invoked +   * when a QUIC packet containing DATAGRAM frame is declared lost. +   * This callback function is optional. +   */ +  ngtcp2_lost_datagram lost_datagram; +  /** +   * :member:`get_path_challenge_data` is a callback function which is +   * invoked when the library needs new data sent along with +   * PATH_CHALLENGE frame.  This callback must be specified. +   */ +  ngtcp2_get_path_challenge_data get_path_challenge_data; +  /** +   * :member:`stream_stop_sending` is a callback function which is +   * invoked when a local endpoint no longer reads from a stream +   * before it receives all stream data.  This callback function is +   * optional. +   */ +  ngtcp2_stream_stop_sending stream_stop_sending; +  /** +   * :member:`version_negotiation` is a callback function which is +   * invoked when the compatible version negotiation takes place. +   * This callback function must be specified. +   */ +  ngtcp2_version_negotiation version_negotiation; +  /** +   * :member:`recv_rx_key` is a callback function which is invoked +   * when a new key for decrypting packets is installed during QUIC +   * cryptographic handshake.  It is not called for +   * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_INITIAL`. +   */ +  ngtcp2_recv_key recv_rx_key; +  /** +   * :member:`recv_tx_key` is a callback function which is invoked +   * when a new key for encrypting packets is installed during QUIC +   * cryptographic handshake.  It is not called for +   * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_INITIAL`. +   */ +  ngtcp2_recv_key recv_tx_key; +  /** +   * :member:`tls_early_data_rejected` is a callback function which is +   * invoked when server rejected early data during TLS handshake, or +   * client decided not to attempt early data.  This callback function +   * is only used by client. +   */ +  ngtcp2_tls_early_data_rejected tls_early_data_rejected; +} ngtcp2_callbacks; + +/** + * @function + * + * `ngtcp2_pkt_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE frame with the given |error_code| and + * the optional |reason| of length |reasonlen| 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 any 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: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( +  uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, +  size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, +  const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, +  const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed + * by |dest| whose length is |destlen|.  |dcid| is the Connection ID + * which appeared in a packet as a Source Connection ID sent by + * client.  |scid| is a server chosen Source Connection ID.  |odcid| + * specifies Original Destination Connection ID which appeared in a + * packet as a Destination Connection ID sent by client.  |token| + * specifies Retry Token, and |tokenlen| specifies its length.  |aead| + * must be AEAD_AES_128_GCM.  |aead_ctx| must be initialized with + * :macro:`NGTCP2_RETRY_KEY` as an encryption key. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     Callback function failed. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     :member:`odcid->datalen <ngtcp2_cid.datalen>` is less than + *     :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( +  uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, +  size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, +  const ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_accept` is used by server implementation, and decides + * whether packet |pkt| of length |pktlen| from client is acceptable + * for the very first packet to a connection. + * + * If |dest| is not ``NULL`` and the function returns 0, the decoded + * packet header is stored in the object pointed by |dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     The packet is not acceptable for the very first packet to a new + *     connection; or the function failed to parse the packet header. + */ +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.  On success, it stores the pointer to the + * newly allocated object in |*pconn|.  |dcid| is a randomized + * Destination Connection ID which must be longer than or equal to + * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`.  |scid| is a Source Connection + * ID chosen by client.  |client_chosen_version| is a QUIC version + * that a client chooses.  |path| is the network path where this QUIC + * connection is being established, and must not be ``NULL``. + * |callbacks|, |settings|, and |params| must not be ``NULL``, and the + * function makes a copy of each of them.  |params| is a local QUIC + * transport parameters, and sent to a remote endpoint during + * handshake.  |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. + * + * Call `ngtcp2_conn_del` to free memory allocated for |*pconn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned( +  ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, +  const ngtcp2_path *path, uint32_t client_chosen_version, +  int callbacks_version, const ngtcp2_callbacks *callbacks, +  int settings_version, const ngtcp2_settings *settings, +  int transport_params_version, const ngtcp2_transport_params *params, +  const ngtcp2_mem *mem, void *user_data); + +/** + * @function + * + * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and + * initializes it as server.  On success, it stores the pointer to the + * newly allocated object in |*pconn|.  |dcid| is a Destination + * Connection ID, and is usually the Connection ID that appears in + * client Initial packet as Source Connection ID.  |scid| is a Source + * Connection ID chosen by server.  |path| is the network path where + * this QUIC connection is being established, and must not be + * ``NULL``.  |client_chosen_version| is a QUIC version that a client + * chooses.  |callbacks|, |settings|, and |params| must not be + * ``NULL``, and the function makes a copy of each of them.  |params| + * is a local QUIC transport parameters, and sent to a remote endpoint + * during handshake.  |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. + * + * Call `ngtcp2_conn_del` to free memory allocated for |*pconn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned( +  ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, +  const ngtcp2_path *path, uint32_t client_chosen_version, +  int callbacks_version, const ngtcp2_callbacks *callbacks, +  int settings_version, const ngtcp2_settings *settings, +  int transport_params_version, const ngtcp2_transport_params *params, +  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``.  |pi| is packet + * metadata and may 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 one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_RETRY` + *    Server must perform address validation by sending Retry packet + *    (see `ngtcp2_crypto_write_retry` and `ngtcp2_pkt_write_retry`), + *    and discard the connection state.  Client application does not + *    get this error code. + * :macro:`NGTCP2_ERR_DROP_CONN` + *    Server application must drop the connection silently (without + *    sending any CONNECTION_CLOSE frame), and discard connection + *    state.  Client application does not get this error code. + * :macro:`NGTCP2_ERR_DRAINING` + *    A connection has entered the draining state, and no further + *    packet transmission is allowed. + * :macro:`NGTCP2_ERR_CLOSING` + *    A connection has entered the closing state, and no further + *    packet transmission is allowed.  Calling + *    `ngtcp2_conn_write_connection_close` makes a connection enter + *    this state. + * :macro:`NGTCP2_ERR_CRYPTO` + *    An error happened in TLS stack.  `ngtcp2_conn_get_tls_alert` + *    returns TLS alert if set. + * + * If any other negative error is returned, call + * `ngtcp2_conn_write_connection_close` to get terminal packet, and + * sending it makes QUIC connection enter the closing state. + */ +NGTCP2_EXTERN int +ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, +                               int pkt_info_version, const ngtcp2_pkt_info *pi, +                               const uint8_t *pkt, size_t pktlen, +                               ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_pkt` is equivalent to calling + * `ngtcp2_conn_writev_stream` with -1 as |stream_id|, no stream data, + * and :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_tls_handshake_completed` tells |conn| that the TLS + * stack declares TLS handshake completion.  This does not mean QUIC + * handshake has completed.  The library needs extra conditions to be + * met. + */ +NGTCP2_EXTERN void ngtcp2_conn_tls_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC + * handshake has completed. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_install_initial_key` installs packet protection keying + * materials for Initial packets.  |rx_aead_ctx| is AEAD cipher + * context object, and must be initialized with a decryption key. + * |rx_iv| is IV of length |rx_ivlen| for decryption.  |rx_hp_ctx| is + * a packet header protection cipher context object for decryption. + * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for + * encrypting outgoing packets, and are the same length with the + * decryption counterpart .  If they have already been set, they are + * overwritten. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * After receiving Retry packet, a Destination Connection ID that + * client sends in Initial packet most likely changes.  In that case, + * client application must generate these keying materials again based + * on new Destination Connection ID, and install them again with this + * function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, +  const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, +  const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, +  const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_vneg_initial_key` installs packet protection + * keying materials for Initial packets on compatible version + * negotiation for |version|.  |rx_aead_ctx| is AEAD cipher context + * object, and must be initialized with a decryption key.  |rx_iv| is + * IV of length |rx_ivlen| for decryption.  |rx_hp_ctx| is a packet + * header protection cipher context object for decryption.  Similarly, + * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing + * packets, and are the same length with the decryption counterpart. + * If they have already been set, they are overwritten. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key( +  ngtcp2_conn *conn, uint32_t version, +  const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, +  const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, +  const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, +  const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_rx_handshake_key` installs packet protection + * keying materials for decrypting incoming Handshake packets. + * |aead_ctx| is AEAD cipher context object which must be initialized + * with a decryption key.  |iv| is IV of length |ivlen|.  |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx|, + * and |hp_ctx|.  :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` + * and :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be + * called to delete these objects when they are no longer used.  If + * this function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_tx_handshake_key` installs packet protection + * keying materials for encrypting outgoing Handshake packets. + * |aead_ctx| is AEAD cipher context object which must be initialized + * with an encryption key.  |iv| is IV of length |ivlen|.  |hp_ctx| is + * a packet header protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|.  :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_0rtt_key` installs packet protection AEAD + * cipher context object |aead_ctx|, IV |iv| of length |ivlen|, and + * packet header protection cipher context object |hp_ctx| to encrypt + * (for client) or decrypt (for server) 0-RTT packets. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|.  :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_0rtt_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_rx_key` installs packet protection keying + * materials for decrypting 1-RTT packets.  |secret| of length + * |secretlen| is the decryption secret which is used to derive keying + * materials passed to this function.  |aead_ctx| is AEAD cipher + * context object which must be initialized with a decryption key. + * |iv| is IV of length |ivlen|.  |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|.  :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_rx_key( +  ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, +  const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, +  const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_tx_key` installs packet protection keying + * materials for encrypting 1-RTT packets.  |secret| of length + * |secretlen| is the encryption secret which is used to derive keying + * materials passed to this function.  |aead_ctx| is AEAD cipher + * context object which must be initialized with an encryption key. + * |iv| is IV of length |ivlen|.  |hp_ctx| is a packet header + * protection cipher context object. + * + * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if + * that is larger. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|.  :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` and + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` will be called + * to delete these objects when they are no longer used.  If this + * function fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_tx_key( +  ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, +  const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, +  const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_initiate_key_update` initiates the key update. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`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 |liberr| in + * |conn|.  |liberr| must be one of ngtcp2 library error codes (which + * is defined as NGTCP2_ERR_* macro, such as + * :macro:`NGTCP2_ERR_DECRYPT`).  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., + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`) using this function. + * + * See also `ngtcp2_conn_get_tls_error`. + */ +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_set_tls_alert` sets a TLS alert |alert| generated by a + * TLS stack of a local endpoint to |conn|. + * + * See also `ngtcp2_conn_get_tls_alert`. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert); + +/** + * @function + * + * `ngtcp2_conn_get_tls_alert` returns the value set by + * `ngtcp2_conn_set_tls_alert`.  If no value is set, this function + * returns 0. + */ +NGTCP2_EXTERN uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_keep_alive_timeout` sets keep-alive timeout.  If + * nonzero value is given, after a connection is idle at least in a + * given amount of time, a keep-alive packet is sent.  If UINT64_MAX + * is set, keep-alive functionality is disabled, and this is the + * default.  Specifying 0 in |timeout| is reserved for a future + * extension, and for now it is treated as if UINT64_MAX is given. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, +                                                      ngtcp2_duration timeout); + +/** + * @function + * + * `ngtcp2_conn_get_expiry` returns the next expiry time.  It returns + * ``UINT64_MAX`` if there is no next expiry. + * + * Call `ngtcp2_conn_handle_expiry` and then + * `ngtcp2_conn_writev_stream` (or `ngtcp2_conn_writev_datagram`) when + * the expiry time has passed. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_handle_expiry` handles expired timer. + */ +NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, +                                            ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO). + */ +NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_decode_and_set_remote_transport_params` decodes QUIC + * transport parameters from the buffer pointed by |data| of length + * |datalen|, and sets the result to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + *     The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + *     The input is malformed. + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + *     Failed to validate the remote QUIC transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + *     Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +NGTCP2_EXTERN int ngtcp2_conn_decode_and_set_remote_transport_params( +  ngtcp2_conn *conn, const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_get_remote_transport_params` returns a pointer to the + * remote QUIC transport parameters.  If no remote transport + * parameters are set, it returns NULL. + */ +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_encode_0rtt_transport_params` encodes the QUIC + * transport parameters that are used for 0-RTT data in the buffer + * pointed by |dest| of length |destlen|.  It includes at least the + * following fields: + * + * - :member:`ngtcp2_transport_params.initial_max_streams_bidi` + * - :member:`ngtcp2_transport_params.initial_max_streams_uni` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_uni` + * - :member:`ngtcp2_transport_params.initial_max_data` + * - :member:`ngtcp2_transport_params.active_connection_id_limit` + * - :member:`ngtcp2_transport_params.max_datagram_frame_size` + * + * If |conn| is initialized as server, the following additional fields + * are also included: + * + * - :member:`ngtcp2_transport_params.max_idle_timeout` + * - :member:`ngtcp2_transport_params.max_udp_payload_size` + * - :member:`ngtcp2_transport_params.disable_active_migration` + * + * If |conn| is initialized as client, these parameters are + * synthesized from the remote transport parameters received from + * server.  Otherwise, it is the local transport parameters that are + * set by the local endpoint. + * + * This function returns the number of bytes written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + */ +NGTCP2_EXTERN +ngtcp2_ssize ngtcp2_conn_encode_0rtt_transport_params(ngtcp2_conn *conn, +                                                      uint8_t *dest, +                                                      size_t destlen); + +/** + * @function + * + * `ngtcp2_conn_decode_and_set_0rtt_transport_params` decodes QUIC + * transport parameters from |data| of length |datalen|, which is + * assumed to be the parameters received from the server in the + * previous connection, and sets it to |conn|.  These parameters are + * used to send 0-RTT data.  QUIC requires that client application + * should remember transport parameters along with a session ticket. + * + * At least following fields should be included: + * + * - :member:`ngtcp2_transport_params.initial_max_streams_bidi` + * - :member:`ngtcp2_transport_params.initial_max_streams_uni` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote` + * - :member:`ngtcp2_transport_params.initial_max_stream_data_uni` + * - :member:`ngtcp2_transport_params.initial_max_data` + * - :member:`ngtcp2_transport_params.active_connection_id_limit` + * - :member:`ngtcp2_transport_params.max_datagram_frame_size` (if + *   DATAGRAM extension was negotiated) + * + * This function must only be used by client. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + *     The input is malformed. + */ +NGTCP2_EXTERN int ngtcp2_conn_decode_and_set_0rtt_transport_params( +  ngtcp2_conn *conn, const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_set_local_transport_params` sets the local transport + * parameters |params|.  This function can only be called by server. + * Although the local transport parameters are passed to + * `ngtcp2_conn_server_new`, server might want to update them after + * ALPN is chosen.  In that case, server can update the transport + * parameters with this function.  Server must call this function + * before calling `ngtcp2_conn_install_tx_handshake_key`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     `ngtcp2_conn_install_tx_handshake_key` has been called. + */ +NGTCP2_EXTERN int ngtcp2_conn_set_local_transport_params_versioned( +  ngtcp2_conn *conn, int transport_params_version, +  const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_get_local_transport_params` returns a pointer to the + * local QUIC transport parameters. + */ +NGTCP2_EXTERN const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_encode_local_transport_params` encodes the local QUIC + * transport parameters in |dest| of length |destlen|. + * + * This function returns the number of bytes written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_encode_local_transport_params( +  ngtcp2_conn *conn, uint8_t *dest, size_t destlen); + +/** + * @function + * + * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream.  The + * |stream_user_data| is the user data specific to the stream.  The + * stream ID of the opened stream is stored in |*pstream_id|. + * + * Application can call this function before handshake completes.  For + * 0-RTT packet, application can call this function after calling + * `ngtcp2_conn_decode_and_set_0rtt_transport_params`.  For 1-RTT + * packet, application can call this function after calling + * `ngtcp2_conn_decode_and_set_remote_transport_params` and + * `ngtcp2_conn_install_tx_key`.  If ngtcp2 crypto support library is + * used, application can call this function after calling + * `ngtcp2_crypto_derive_and_install_tx_key` for 1-RTT packet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` + *     The remote endpoint 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 + * stream ID of the opened stream is stored in |*pstream_id|. + * + * Application can call this function before handshake completes.  For + * 0-RTT packet, application can call this function after calling + * `ngtcp2_conn_decode_and_set_0rtt_transport_params`.  For 1-RTT + * packet, application can call this function after calling + * `ngtcp2_conn_decode_and_set_remote_transport_params` and + * `ngtcp2_conn_install_tx_key`.  If ngtcp2 crypto support library is + * used, application can call this function after calling + * `ngtcp2_crypto_derive_and_install_tx_key` for 1-RTT packet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` + *     The remote endpoint 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 a 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 with the following + * differences.  If |stream_id| refers to a local unidirectional + * stream, this function only shutdowns write side of the stream.  If + * |stream_id| refers to a remote unidirectional stream, this function + * only shutdowns read side of the stream. + * + * |flags| is currently unused, and should be set to 0. + * + * This function returns 0 if a stream denoted by |stream_id| is not + * found. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, uint32_t flags, +                                              int64_t stream_id, +                                              uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_write` closes write-side of a 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 further application data is sent to the + * remote endpoint.  It discards all data which has not been + * acknowledged yet. + * + * |flags| is currently unused, and should be set to 0. + * + * This function returns 0 if a stream denoted by |stream_id| is not + * found. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     |stream_id| refers to a remote unidirectional stream. + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, +                                                    uint32_t flags, +                                                    int64_t stream_id, +                                                    uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_read` closes read-side of a 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 further application data is forwarded to + * an application layer. + * + * |flags| is currently unused, and should be set to 0. + * + * This function returns 0 if a stream denoted by |stream_id| is not + * found. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     |stream_id| refers to a local unidirectional stream. + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, +                                                   uint32_t flags, +                                                   int64_t stream_id, +                                                   uint64_t app_error_code); + +/** + * @macrosection + * + * Write stream data flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may + * come, and should be coalesced into the same packet if possible. + */ +#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that a passed data + * is the final part of a stream. + */ +#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u + +/** + * @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_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, +  uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_writev_stream` writes a packet containing stream data + * of a stream denoted by |stream_id|.  The buffer of the packet is + * pointed by |dest| of length |destlen|.  This function performs QUIC + * handshake as well. + * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_tx_udp_payload_size`.  It must be at + * least :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * 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 + * (:member:`ngtcp2_path.local` and :member:`ngtcp2_path.remote`) must + * point to the buffer which should be at least + * sizeof(:type:`sockaddr_union`) bytes long.  The assignment might + * not be done if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds.  The metadata includes ECN markings.  When calling + * this function again after it returns + * :macro:`NGTCP2_ERR_WRITE_MORE`, caller must pass the same |pi| to + * this function. + * + * Stream data is specified as vector of data |datav|.  |datavcnt| + * specifies the number of :type:`ngtcp2_vec` that |datav| includes. + * + * If all given data is encoded as STREAM frame in |dest|, and if + * |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, fin + * flag is set to outgoing STREAM frame.  Otherwise, fin flag in + * STREAM frame is not set. + * + * This packet may contain frames other than STREAM frame.  The packet + * might not contain STREAM frame if other frames occupy the packet. + * In that case, |*pdatalen| would be -1 if |pdatalen| is not + * ``NULL``. + * + * Empty data is treated specially, and it is only accepted if no + * data, including the empty data, is submitted to a stream or + * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is set in |flags|.  If 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 + * :member:`ngtcp2_callbacks.acked_stream_data_offset` indicates that + * they are acknowledged by a remote endpoint or the stream is closed. + * + * 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 + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|.  This is + * analogous to ``MSG_MORE`` flag in :manpage:`send(2)`.  If the + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * outcomes: + * + * - The function returns the written length of packet just like + *   without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`.  This is because + *   packet is nearly full, and the library decided to make a complete + *   packet.  |*pdatalen| might be -1 or >= 0.  It may return 0 which + *   indicates that no packet transmission is possible at the moment + *   for some reason. + * + * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`.  In this + *   case, |*pdatalen| >= 0 is asserted.  It indicates that + *   application can still call this function with different stream + *   data (or `ngtcp2_conn_writev_datagram` if it has data to send in + *   unreliable datagram) to pack them into the same packet. + *   Application has to specify the same |conn|, |path|, |pi|, |dest|, + *   |destlen|, and |ts| parameters, otherwise the behaviour is + *   undefined.  The application can change |flags|. + * + * - The function returns one of the following negative error codes: + *   :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`, + *   :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`, or + *   :macro:`NGTCP2_ERR_STREAM_SHUT_WR`.  In this case, |*pdatalen| == + *   -1 is asserted.  Application can still write the stream data of + *   the other streams by calling this function (or + *   `ngtcp2_conn_writev_datagram` if it has data to send in + *   unreliable datagram) to pack them into the same packet. + *   Application has to specify the same |conn|, |path|, |pi|, |dest|, + *   |destlen|, and |ts| parameters, otherwise the behaviour is + *   undefined.  The application can change |flags|. + * + * - The other negative error codes might be returned just like + *   without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`.  These errors + *   should be treated as a connection error. + * + * When application uses :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` at + * least once, it must not call other ngtcp2 API functions + * (application can still call `ngtcp2_conn_write_connection_close` to + * handle error from this function.  It can also call + * `ngtcp2_conn_shutdown_stream_read`, + * `ngtcp2_conn_shutdown_stream_write`, and + * `ngtcp2_conn_shutdown_stream`), just keep calling this function (or + * `ngtcp2_conn_writev_datagram`) until it returns 0, a positive + * number (which indicates a complete packet is ready), or the error + * codes other than :macro:`NGTCP2_ERR_WRITE_MORE`, + * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`, + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`, and + * :macro:`NGTCP2_ERR_STREAM_SHUT_WR`.  If there is no stream data to + * include, call this function with |stream_id| as -1 to stop + * coalescing and write a 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. + * + * `ngtcp2_conn_update_pkt_tx_time` must be called after this + * function.  Application may call this function multiple times before + * calling `ngtcp2_conn_update_pkt_tx_time`. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + *     Stream does not exist + * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` + *     Stream is half closed (local); or stream is being reset. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + *     Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     The total length of stream data is too large. + * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` + *     Stream is blocked because of flow control. + * :macro:`NGTCP2_ERR_WRITE_MORE` + *     (Only when :macro:`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. + * + * If any other negative error is returned, call + * `ngtcp2_conn_write_connection_close` to get terminal packet, and + * sending it makes QUIC connection enter the closing state. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, +  uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, +  ngtcp2_tstamp ts); + +/** + * @macrosection + * + * Write datagram flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data + * may come, and should be coalesced into the same packet if possible. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u + +/** + * @function + * + * `ngtcp2_conn_write_datagram` is just like + * `ngtcp2_conn_writev_datagram`.  The only difference is that it + * conveniently accepts a single buffer. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, +  uint32_t flags, uint64_t dgram_id, const uint8_t *data, size_t datalen, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_writev_datagram` writes a packet containing unreliable + * data in DATAGRAM frame.  The buffer of the packet is pointed by + * |dest| of length |destlen|.  This function performs QUIC handshake + * as well. + * + * |destlen| should be at least + * :member:`ngtcp2_settings.max_tx_udp_payload_size`.  It must be at + * least :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * For |path| and |pi| parameters, refer to + * `ngtcp2_conn_writev_stream`. + * + * Stream data is specified as vector of data |datav|.  |datavcnt| + * specifies the number of :type:`ngtcp2_vec` that |datav| includes. + * + * If the given data is written to the buffer, nonzero value is + * assigned to |*paccepted| if it is not NULL.  The data in DATAGRAM + * frame cannot be fragmented; writing partial data is not possible. + * + * |dgram_id| is an opaque identifier which should uniquely identify + * the given DATAGRAM data.  It is passed to + * :member:`ngtcp2_callbacks.ack_datagram` callback when a packet that + * contains DATAGRAM frame is acknowledged.  It is also passed to + * :member:`ngtcp2_callbacks.lost_datagram` callback when a packet + * that contains DATAGRAM frame is declared lost.  If an application + * uses neither of those callbacks, it can sets 0 to this parameter. + * + * This function might write other frames other than DATAGRAM frame, + * just like `ngtcp2_conn_writev_stream`. + * + * If the function returns 0, it means that no more data cannot be + * sent because of congestion control limit; or, data does not fit + * into the provided buffer; or, a local endpoint, as a server, is + * unable to send data because of its amplification limit.  In this + * case, |*paccepted| is assigned zero if it is not NULL. + * + * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is set in |flags|, + * there are 3 outcomes: + * + * - The function returns the written length of packet just like + *   without :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`.  This is + *   because packet is nearly full and the library decided to make a + *   complete packet.  |*paccepted| might be zero or nonzero. + * + * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`.  In this + *   case, |*paccepted| != 0 is asserted.  This indicates that + *   application can call this function with another unreliable data + *   (or `ngtcp2_conn_writev_stream` if it has stream data to send) to + *   pack them into the same packet.  Application has to specify the + *   same |conn|, |path|, |pi|, |dest|, |destlen|, and |ts| + *   parameters, otherwise the behaviour is undefined.  The + *   application can change |flags|. + * + * - The other error might be returned just like without + *   :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`. + * + * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not + * call other ngtcp2 API functions (application can still call + * `ngtcp2_conn_write_connection_close` to handle error from this + * function.  It can also call `ngtcp2_conn_shutdown_stream_read`, + * `ngtcp2_conn_shutdown_stream_write`, and + * `ngtcp2_conn_shutdown_stream`).  Just keep calling this function + * (or `ngtcp2_conn_writev_stream`) until it returns a positive number + * (which indicates a complete packet is ready). + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + *     Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + * :macro:`NGTCP2_ERR_WRITE_MORE` + *     (Only when :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is + *     specified) Application can call this function to pack more data + *     into the same packet.  See above to know how it works. + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     A remote endpoint did not express the DATAGRAM frame support. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     The provisional DATAGRAM frame size exceeds the maximum + *     DATAGRAM frame size that a remote endpoint can receive. + * + * If any other negative error is returned, call + * `ngtcp2_conn_write_connection_close` to get terminal packet, and + * sending it makes QUIC connection enter the closing state. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, +  uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_in_closing_period` returns nonzero if |conn| is in the + * closing period. + */ +NGTCP2_EXTERN int ngtcp2_conn_in_closing_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_in_draining_period` returns nonzero if |conn| is in + * the draining period. + */ +NGTCP2_EXTERN int ngtcp2_conn_in_draining_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_extend_max_stream_offset` extends the maximum stream + * data that a remote endpoint can send by |datalen|.  |stream_id| + * specifies the stream ID.  This function only extends stream-level + * flow control window. + * + * This function returns 0 if a stream denoted by |stream_id| is not + * found. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     |stream_id| refers to a local unidirectional stream. + */ +NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, +                                                       int64_t stream_id, +                                                       uint64_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_offset` extends max data offset by + * |datalen|.  This function only extends connection-level flow + * control window. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, +                                                 uint64_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum + * remote 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 + * :member:`ngtcp2_callbacks.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 + * remote 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 + * :member:`ngtcp2_callbacks.stream_open` callback being called.  In + * this case, stream limit is increased automatically. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, +                                                      size_t n); + +/** + * @function + * + * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to the current + * Destination Connection ID.  If no Destination Connection ID is + * present, the return value is not ``NULL``, and its :member:`datalen + * <ngtcp2_cid.datalen>` field is 0. + */ +NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_client_initial_dcid` returns the non-NULL pointer + * to the Destination Connection ID that client sent in its Initial + * packet.  If the Destination Connection ID is not present, the + * return value is not ``NULL``, and its :member:`datalen + * <ngtcp2_cid.datalen>` field is 0. + */ +NGTCP2_EXTERN const ngtcp2_cid * +ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_scid` writes the all Source Connection IDs which a + * local endpoint has provided to a remote endpoint, and are not + * retired in |dest|.  If |dest| is NULL, this function does not write + * anything, and returns the number of Source Connection IDs that + * would otherwise be written to the provided buffer.  The buffer + * pointed by |dest| must have sizeof(:type:`ngtcp2_cid`) * n bytes + * available, where n is the return value of `ngtcp2_conn_get_scid` + * with |dest| == NULL. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest); + +/** + * @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 { +  /** +   * :member:`seq` is the sequence number of this Connection ID. +   */ +  uint64_t seq; +  /** +   * :member:`cid` is Connection ID. +   */ +  ngtcp2_cid cid; +  /** +   * :member:`ps` is the path which this Connection ID is associated +   * with. +   */ +  ngtcp2_path_storage ps; +  /** +   * :member:`token` is the stateless reset token for this Connection +   * ID. +   */ +  uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +  /** +   * :member:`token_present` 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 their tokens to |dest|.  Before handshake + * completes, this function returns 0.  If |dest| is NULL, this + * function does not write anything, and returns the number of + * Destination Connection IDs that would otherwise be written to the + * provided buffer.  The buffer pointed by |dest| must have + * sizeof(:type:`ngtcp2_cid_token`) * n bytes available, where n is + * the return value of `ngtcp2_conn_get_active_dcid` with |dest| == + * NULL. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, +                                                 ngtcp2_cid_token *dest); + +/** + * @function + * + * `ngtcp2_conn_get_client_chosen_version` returns the client chosen + * version. + */ +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_negotiated_version` returns the negotiated + * version. + * + * Until the version is negotiated, this function returns 0. + */ +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_tls_early_data_rejected` tells |conn| that early data + * was rejected by a server during TLS handshake, or client decided + * not to attempt early data for some reason.  |conn| discards the + * following connection states: + * + * - Any opened streams. + * - Stream identifier allocations. + * - Max data extended by `ngtcp2_conn_extend_max_offset`. + * - Max bidi streams extended by `ngtcp2_conn_extend_max_streams_bidi`. + * - Max uni streams extended by `ngtcp2_conn_extend_max_streams_uni`. + * + * Application which wishes to retransmit early data, it has to open + * streams, and send stream data again. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +NGTCP2_EXTERN int ngtcp2_conn_tls_early_data_rejected(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_tls_early_data_rejected` returns nonzero if + * `ngtcp2_conn_tls_early_data_rejected` has been called. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_tls_early_data_rejected(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_conn_info` assigns connection statistics data to + * |*cinfo|. + */ +NGTCP2_EXTERN void ngtcp2_conn_get_conn_info_versioned(ngtcp2_conn *conn, +                                                       int conn_info_version, +                                                       ngtcp2_conn_info *cinfo); + +/** + * @function + * + * `ngtcp2_conn_submit_crypto_data` submits crypto data |data| of + * length |datalen| to the library for transmission. + * |encryption_level| specifies the encryption level of data. + * + * The library makes a copy of the buffer pointed by |data| of length + * |datalen|.  Application can discard |data|. + */ +NGTCP2_EXTERN int +ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, +                               ngtcp2_encryption_level encryption_level, +                               const uint8_t *data, const size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_submit_new_token` submits address validation token. + * It is sent in NEW_TOKEN frame.  Only server can call this function. + * |tokenlen| must not be 0. + * + * This function makes a copy of the buffer pointed by |token| of + * length |tokenlen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, +                                               const uint8_t *token, +                                               size_t tokenlen); + +/** + * @function + * + * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to + * the current path of |conn|.  This function is provided for testing + * purpose only. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, +                                              const ngtcp2_addr *addr); + +/** + * @function + * + * `ngtcp2_conn_set_path_user_data` sets the |path_user_data| to the + * current path (see :member:`ngtcp2_path.user_data`). + */ +NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, +                                                  void *path_user_data); + +/** + * @function + * + * `ngtcp2_conn_get_path` returns the current path. + */ +NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_max_tx_udp_payload_size` returns the maximum UDP + * payload size that this local endpoint would send.  This is the + * value of :member:`ngtcp2_settings.max_tx_udp_payload_size` that is + * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_max_tx_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_path_max_tx_udp_payload_size` returns the maximum + * UDP payload size for the current path.  If + * :member:`ngtcp2_settings.no_tx_udp_payload_size_shaping` is set to + * nonzero, this function is equivalent to + * `ngtcp2_conn_get_max_tx_udp_payload_size`.  Otherwise, it returns + * the maximum UDP payload size that is probed for the current path. + */ +NGTCP2_EXTERN size_t +ngtcp2_conn_get_path_max_tx_udp_payload_size(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_initiate_immediate_migration` starts connection + * migration to the given |path|.  Only client can initiate migration. + * This function does immediate migration; while the path validation + * is nonetheless performed, this function does not wait for it to + * succeed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     Migration is disabled; or handshake is not yet confirmed; or + *     client is migrating to server's preferred address. + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` + *     No unused connection ID is available. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     :member:`local <ngtcp2_path.local>` field of |path| equals the + *     current local address. + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_immediate_migration( +  ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_initiate_migration` starts connection migration to the + * given |path|.  Only client can initiate migration.  Unlike + * `ngtcp2_conn_initiate_immediate_migration`, this function starts a + * path validation with a new path, and migrate to the new path after + * successful path validation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     Migration is disabled; or handshake is not yet confirmed; or + *     client is migrating to server's preferred address. + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` + *     No unused connection ID is available. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + *     :member:`local <ngtcp2_path.local>` field of |path| equals the + *     current local address. + * :macro:`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_data_left` returns the number of bytes that + * this local endpoint can send in this connection without violating + * connection-level flow control. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_max_stream_data_left` returns the number of bytes + * that this local endpoint can send to a stream identified by + * |stream_id| without violating stream-level flow control.  If no + * such stream is found, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, +                                                            int64_t stream_id); + +/** + * @function + * + * `ngtcp2_conn_get_streams_bidi_left` returns the number of + * bidirectional streams which the local endpoint can open without + * violating stream concurrency limit. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_streams_uni_left` returns the number of + * unidirectional streams which the local endpoint can open without + * violating stream concurrency limit. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of + * bytes in flight on the current path.  If the former is smaller than + * the latter, this function returns 0. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_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_crypto_ctx` sets |ctx| for Handshake/1-RTT 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_crypto_ctx(ngtcp2_conn *conn, +                                              const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` + * object for Handshake/1-RTT packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_0rtt_crypto_ctx` sets |ctx| for 0-RTT 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_0rtt_crypto_ctx(ngtcp2_conn *conn, +                                const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_0rtt_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` + * object for 0-RTT packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_0rtt_crypto_ctx(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_tls_native_handle` returns TLS native handle set + * by `ngtcp2_conn_set_tls_native_handle`. + */ +NGTCP2_EXTERN void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_tls_native_handle` sets TLS native handle + * |tls_native_handle| to |conn|.  Internally, it is used as an opaque + * pointer. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, +                                                     void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_conn_set_retry_aead` sets |aead| and |aead_ctx| for Retry + * integrity tag verification.  |aead| must be AEAD_AES_128_GCM. + * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as + * encryption key.  This function must be called if |conn| is + * initialized as client.  Server does not verify the tag, and has no + * need to call this function. + * + * |conn| takes ownership of |aead_ctx|. + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` will be called to + * delete this object when it is no longer used. + */ +NGTCP2_EXTERN void +ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, +                           const ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @enum + * + * :type:`ngtcp2_ccerr_type` defines connection error type. + */ +typedef enum ngtcp2_ccerr_type { +  /** +   * :enum:`NGTCP2_CCERR_TYPE_TRANSPORT` indicates the QUIC transport +   * error, and the error code is QUIC transport error code. +   */ +  NGTCP2_CCERR_TYPE_TRANSPORT, +  /** +   * :enum:`NGTCP2_CCERR_TYPE_APPLICATION` indicates an application +   * error, and the error code is application error code. +   */ +  NGTCP2_CCERR_TYPE_APPLICATION, +  /** +   * :enum:`NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION` is a special case +   * of QUIC transport error, and it indicates that client receives +   * Version Negotiation packet. +   */ +  NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION, +  /** +   * :enum:`NGTCP2_CCERR_TYPE_IDLE_CLOSE` is a special case of QUIC +   * transport error, and it indicates that connection is closed +   * because of idle timeout. +   */ +  NGTCP2_CCERR_TYPE_IDLE_CLOSE, +  /** +   * :enum:`NGTCP2_CCERR_TYPE_DROP_CONN` is a special case of QUIC +   * transport error, and it indicates that connection should be +   * dropped without sending a CONNECTION_CLOSE frame. +   */ +  NGTCP2_CCERR_TYPE_DROP_CONN, +  /** +   * :enum:`NGTCP2_CCERR_TYPE_RETRY` is a special case of QUIC +   * transport error, and it indicates that RETRY packet should be +   * sent to a client. +   */ +  NGTCP2_CCERR_TYPE_RETRY +} ngtcp2_ccerr_type; + +/** + * @struct + * + * :type:`ngtcp2_ccerr` contains connection error code, its type, a + * frame type that caused this error, and the optional reason phrase. + */ +typedef struct ngtcp2_ccerr { +  /** +   * :member:`type` is the type of this error. +   */ +  ngtcp2_ccerr_type type; +  /** +   * :member:`error_code` is the error code for connection closure. +   * Its interpretation depends on :member:`type`. +   */ +  uint64_t error_code; +  /** +   * :member:`frame_type` is the type of QUIC frame which triggers +   * this connection error.  This field is set to 0 if the frame type +   * is unknown. +   */ +  uint64_t frame_type; +  /** +   * :member:`reason` points to the buffer which contains a reason +   * phrase.  It may be NULL if there is no reason phrase.  If it is +   * received from a remote endpoint, it is truncated to at most 1024 +   * bytes. +   */ +  const uint8_t *reason; +  /** +   * :member:`reasonlen` is the length of data pointed by +   * :member:`reason`. +   */ +  size_t reasonlen; +} ngtcp2_ccerr; + +/** + * @function + * + * `ngtcp2_ccerr_default` initializes |ccerr| with the default values. + * It sets the following fields: + * + * - :member:`type <ngtcp2_ccerr.type>` = + *   :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT` + * - :member:`error_code <ngtcp2_ccerr.error_code>` = + *   :macro:`NGTCP2_NO_ERROR`. + * - :member:`frame_type <ngtcp2_ccerr.frame_type>` = 0 + * - :member:`reason <ngtcp2_ccerr.reason>` = NULL + * - :member:`reasonlen <ngtcp2_ccerr.reasonlen>` = 0 + */ +NGTCP2_EXTERN void ngtcp2_ccerr_default(ngtcp2_ccerr *ccerr); + +/** + * @function + * + * `ngtcp2_ccerr_set_transport_error` sets :member:`ccerr->type + * <ngtcp2_ccerr.type>` to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to + * |error_code|.  |reason| is the reason phrase of length |reasonlen|. + * This function does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_ccerr_set_transport_error(ngtcp2_ccerr *ccerr, +                                                    uint64_t error_code, +                                                    const uint8_t *reason, +                                                    size_t reasonlen); + +/** + * @function + * + * `ngtcp2_ccerr_set_liberr` sets type and error_code based on + * |liberr|. + * + * |reason| is the reason phrase of length |reasonlen|.  This function + * does not make a copy of the reason phrase. + * + * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`, + * :member:`ccerr->type <ngtcp2_ccerr.type>` is set to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION`, + * and :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. + * + * If |liberr| is :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type + * <ngtcp2_ccerr.type>` is set to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_IDLE_CLOSE`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. + * + * If |liberr| is :macro:`NGTCP2_ERR_DROP_CONN`, :member:`ccerr->type + * <ngtcp2_ccerr.type>` is set to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_DROP_CONN`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. + * + * If |liberr| is :macro:`NGTCP2_ERR_RETRY`, :member:`ccerr->type + * <ngtcp2_ccerr.type>` is set to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_RETRY`, and + * :member:`ccerr->error_type <ngtcp2_ccerr.error_code>` to + * :macro:`NGTCP2_NO_ERROR`. + * + * Otherwise, :member:`ccerr->type <ngtcp2_ccerr.type>` is set to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` is set to an + * error code inferred by |liberr| (see + * `ngtcp2_err_infer_quic_transport_error_code`). + */ +NGTCP2_EXTERN void ngtcp2_ccerr_set_liberr(ngtcp2_ccerr *ccerr, int liberr, +                                           const uint8_t *reason, +                                           size_t reasonlen); + +/** + * @function + * + * `ngtcp2_ccerr_set_tls_alert` sets :member:`ccerr->type + * <ngtcp2_ccerr.type>` to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to bitwise-OR + * of :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|.  |reason| is the + * reason phrase of length |reasonlen|.  This function does not make a + * copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_ccerr_set_tls_alert(ngtcp2_ccerr *ccerr, +                                              uint8_t tls_alert, +                                              const uint8_t *reason, +                                              size_t reasonlen); + +/** + * @function + * + * `ngtcp2_ccerr_set_application_error` sets :member:`ccerr->type + * <ngtcp2_ccerr.type>` to + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_APPLICATION`, and + * :member:`ccerr->error_code <ngtcp2_ccerr.error_code>` to + * |error_code|.  |reason| is the reason phrase of length |reasonlen|. + * This function does not make a copy of the reason phrase. + */ +NGTCP2_EXTERN void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr, +                                                      uint64_t error_code, +                                                      const uint8_t *reason, +                                                      size_t reasonlen); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed + * by |dest| whose capacity is |destlen|. + * + * For client, |destlen| should be at least + * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`. + * + * 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 should be at least + * sizeof(:type:`ngtcp2_sockaddr_union`) bytes long.  The assignment + * might not be done if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds.  The metadata includes ECN markings. + * + * If :member:`ccerr->type <ngtcp2_ccerr.type>` == + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_TRANSPORT`, this + * function sends CONNECTION_CLOSE (type 0x1c) frame.  If + * :member:`ccerr->type <ngtcp2_ccerr.type>` == + * :enum:`ngtcp2_ccerr_type.NGTCP2_CCERR_TYPE_APPLICATION`, it sends + * CONNECTION_CLOSE (type 0x1d) frame.  Otherwise, it does not produce + * any data, and returns 0. + * + * |destlen| could be shorten by some factors (e.g., server side + * amplification limit).  This function returns + * :macro:`NGTCP2_ERR_NOBUF` if the resulting buffer is too small even + * if the given buffer has enough space. + * + * 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. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     The current state does not allow sending CONNECTION_CLOSE + *     frame. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + *     Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, const ngtcp2_ccerr *ccerr, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_ccerr` returns the received connection close + * error.  If no connection error is received, it returns + * :type:`ngtcp2_ccerr` that is initialized by `ngtcp2_ccerr_default`. + */ +NGTCP2_EXTERN const ngtcp2_ccerr *ngtcp2_conn_get_ccerr(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| + * denotes a locally initiated stream. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, +                                              int64_t stream_id); + +/** + * @function + * + * `ngtcp2_conn_is_server` returns nonzero if |conn| is initialized as + * server. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_server(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_after_retry` returns nonzero if |conn| as a client has + * received Retry packet from server, and successfully validated it. + */ +NGTCP2_EXTERN int ngtcp2_conn_after_retry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_stream_user_data` sets |stream_user_data| to the + * stream identified by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + *     Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, +                                                   int64_t stream_id, +                                                   void *stream_user_data); + +/** + * @function + * + * `ngtcp2_conn_update_pkt_tx_time` sets the time instant of the next + * packet transmission to pace packets.  This function must be called + * after (multiple invocation of) `ngtcp2_conn_writev_stream`.  If + * packet aggregation (e.g., packet batching, GSO) is used, call this + * function after all aggregated datagrams are sent, which indicates + * multiple invocation of `ngtcp2_conn_writev_stream`. + */ +NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, +                                                  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_send_quantum` returns the maximum number of bytes + * that can be sent in one go without packet spacing. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_stream_loss_count` returns the number of packets + * that contain STREAM frame for a stream identified by |stream_id| + * and are declared to be lost.  The number may include the spurious + * losses.  If no stream identified by |stream_id| is found, this + * function returns 0. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, +                                                       int64_t stream_id); + +/** + * @function + * + * `ngtcp2_strerror` returns the text representation of |liberr|. + * |liberr| must be one of ngtcp2 library error codes (which is + * defined as :macro:`NGTCP2_ERR_* <NGTCP2_ERR_INVALID_ARGUMENT>` + * macros). + */ +NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); + +/** + * @function + * + * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. + * |liberr| must be one of ngtcp2 library error codes (which is + * defined as :macro:`NGTCP2_ERR_* <NGTCP2_ERR_INVALID_ARGUMENT>` + * macros). + */ +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|.  |liberr| must + * be one of ngtcp2 library error codes (which is defined as + * :macro:`NGTCP2_ERR_* <NGTCP2_ERR_INVALID_ARGUMENT>` macros). + */ +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 ngtcp2_sockaddr *addr, +                                            ngtcp2_socklen addrlen); + +/** + * @function + * + * `ngtcp2_addr_copy_byte` copies |addr| of length |addrlen| into the + * buffer pointed by :member:`dest->addr <ngtcp2_addr.addr>`. + * :member:`dest->addrlen <ngtcp2_addr.addrlen>` is updated to have + * |addrlen|.  This function assumes that :member:`dest->addr + * <ngtcp2_addr.addr>` points to a buffer which has a sufficient + * capacity to store the copy. + */ +NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, +                                         const ngtcp2_sockaddr *addr, +                                         ngtcp2_socklen addrlen); + +/** + * @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 ngtcp2_sockaddr *local_addr, +                                            ngtcp2_socklen local_addrlen, +                                            const ngtcp2_sockaddr *remote_addr, +                                            ngtcp2_socklen remote_addrlen, +                                            void *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: + * + * * :type:`cc_algo <ngtcp2_settings.cc_algo>` = + *   :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC` + * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` = + *   :macro:`NGTCP2_DEFAULT_INITIAL_RTT` + * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2 + * * :type:`max_tx_udp_payload_size + *   <ngtcp2_settings.max_tx_udp_payload_size>` = 1452 + * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` = + *   ``UINT64_MAX`` + */ +NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version, +                                                     ngtcp2_settings *settings); + +/** + * @function + * + * `ngtcp2_transport_params_default` initializes |params| with the + * default values.  First this function fills |params| with 0, and set + * the default value to the following fields: + * + * * :type:`max_udp_payload_size + *   <ngtcp2_transport_params.max_udp_payload_size>` = + *   :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` + * * :type:`ack_delay_exponent + *   <ngtcp2_transport_params.ack_delay_exponent>` = + *   :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` + * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` = + *   :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` + * * :type:`active_connection_id_limit + *   <ngtcp2_transport_params.active_connection_id_limit>` = + *   :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` + */ +NGTCP2_EXTERN void +ngtcp2_transport_params_default_versioned(int transport_params_version, +                                          ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_mem_default` returns the default, system standard memory + * allocator. + */ +NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void); + +/** + * @macrosection + * + * ngtcp2_info macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_VERSION_AGE` is the age of :type:`ngtcp2_info` + */ +#define NGTCP2_VERSION_AGE 1 + +/** + * @struct + * + * :type:`ngtcp2_info` is what `ngtcp2_version` returns.  It holds + * information about the particular ngtcp2 version. + */ +typedef struct ngtcp2_info { +  /** +   * :member:`age` is the 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; +  /** +   * :member:`version_num` is the :macro:`NGTCP2_VERSION_NUM` number +   * (since :member:`age` ==1) +   */ +  int version_num; +  /** +   * :member:`version_str` points to the :macro:`NGTCP2_VERSION` +   * string (since :member:`age` ==1) +   */ +  const char *version_str; +  /* -------- the above fields all exist when age == 1 */ +} ngtcp2_info; + +/** + * @function + * + * `ngtcp2_version` returns a pointer to a :type:`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 const 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); + +/** + * @function + * + * `ngtcp2_path_copy` copies |src| into |dest|.  This function assumes + * that |dest| has enough buffer to store the deep copy of + * :member:`src->local <ngtcp2_path.local>` and :member:`src->remote + * <ngtcp2_path.remote>`. + */ +NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); + +/** + * @function + * + * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same + * local and remote addresses. + */ +NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); + +/** + * @function + * + * `ngtcp2_is_supported_version` returns nonzero if the library + * supports QUIC version |version|. + */ +NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version); + +/** + * @function + * + * `ngtcp2_is_reserved_version` returns nonzero if |version| is a + * reserved version. + */ +NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version); + +/** + * @function + * + * `ngtcp2_select_version` selects and returns a version from the + * version set |offered_versions| of |offered_versionslen| elements. + * |preferred_versions| of |preferred_versionslen| elements specifies + * the preference of versions, which is sorted in the order of + * preference.  All versions included in |preferred_versions| must be + * supported by the library, that is, passing any version in the array + * to `ngtcp2_is_supported_version` must return nonzero.  This + * function is intended to be used by client when it receives Version + * Negotiation packet.  If no version is selected, this function + * returns 0. + */ +NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, +                                             size_t preferred_versionslen, +                                             const uint32_t *offered_versions, +                                             size_t offered_versionslen); + +/* + * Versioned function wrappers + */ + +/* + * `ngtcp2_conn_read_pkt` is a wrapper around + * `ngtcp2_conn_read_pkt_versioned` to set the correct struct version. + */ +#define ngtcp2_conn_read_pkt(CONN, PATH, PI, PKT, PKTLEN, TS)                  \ +  ngtcp2_conn_read_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION,      \ +                                 (PI), (PKT), (PKTLEN), (TS)) + +/* + * `ngtcp2_conn_write_pkt` is a wrapper around + * `ngtcp2_conn_write_pkt_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_pkt(CONN, PATH, PI, DEST, DESTLEN, TS)               \ +  ngtcp2_conn_write_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION,     \ +                                  (PI), (DEST), (DESTLEN), (TS)) + +/* + * `ngtcp2_conn_write_stream` is a wrapper around + * `ngtcp2_conn_write_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN,      \ +                                 FLAGS, STREAM_ID, DATA, DATALEN, TS)          \ +  ngtcp2_conn_write_stream_versioned(                                          \ +    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),          \ +    (PDATALEN), (FLAGS), (STREAM_ID), (DATA), (DATALEN), (TS)) + +/* + * `ngtcp2_conn_writev_stream` is a wrapper around + * `ngtcp2_conn_writev_stream_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN,     \ +                                  FLAGS, STREAM_ID, DATAV, DATAVCNT, TS)       \ +  ngtcp2_conn_writev_stream_versioned(                                         \ +    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),          \ +    (PDATALEN), (FLAGS), (STREAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_write_datagram` is a wrapper around + * `ngtcp2_conn_write_datagram_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_write_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED,   \ +                                   FLAGS, DGRAM_ID, DATA, DATALEN, TS)         \ +  ngtcp2_conn_write_datagram_versioned(                                        \ +    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),          \ +    (PACCEPTED), (FLAGS), (DGRAM_ID), (DATA), (DATALEN), (TS)) + +/* + * `ngtcp2_conn_writev_datagram` is a wrapper around + * `ngtcp2_conn_writev_datagram_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_writev_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED,  \ +                                    FLAGS, DGRAM_ID, DATAV, DATAVCNT, TS)      \ +  ngtcp2_conn_writev_datagram_versioned(                                       \ +    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),          \ +    (PACCEPTED), (FLAGS), (DGRAM_ID), (DATAV), (DATAVCNT), (TS)) + +/* + * `ngtcp2_conn_write_connection_close` is a wrapper around + * `ngtcp2_conn_write_connection_close_versioned` to set the correct + * struct version. + */ +#define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN,      \ +                                           CCERR, TS)                          \ +  ngtcp2_conn_write_connection_close_versioned(                                \ +    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), (CCERR), \ +    (TS)) + +/* + * `ngtcp2_transport_params_encode` is a wrapper around + * `ngtcp2_transport_params_encode_versioned` to set the correct + * struct version. + */ +#define ngtcp2_transport_params_encode(DEST, DESTLEN, PARAMS)                  \ +  ngtcp2_transport_params_encode_versioned(                                    \ +    (DEST), (DESTLEN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_transport_params_decode` is a wrapper around + * `ngtcp2_transport_params_decode_versioned` to set the correct + * struct version. + */ +#define ngtcp2_transport_params_decode(PARAMS, DATA, DATALEN)                  \ +  ngtcp2_transport_params_decode_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION,    \ +                                           (PARAMS), (DATA), (DATALEN)) + +/* + * `ngtcp2_conn_client_new` is a wrapper around + * `ngtcp2_conn_client_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_client_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS,    \ +                               SETTINGS, PARAMS, MEM, USER_DATA)               \ +  ngtcp2_conn_client_new_versioned(                                            \ +    (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION,      \ +    (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS),                          \ +    NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_server_new` is a wrapper around + * `ngtcp2_conn_server_new_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_server_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS,    \ +                               SETTINGS, PARAMS, MEM, USER_DATA)               \ +  ngtcp2_conn_server_new_versioned(                                            \ +    (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION,      \ +    (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS),                          \ +    NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA)) + +/* + * `ngtcp2_conn_set_local_transport_params` is a wrapper around + * `ngtcp2_conn_set_local_transport_params_versioned` to set the + * correct struct version. + */ +#define ngtcp2_conn_set_local_transport_params(CONN, PARAMS)                   \ +  ngtcp2_conn_set_local_transport_params_versioned(                            \ +    (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS)) + +/* + * `ngtcp2_transport_params_default` is a wrapper around + * `ngtcp2_transport_params_default_versioned` to set the correct + * struct version. + */ +#define ngtcp2_transport_params_default(PARAMS)                                \ +  ngtcp2_transport_params_default_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION,   \ +                                            (PARAMS)) + +/* + * `ngtcp2_conn_get_conn_info` is a wrapper around + * `ngtcp2_conn_get_conn_info_versioned` to set the correct struct + * version. + */ +#define ngtcp2_conn_get_conn_info(CONN, CINFO)                                 \ +  ngtcp2_conn_get_conn_info_versioned((CONN), NGTCP2_CONN_INFO_VERSION, (CINFO)) + +/* + * `ngtcp2_settings_default` is a wrapper around + * `ngtcp2_settings_default_versioned` to set the correct struct + * version. + */ +#define ngtcp2_settings_default(SETTINGS)                                      \ +  ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS)) + +#ifdef _MSC_VER +#  pragma warning(pop) +#endif /* defined(_MSC_VER) */ + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGTCP2_H) */ diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto.h new file mode 100644 index 00000000000..5b8626191dd --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto.h @@ -0,0 +1,838 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_H +#define NGTCP2_CRYPTO_H + +#include <ngtcp2/ngtcp2.h> + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +#ifdef WIN32 +#  ifndef WIN32_LEAN_AND_MEAN +#    define WIN32_LEAN_AND_MEAN +#  endif /* !defined(WIN32_LEAN_AND_MEAN) */ +#  include <ws2tcpip.h> +#endif /* defined(WIN32) */ + +/** + * @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 1-RTT packets.  If it is unable to obtain necessary + * data from |tls_native_handle|, this function returns NULL. + * + * If libngtcp2_crypto_quictls 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_ctx_tls_early` initializes |ctx| by extracting early + * ciphers and message digests from native TLS session + * |tls_native_handle|.  This is used for encrypting/decrypting 0-RTT + * packets.  If it is unable to obtain necessary data from + * |tls_native_handle|, this function returns NULL. + * + * If libngtcp2_crypto_quictls is linked, |tls_native_handle| must be + * a pointer to SSL object. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx * +ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_md_init` initializes |md| with the provided + * |md_native_handle| which is an underlying message digest object. + * + * If libngtcp2_crypto_quictls is linked, |md_native_handle| must be a + * pointer to EVP_MD. + * + * If libngtcp2_crypto_gnutls is linked, |md_native_handle| must be + * gnutls_mac_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |md_native_handle| must be + * a pointer to EVP_MD. + */ +NGTCP2_EXTERN ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, +                                                      void *md_native_handle); + +/** + * @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_hkdf_extract` performs HKDF extract operation. + * + * The length of output is `ngtcp2_crypto_md_hashlen(md) + * <ngtcp2_crypto_md_hashlen>`.  The output is stored in the buffer + * pointed by |dest|.  The caller is responsible to specify the buffer + * that has enough capacity to 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 in 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` performs HKDF operation.  The result is + * |destlen| bytes long, and is stored in the buffer pointed by + * |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, +                                     const ngtcp2_crypto_md *md, +                                     const uint8_t *secret, size_t secretlen, +                                     const uint8_t *salt, size_t saltlen, +                                     const uint8_t *info, size_t infolen); + +/** + * @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_encrypt` encrypts |plaintext| of length + * |plaintextlen| and writes the ciphertext into the buffer pointed by + * |dest|.  The length of ciphertext is |plaintextlen| + + * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>` + * bytes long.  |dest| must have enough capacity to store the + * ciphertext.  |dest| and |plaintext| may point to the same buffer. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest, +                                        const ngtcp2_crypto_aead *aead, +                                        const ngtcp2_crypto_aead_ctx *aead_ctx, +                                        const uint8_t *plaintext, +                                        size_t plaintextlen, +                                        const uint8_t *nonce, size_t noncelen, +                                        const uint8_t *aad, size_t aadlen); + +/** + * @function + * + * `ngtcp2_crypto_encrypt_cb` is a wrapper function around + * `ngtcp2_crypto_encrypt`.  It can be directly passed to + * :member:`ngtcp2_callbacks.encrypt` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                         const ngtcp2_crypto_aead_ctx *aead_ctx, +                         const uint8_t *plaintext, size_t plaintextlen, +                         const uint8_t *nonce, size_t noncelen, +                         const uint8_t *aad, size_t aadlen); + +/** + * @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| - + * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>` + * bytes long.  |dest| must have enough capacity to store the + * plaintext.  |dest| and |ciphertext| may point to the same buffer. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest, +                                        const ngtcp2_crypto_aead *aead, +                                        const ngtcp2_crypto_aead_ctx *aead_ctx, +                                        const uint8_t *ciphertext, +                                        size_t ciphertextlen, +                                        const uint8_t *nonce, size_t noncelen, +                                        const uint8_t *aad, size_t aadlen); + +/** + * @function + * + * `ngtcp2_crypto_decrypt_cb` is a wrapper function around + * `ngtcp2_crypto_decrypt`.  It can be directly passed to + * :member:`ngtcp2_callbacks.decrypt` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_TLS_DECRYPT`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                         const ngtcp2_crypto_aead_ctx *aead_ctx, +                         const uint8_t *ciphertext, size_t ciphertextlen, +                         const uint8_t *nonce, size_t noncelen, +                         const uint8_t *aad, size_t aadlen); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask` generates a mask which is used in packet + * header encryption.  The mask is written to the buffer pointed by + * |dest|.  The sample is passed as |sample| which is + * :macro:`NGTCP2_HP_SAMPLELEN` bytes long.  The length of mask must + * be at least :macro:`NGTCP2_HP_MASKLEN`.  The library only uses the + * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask.  The + * buffer pointed by |dest| must have at least + * :macro:`NGTCP2_HP_SAMPLELEN` bytes available. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, +                                        const ngtcp2_crypto_cipher *hp, +                                        const ngtcp2_crypto_cipher_ctx *hp_ctx, +                                        const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around + * `ngtcp2_crypto_hp_mask`.  It can be directly passed to + * :member:`ngtcp2_callbacks.hp_mask` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, +                         const ngtcp2_crypto_cipher_ctx *hp_ctx, +                         const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_rx_key` derives the decryption + * keying materials from |secret|, and installs them to |conn|. + * + * If |key| is not NULL, the derived packet protection key is written + * to the buffer pointed by |key|.  If |iv| is not NULL, the derived + * packet protection IV is written to the buffer pointed by |iv|.  If + * |hp| is not NULL, the derived header protection key is written to + * the buffer pointed by |hp|. + * + * |secretlen| specifies the length of |secret|. + * + * The length of packet protection key and header protection key is + * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`). + * + * In the first call of this function, it calls + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to + * set negotiated AEAD and message digest algorithm.  After the + * successful call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to + * get :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as client, and |level| is + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_1RTT`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle`, and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_and_set_remote_transport_params`. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( +  ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, +  ngtcp2_encryption_level level, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_tx_key` derives the encryption + * keying materials from |secret|, and installs new keys to |conn|. + * + * If |key| is not NULL, the derived packet protection key is written + * to the buffer pointed by |key|.  If |iv| is not NULL, the derived + * packet protection IV is written to the buffer pointed by |iv|.  If + * |hp| is not NULL, the derived header protection key is written to + * the buffer pointed by |hp|. + * + * |secretlen| specifies the length of |secret|. + * + * The length of packet protection key and header protection key is + * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if + * |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`). + * + * In the first call of this function, it calls + * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to + * set negotiated AEAD and message digest algorithm.  After the + * successful call of this function, application can use + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * if |level| == + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to + * get :type:`ngtcp2_crypto_ctx`. + * + * If |conn| is initialized as server, and |level| is + * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_1RTT`, this + * function retrieves a remote QUIC transport parameters extension + * from an object obtained by `ngtcp2_conn_get_tls_native_handle`, and + * sets it to |conn| by calling + * `ngtcp2_conn_decode_and_set_remote_transport_params`. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key( +  ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, +  ngtcp2_encryption_level level, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_update_key` updates traffic keying materials. + * + * The new decryption traffic secret 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 encryption traffic secret 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 decryption packet protection key is written to the + * buffer pointed by |rx_key|.  The derived decryption packet + * protection IV is written to the buffer pointed by |rx_iv|. + * |rx_aead_ctx| is initialized with the derived key and IV. + * + * The derived encryption packet protection key is written to the + * buffer pointed by |tx_key|.  The derived encryption packet + * protection IV is written to the buffer pointed by |tx_iv|. + * |tx_aead_ctx| is initialized with the derived key and IV. + * + * |current_rx_secret| and |current_tx_secret| are the current + * decryption and encryption traffic secrets respectively.  They share + * the same length with |rx_secret| and |tx_secret|. + * + * The length of packet protection key and header protection key is + * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`, + * and the length of packet protection IV is + * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead) + * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by + * `ngtcp2_crypto_ctx_tls`. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_update_key( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, +  ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, +  const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, +  size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_update_key_cb` is a wrapper function around + * `ngtcp2_crypto_update_key`.  It can be directly passed to + * :member:`ngtcp2_callbacks.update_key` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, +  ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, +  const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, +  size_t secretlen, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_client_initial_cb` installs initial secrets and + * encryption keys, and sets QUIC transport parameters. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.client_initial` field.  It is only used + * by client. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, +                                                  void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in + * response to incoming Retry packet. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.recv_retry` field.  It is only used by + * client. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, +                                              const ngtcp2_pkt_hd *hd, +                                              void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in + * response to an incoming Initial packet from client, and sets QUIC + * transport parameters. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.recv_client_initial` field.  It is only + * used by server. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, +                                                       const ngtcp2_cid *dcid, +                                                       void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of + * length |datalen| in an encryption level |encryption_level|, and may + * feed outgoing CRYPTO data to |conn|.  This function can drive + * handshake.  This function can be also used after handshake + * completes.  It is allowed to call this function with |datalen| == + * 0.  In this case, no additional read operation is done. + * + * This function returns 0 if it succeeds, or a negative error code. + * The generic error code is -1 if a specific error code is not + * suitable.  The error codes less than -10000 are specific to + * underlying TLS implementation.  For quictls, the error codes are + * defined in *ngtcp2_crypto_quictls.h*. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, +                                     ngtcp2_encryption_level encryption_level, +                                     const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around + * `ngtcp2_crypto_read_write_crypto_data`.  It can be directly passed + * to :member:`ngtcp2_callbacks.recv_crypto_data` field. + * + * If this function is used, the TLS implementation specific error + * codes described in `ngtcp2_crypto_read_write_crypto_data` are + * treated as if it returns -1.  Do not use this function if an + * application wishes to use the TLS implementation specific error + * codes. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb( +  ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, uint64_t offset, +  const uint8_t *data, size_t datalen, void *user_data); + +/** + * @function + * + *  `ngtcp2_crypto_generate_stateless_reset_token` generates a + *  stateless reset token using HKDF extraction using the given |cid| + *  and |secret| as input.  The token will be written to the buffer + *  pointed by |token|, and it must have a capacity of at least + *  :macro:`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 uint8_t *secret, size_t secretlen, +  const ngtcp2_cid *cid); + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random + * data added to a token generated by + * `ngtcp2_crypto_generate_retry_token` or + * `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for + * Retry token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a + * token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of + * a token generated by `ngtcp2_crypto_generate_retry_token`. + */ +#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN                                       \ +  (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN +                   \ +   sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 +                               \ +   NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length + *  of a token generated by `ngtcp2_crypto_generate_regular_token`. + */ +#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN                                     \ +  (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 +             \ +   NGTCP2_CRYPTO_TOKEN_RAND_DATALEN) + +/** + * @function + * + * `ngtcp2_crypto_generate_retry_token` generates a token in the + * buffer pointed by |token| that is sent with Retry packet.  The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long.  The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`.  |secret| of length + * |secretlen| is a keying material to generate keys to encrypt the + * token.  |version| is QUIC version.  |remote_addr| of length + * |remote_addrlen| is an address of client.  |retry_scid| is a Source + * Connection ID chosen by server, and set in Retry packet.  |odcid| + * is a Destination Connection ID in Initial packet sent by client. + * |ts| is the timestamp when the token is generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token( +  uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in + * the buffer pointed by |token| of length |tokenlen|.  |secret| of + * length |secretlen| is a keying material to generate keys to decrypt + * the token.  |version| is QUIC version of the Initial packet that + * contains this token.  |remote_addr| of length |remote_addrlen| is + * an address of client.  |dcid| is a Destination Connection ID in + * Initial packet sent by client.  |timeout| is the period during + * which the token is valid.  |ts| is the current timestamp.  When + * validation succeeds, the extracted Destination Connection ID (which + * is the Destination Connection ID in Initial packet sent by client + * that triggered Retry packet) is stored in the buffer pointed by + * |odcid|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token( +  ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, +  const uint8_t *secret, size_t secretlen, uint32_t version, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_generate_regular_token` generates a token in the + * buffer pointed by |token| that is sent with NEW_TOKEN frame.  The + * buffer pointed by |token| must have at least + * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long.  The + * successfully generated token starts with + * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`.  |secret| of length + * |secretlen| is a keying material to generate keys to encrypt the + * token.  |remote_addr| of length |remote_addrlen| is an address of + * client.  |ts| is the timestamp when the token is generated. + * + * This function returns the length of generated token if it succeeds, + * or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token( +  uint8_t *token, const uint8_t *secret, size_t secretlen, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_verify_regular_token` verifies a regular token + * stored in the buffer pointed by |token| of length |tokenlen|. + * |secret| of length |secretlen| is a keying material to generate + * keys to decrypt the token.  |remote_addr| of length + * |remote_addrlen| is an address of client.  |timeout| is the period + * during which the token is valid.  |ts| is the current timestamp. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token( +  const uint8_t *token, size_t tokenlen, const uint8_t *secret, +  size_t secretlen, const ngtcp2_sockaddr *remote_addr, +  ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_crypto_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE with the given |error_code| and the + * optional |reason| of length |reasonlen| 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, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, +  size_t reasonlen); + +/** + * @function + * + * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer + * pointed by |dest| of length |destlen|.  |dcid| is the Connection ID + * which appeared in a packet as a Source Connection ID sent by + * client.  |scid| is a server chosen Source Connection ID.  |odcid| + * specifies Original Destination Connection ID which appeared in a + * packet as a Destination Connection ID sent by client.  |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, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, +  size_t tokenlen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with + * new AEAD cipher context object for encryption which is constructed + * to use |key| as encryption key.  |aead| specifies AEAD cipher to + * use.  |noncelen| is the length of nonce. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, +                                    const ngtcp2_crypto_aead *aead, +                                    const uint8_t *key, size_t noncelen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with + * new AEAD cipher context object for decryption which is constructed + * to use |key| as decryption key.  |aead| specifies AEAD cipher to + * use.  |noncelen| is the length of nonce. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, +                                    const ngtcp2_crypto_aead *aead, +                                    const uint8_t *key, size_t noncelen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_free` frees up resources used by + * |aead_ctx|.  This function does not free the memory pointed by + * |aead_ctx| itself. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given + * |aead_ctx|. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field. + */ +NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( +  ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given + * |cipher_ctx|. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field. + */ +NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( +  ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable + * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data| + * which is sent with PATH_CHALLENGE frame. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.get_path_challenge_data` field. + */ +NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, +                                                           uint8_t *data, +                                                           void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for + * |version| which is negotiated or being negotiated.  |client_dcid| + * is the destination connection ID in first Initial packet from + * client. + * + * This function can be directly passed to + * :member:`ngtcp2_callbacks.version_negotiation` field. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, +                                     const ngtcp2_cid *client_dcid, +                                     void *user_data); + +typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref; + +/** + * @functypedef + * + * :type:`ngtcp2_crypto_get_conn` is a callback function to get a + * pointer to :type:`ngtcp2_conn` from |conn_ref|.  The implementation + * must return non-NULL :type:`ngtcp2_conn` object. + */ +typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)( +  ngtcp2_crypto_conn_ref *conn_ref); + +/** + * @struct + * + * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to + * :type:`ngtcp2_conn`.  It is meant to be set to TLS native handle as + * an application specific data (e.g. SSL_set_app_data in quictls). + */ +typedef struct ngtcp2_crypto_conn_ref { +  /** +   * :member:`get_conn` is a callback function to get a pointer to +   * :type:`ngtcp2_conn` object. +   */ +  ngtcp2_crypto_get_conn get_conn; +  /** +   * :member:`user_data` is a pointer to arbitrary user data. +   */ +  void *user_data; +} ngtcp2_crypto_conn_ref; + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGTCP2_CRYPTO_H) */ diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto_quictls.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto_quictls.h new file mode 100644 index 00000000000..22e3eda0c41 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_crypto_quictls.h @@ -0,0 +1,147 @@ +/* + * 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_QUICTLS_H +#define NGTCP2_CRYPTO_QUICTLS_H + +#include <ngtcp2/ngtcp2.h> + +#include <openssl/ssl.h> + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/** + * @macrosection + * + * quictls specific error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_X509_LOOKUP` is the + * error code which indicates that TLS handshake routine is + * interrupted by X509 certificate lookup.  See + * :macro:`SSL_ERROR_WANT_X509_LOOKUP` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_X509_LOOKUP -10001 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_CLIENT_HELLO_CB` is the + * error code which indicates that TLS handshake routine is + * interrupted by client hello callback.  See + * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 + +/** + * @function + * + * `ngtcp2_crypto_quictls_from_ossl_encryption_level` translates + * |ossl_level| to :type:`ngtcp2_encryption_level`.  This function is + * only available for quictls backend. + */ +NGTCP2_EXTERN ngtcp2_encryption_level +ngtcp2_crypto_quictls_from_ossl_encryption_level( +  OSSL_ENCRYPTION_LEVEL ossl_level); + +/** + * @function + * + * `ngtcp2_crypto_quictls_from_ngtcp2_encryption_level` translates + * |encryption_level| to OSSL_ENCRYPTION_LEVEL.  This function is only + * available for quictls backend. + */ +NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_quictls_from_ngtcp2_encryption_level( +  ngtcp2_encryption_level encryption_level); + +/** + * @function + * + * `ngtcp2_crypto_quictls_configure_server_context` configures + * |ssl_ctx| for server side QUIC connection.  It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_quictls_configure_server_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_quictls_configure_client_context` configures + * |ssl_ctx| for client side QUIC connection.  It performs the + * following modifications: + * + * - Set minimum and maximum TLS version to TLSv1.3. + * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method. + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_quictls_configure_client_context(SSL_CTX *ssl_ctx); + +/** + * @function + * + * `ngtcp2_crypto_quictls_init` initializes libngtcp2_crypto_quictls + * library.  This initialization is optional.  For quictls >= 3.0, it + * is highly recommended to call this function before any use of + * libngtcp2_crypto library API to workaround the performance + * regression.  Note that calling this function does not solve all + * performance issues introduced in 3.x.  For quictls 1.1.1, this + * function does nothing, and always succeeds. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_quictls_init(void); + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGTCP2_CRYPTO_QUICTLS_H) */ diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_macro.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_macro.h new file mode 100644 index 00000000000..649ed8434a4 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_macro.h @@ -0,0 +1,81 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MACRO_H +#define NGTCP2_MACRO_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <stddef.h> + +#include <ngtcp2/ngtcp2.h> + +#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) + +/* + * ngtcp2_arraylen returns the number of elements in array |A|. + */ +#define ngtcp2_arraylen(A) (sizeof(A) / sizeof(A[0])) + +#define ngtcp2_max_def(SUFFIX, T)                                              \ +  static inline T ngtcp2_max_##SUFFIX(T a, T b) { return a < b ? b : a; } + +ngtcp2_max_def(int8, int8_t); +ngtcp2_max_def(int16, int16_t); +ngtcp2_max_def(int32, int32_t); +ngtcp2_max_def(int64, int64_t); +ngtcp2_max_def(uint8, uint8_t); +ngtcp2_max_def(uint16, uint16_t); +ngtcp2_max_def(uint32, uint32_t); +ngtcp2_max_def(uint64, uint64_t); +ngtcp2_max_def(size, size_t); + +#define ngtcp2_min_def(SUFFIX, T)                                              \ +  static inline T ngtcp2_min_##SUFFIX(T a, T b) { return a < b ? a : b; } + +ngtcp2_min_def(int8, int8_t); +ngtcp2_min_def(int16, int16_t); +ngtcp2_min_def(int32, int32_t); +ngtcp2_min_def(int64, int64_t); +ngtcp2_min_def(uint8, uint8_t); +ngtcp2_min_def(uint16, uint16_t); +ngtcp2_min_def(uint32, uint32_t); +ngtcp2_min_def(uint64, uint64_t); +ngtcp2_min_def(size, size_t); + +#endif /* !defined(NGTCP2_MACRO_H) */ diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_net.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_net.h new file mode 100644 index 00000000000..103a2fb2d80 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2_net.h @@ -0,0 +1,141 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 + +/* This header file is explicitly allowed to be shared with +   ngtcp2_crypto library. */ + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#ifdef HAVE_ARPA_INET_H +#  include <arpa/inet.h> +#endif /* defined(HAVE_ARPA_INET_H) */ + +#ifdef HAVE_NETINET_IN_H +#  include <netinet/in.h> +#endif /* defined(HAVE_NETINET_IN_H) */ + +#ifdef HAVE_BYTESWAP_H +#  include <byteswap.h> +#endif /* defined(HAVE_BYTESWAP_H) */ + +#ifdef HAVE_ENDIAN_H +#  include <endian.h> +#endif /* defined(HAVE_ENDIAN_H) */ + +#ifdef HAVE_SYS_ENDIAN_H +#  include <sys/endian.h> +#endif /* defined(HAVE_SYS_ENDIAN_H) */ + +#ifdef __APPLE__ +#  include <libkern/OSByteOrder.h> +#endif /* defined(__APPLE__) */ + +#include <ngtcp2/ngtcp2.h> + +#if HAVE_DECL_BE64TOH +#  define ngtcp2_ntohl64(N) be64toh(N) +#  define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_DECL_BE64TOH */ +#  ifdef WORDS_BIGENDIAN +#    define ngtcp2_ntohl64(N) (N) +#    define ngtcp2_htonl64(N) (N) +#  else /* !defined(WORDS_BIGENDIAN) */ +#    if HAVE_DECL_BSWAP_64 +#      define ngtcp2_bswap64 bswap_64 +#    elif defined(WIN32) +#      define ngtcp2_bswap64 _byteswap_uint64 +#    elif defined(__APPLE__) +#      define ngtcp2_bswap64 OSSwapInt64 +#    else /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#      define ngtcp2_bswap64(N)                                                \ +        ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 |                       \ +         ngtcp2_ntohl((uint32_t)((N) >> 32))) +#    endif /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#    define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +#    define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +#  endif /* !defined(WORDS_BIGENDIAN) */ +#endif   /* !HAVE_DECL_BE64TOH */ + +#ifdef WIN32 +/* Windows requires ws2_32 library for ntonl family functions.  We +   define inline functions for those function so that we don't have +   dependency on that lib. */ + +#  ifdef _MSC_VER +#    define STIN static __inline +#  else /* !defined(_MSC_VER) */ +#    define STIN static inline +#  endif /* !defined(_MSC_VER) */ + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&res; +  *p++ = (unsigned char)(hostlong >> 24); +  *p++ = (hostlong >> 16) & 0xffu; +  *p++ = (hostlong >> 8) & 0xffu; +  *p = hostlong & 0xffu; +  return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { +  uint16_t res; +  unsigned char *p = (unsigned char *)&res; +  *p++ = (unsigned char)(hostshort >> 8); +  *p = hostshort & 0xffu; +  return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&netlong; +  res = (uint32_t)(*p++ << 24); +  res += (uint32_t)(*p++ << 16); +  res += (uint32_t)(*p++ << 8); +  res += *p; +  return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { +  uint16_t res; +  unsigned char *p = (unsigned char *)&netshort; +  res = (uint16_t)(*p++ << 8); +  res += *p; +  return res; +} + +#else /* !defined(WIN32) */ + +#  define ngtcp2_htonl htonl +#  define ngtcp2_htons htons +#  define ngtcp2_ntohl ntohl +#  define ngtcp2_ntohs ntohs + +#endif /* !defined(WIN32) */ + +#endif /* !defined(NGTCP2_NET_H) */ diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h new file mode 100644 index 00000000000..881ecb114de --- /dev/null +++ b/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h @@ -0,0 +1,51 @@ +/* + * 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 NGTCP2_VERSION_H +#define NGTCP2_VERSION_H + +/** + * @macrosection + * + * Library version macros + */ + +/** + * @macro + * + * Version number of the ngtcp2 library release. + */ +#define NGTCP2_VERSION "1.8.1" + +/** + * @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 0x010801 + +#endif /* !defined(NGTCP2_VERSION_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c b/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c new file mode 100644 index 00000000000..d5508ba8909 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c @@ -0,0 +1,328 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_acktr.h" + +#include <assert.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_tstamp.h" + +ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent); + +static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num, +                             ngtcp2_tstamp tstamp) { +  ent->pkt_num = pkt_num; +  ent->len = 1; +  ent->tstamp = tstamp; +} + +int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, +                                    ngtcp2_tstamp tstamp, +                                    ngtcp2_objalloc *objalloc) { +  *ent = ngtcp2_objalloc_acktr_entry_get(objalloc); +  if (*ent == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  acktr_entry_init(*ent, pkt_num, tstamp); + +  return 0; +} + +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, +                                     ngtcp2_objalloc *objalloc) { +  ngtcp2_objalloc_acktr_entry_release(objalloc, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { +  return *(int64_t *)lhs > *(int64_t *)rhs; +} + +void ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, +                       const ngtcp2_mem *mem) { +  ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, NGTCP2_ACKTR_MAX_ENT + 1, +                                   mem); + +  ngtcp2_static_ringbuf_acks_init(&acktr->acks); + +  ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); + +  acktr->log = log; +  acktr->flags = NGTCP2_ACKTR_FLAG_NONE; +  acktr->first_unacked_ts = UINT64_MAX; +  acktr->rx_npkt = 0; +} + +void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { +#ifdef NOMEMPOOL +  ngtcp2_ksl_it it; +#endif /* defined(NOMEMPOOL) */ + +  if (acktr == NULL) { +    return; +  } + +#ifdef NOMEMPOOL +  for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); +       ngtcp2_ksl_it_next(&it)) { +    ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc); +  } +#endif /* defined(NOMEMPOOL) */ + +  ngtcp2_ksl_free(&acktr->ents); + +  ngtcp2_objalloc_free(&acktr->objalloc); +} + +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, +                     ngtcp2_tstamp ts) { +  ngtcp2_ksl_it it, prev_it; +  ngtcp2_acktr_entry *ent, *prev_ent, *delent; +  int rv; +  int added = 0; + +  if (ngtcp2_ksl_len(&acktr->ents)) { +    it = ngtcp2_ksl_lower_bound(&acktr->ents, &pkt_num); +    if (ngtcp2_ksl_it_end(&it)) { +      ngtcp2_ksl_it_prev(&it); +      ent = ngtcp2_ksl_it_get(&it); + +      assert(ent->pkt_num >= pkt_num + (int64_t)ent->len); + +      if (ent->pkt_num == pkt_num + (int64_t)ent->len) { +        ++ent->len; +        added = 1; +      } +    } else { +      ent = ngtcp2_ksl_it_get(&it); + +      assert(ent->pkt_num != pkt_num); + +      if (ngtcp2_ksl_it_begin(&it)) { +        if (ent->pkt_num + 1 == pkt_num) { +          ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); +          ent->pkt_num = pkt_num; +          ent->tstamp = ts; +          ++ent->len; +          added = 1; +        } +      } else { +        prev_it = it; +        ngtcp2_ksl_it_prev(&prev_it); +        prev_ent = ngtcp2_ksl_it_get(&prev_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_hint(&acktr->ents, NULL, &it, &ent->pkt_num); +            ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); +            added = 1; +          } else { +            ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); +            ent->pkt_num = pkt_num; +            ent->tstamp = ts; +            ++ent->len; +            added = 1; +          } +        } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { +          ++prev_ent->len; +          added = 1; +        } +      } +    } +  } + +  if (!added) { +    rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc); +    if (rv != 0) { +      return rv; +    } +    rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); +    if (rv != 0) { +      ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); +      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_hint(&acktr->ents, NULL, &it, &delent->pkt_num); +    ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc); +  } + +  return 0; +} + +void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { +  ngtcp2_ksl_it it; + +  it = ngtcp2_ksl_lower_bound(&acktr->ents, &ent->pkt_num); +  assert(*(int64_t *)ngtcp2_ksl_it_key(&it) == (int64_t)ent->pkt_num); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    ent = ngtcp2_ksl_it_get(&it); +    ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num); +    ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); +  } +} + +ngtcp2_ksl_it ngtcp2_acktr_get(const ngtcp2_acktr *acktr) { +  return ngtcp2_ksl_begin(&acktr->ents); +} + +int ngtcp2_acktr_empty(const ngtcp2_acktr *acktr) { +  ngtcp2_ksl_it it = ngtcp2_ksl_begin(&acktr->ents); +  return ngtcp2_ksl_it_end(&it); +} + +ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, +                                             int64_t pkt_num, +                                             int64_t largest_ack) { +  ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks.rb); + +  ent->largest_ack = largest_ack; +  ent->pkt_num = pkt_num; + +  return ent; +} + +/* + * acktr_remove removes |ent| from |acktr|.  |it| must point to the + * node whose key identifies |ent|.  The iterator which points to the + * entry next to |ent| is assigned to |it|. + */ +static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, +                         ngtcp2_acktr_entry *ent) { +  ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num); +  ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc); +} + +static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, +                         size_t ack_ent_offset) { +  ngtcp2_acktr_ack_entry *ack_ent; +  ngtcp2_acktr_entry *ent; +  ngtcp2_ksl_it it; + +  assert(ngtcp2_ringbuf_len(rb)); + +  ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset); + +  /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ +  it = ngtcp2_ksl_lower_bound(&acktr->ents, &ack_ent->largest_ack); +  for (; !ngtcp2_ksl_it_end(&it);) { +    ent = ngtcp2_ksl_it_get(&it); +    acktr_remove(acktr, &it, ent); +  } + +  if (ngtcp2_ksl_len(&acktr->ents)) { +    assert(ngtcp2_ksl_it_end(&it)); + +    ngtcp2_ksl_it_prev(&it); +    ent = ngtcp2_ksl_it_get(&it); + +    assert(ent->pkt_num > ack_ent->largest_ack); + +    if (ack_ent->largest_ack + (int64_t)ent->len > ent->pkt_num) { +      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.rb; +  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_range; + +  if (min_ack <= ent->pkt_num) { +    acktr_on_ack(acktr, rb, j); +    return; +  } + +  for (i = 0; i < fr->rangecnt && j < nacks; ++i) { +    largest_ack = min_ack - (int64_t)fr->ranges[i].gap - 2; +    min_ack = largest_ack - (int64_t)fr->ranges[i].len; + +    for (;;) { +      if (ent->pkt_num > largest_ack) { +        if (++j == nacks) { +          return; +        } +        ent = ngtcp2_ringbuf_get(rb, j); +        continue; +      } +      if (ent->pkt_num < min_ack) { +        break; +      } +      acktr_on_ack(acktr, rb, j); +      return; +    } +  } +} + +void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { +  acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | +                               NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | +                               NGTCP2_ACKTR_FLAG_CANCEL_TIMER); +  acktr->first_unacked_ts = UINT64_MAX; +  acktr->rx_npkt = 0; +} + +int ngtcp2_acktr_require_active_ack(const ngtcp2_acktr *acktr, +                                    ngtcp2_duration max_ack_delay, +                                    ngtcp2_tstamp ts) { +  return ngtcp2_tstamp_elapsed(acktr->first_unacked_ts, max_ack_delay, ts); +} + +void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { +  acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_acktr.h b/contrib/libs/ngtcp2/lib/ngtcp2_acktr.h new file mode 100644 index 00000000000..4ef420723d5 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_acktr.h @@ -0,0 +1,216 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ACKTR_H +#define NGTCP2_ACKTR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_ringbuf.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_objalloc.h" + +/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry +   which ngtcp2_acktr stores. */ +#define NGTCP2_ACKTR_MAX_ENT (NGTCP2_MAX_ACK_RANGES + 1) + +typedef struct ngtcp2_log ngtcp2_log; + +/* + * ngtcp2_acktr_entry is a range of packets which need to be acked. + */ +typedef struct ngtcp2_acktr_entry { +  union { +    struct { +      /* 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_opl_entry oplent; +  }; +} ngtcp2_acktr_entry; + +ngtcp2_objalloc_decl(acktr_entry, ngtcp2_acktr_entry, oplent); + +/* + * ngtcp2_acktr_entry_objalloc_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_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, +                                    ngtcp2_tstamp tstamp, +                                    ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for + * |ent|. + */ +void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent, +                                     ngtcp2_objalloc *objalloc); + +typedef struct ngtcp2_acktr_ack_entry { +  /* 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; + +/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */ +#define NGTCP2_ACKTR_FLAG_NONE 0x00u +/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate +   acknowledgement is required. */ +#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u +/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending +   protected packet to be acknowledged. */ +#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u +/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is +   expired and canceled. */ +#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u + +ngtcp2_static_ringbuf_def(acks, 32, sizeof(ngtcp2_acktr_ack_entry)); + +/* + * ngtcp2_acktr tracks received packets which we have to send ack. + */ +typedef struct ngtcp2_acktr { +  ngtcp2_objalloc objalloc; +  ngtcp2_static_ringbuf_acks acks; +  /* ents includes ngtcp2_acktr_entry sorted by decreasing order of +     packet number. */ +  ngtcp2_ksl ents; +  ngtcp2_log *log; +  /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_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 ACK eliciting packets received without +     sending ACK. */ +  size_t rx_npkt; +} ngtcp2_acktr; + +/* + * ngtcp2_acktr_init initializes |acktr|. + */ +void 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(const ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_empty returns nonzero if it has no packet to + * acknowledge. + */ +int ngtcp2_acktr_empty(const 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(const 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 /* !defined(NGTCP2_ACKTR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_addr.c b/contrib/libs/ngtcp2/lib/ngtcp2_addr.c new file mode 100644 index 00000000000..f389abe76d7 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_addr.c @@ -0,0 +1,117 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_addr.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_unreachable.h" + +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, +                              ngtcp2_socklen addrlen) { +  dest->addrlen = addrlen; +  dest->addr = (ngtcp2_sockaddr *)addr; +  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, (size_t)src->addrlen); +  } +} + +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, +                           ngtcp2_socklen addrlen) { +  dest->addrlen = addrlen; +  if (addrlen) { +    memcpy(dest->addr, addr, (size_t)addrlen); +  } +} + +static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { +  assert(a->sa_family == b->sa_family); + +  switch (a->sa_family) { +  case NGTCP2_AF_INET: { +    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, +                             *bi = (const ngtcp2_sockaddr_in *)(void *)b; +    return ai->sin_port == bi->sin_port && +           memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; +  } +  case NGTCP2_AF_INET6: { +    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, +                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; +    return ai->sin6_port == bi->sin6_port && +           memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; +  } +  default: +    ngtcp2_unreachable(); +  } +} + +int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { +  return a->addr->sa_family == b->addr->sa_family && +         sockaddr_eq(a->addr, b->addr); +} + +uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { +  uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; +  const ngtcp2_sockaddr *a = aa->addr; +  const ngtcp2_sockaddr *b = bb->addr; + +  if (a->sa_family != b->sa_family) { +    return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; +  } + +  switch (a->sa_family) { +  case NGTCP2_AF_INET: { +    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, +                             *bi = (const ngtcp2_sockaddr_in *)(void *)b; +    if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { +      flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; +    } +    if (ai->sin_port != bi->sin_port) { +      flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; +    } +    return flags; +  } +  case NGTCP2_AF_INET6: { +    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, +                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; +    if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { +      flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; +    } +    if (ai->sin6_port != bi->sin6_port) { +      flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; +    } +    return flags; +  } +  default: +    ngtcp2_unreachable(); +  } +} + +int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; } diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_addr.h b/contrib/libs/ngtcp2/lib/ngtcp2_addr.h new file mode 100644 index 00000000000..65ee7cd9f30 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_addr.h @@ -0,0 +1,71 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ADDR_H +#define NGTCP2_ADDR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_addr_copy copies |src| to |dest|.  This function assumes + * that dest->addr points to a buffer which have sufficient size to + * store the copy. + */ +void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); + +/** + * @function + * + * `ngtcp2_addr_eq` returns nonzero if |a| equals |b|. + */ +int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); + +/* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */ +#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u +/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not +   match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u +/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u +/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not +   match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u + +/* + * ngtcp2_addr_compare compares address and port between |a| and |b|, + * and returns zero or more of NGTCP2_ADDR_COMPARE_FLAG_*. + */ +uint32_t ngtcp2_addr_compare(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 /* !defined(NGTCP2_ADDR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_balloc.c b/contrib/libs/ngtcp2/lib/ngtcp2_balloc.c new file mode 100644 index 00000000000..4a6797689fc --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_balloc.c @@ -0,0 +1,90 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_balloc.h" + +#include <assert.h> + +#include "ngtcp2_mem.h" + +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, +                        const ngtcp2_mem *mem) { +  assert((blklen & 0xfu) == 0); + +  balloc->mem = mem; +  balloc->blklen = blklen; +  balloc->head = NULL; +  ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +void ngtcp2_balloc_free(ngtcp2_balloc *balloc) { +  if (balloc == NULL) { +    return; +  } + +  ngtcp2_balloc_clear(balloc); +} + +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) { +  ngtcp2_memblock_hd *p, *next; + +  for (p = balloc->head; p; p = next) { +    next = p->next; +    ngtcp2_mem_free(balloc->mem, p); +  } + +  balloc->head = NULL; +  ngtcp2_buf_init(&balloc->buf, (void *)"", 0); +} + +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) { +  uint8_t *p; +  ngtcp2_memblock_hd *hd; + +  assert(n <= balloc->blklen); + +  if (ngtcp2_buf_left(&balloc->buf) < n) { +    p = ngtcp2_mem_malloc(balloc->mem, +                          sizeof(ngtcp2_memblock_hd) + 0x8u + balloc->blklen); +    if (p == NULL) { +      return NGTCP2_ERR_NOMEM; +    } + +    hd = (ngtcp2_memblock_hd *)(void *)p; +    hd->next = balloc->head; +    balloc->head = hd; +    ngtcp2_buf_init( +      &balloc->buf, +      (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) & +                  ~(uintptr_t)0xfu), +      balloc->blklen); +  } + +  assert(((uintptr_t)balloc->buf.last & 0xfu) == 0); + +  *pbuf = balloc->buf.last; +  balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu; + +  return 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_balloc.h b/contrib/libs/ngtcp2/lib/ngtcp2_balloc.h new file mode 100644 index 00000000000..e69e7efc6f2 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_balloc.h @@ -0,0 +1,94 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_BALLOC_H +#define NGTCP2_BALLOC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_buf.h" + +typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd; + +/* + * ngtcp2_memblock_hd is the header of memory block. + */ +struct ngtcp2_memblock_hd { +  union { +    ngtcp2_memblock_hd *next; +    uint64_t pad; +  }; +}; + +/* + * ngtcp2_balloc is a custom memory allocator.  It allocates |blklen| + * bytes of memory at once on demand, and returns its slice when the + * allocation is requested. + */ +typedef struct ngtcp2_balloc { +  /* mem is the underlying memory allocator. */ +  const ngtcp2_mem *mem; +  /* blklen is the size of memory block. */ +  size_t blklen; +  /* head points to the list of memory block allocated so far. */ +  ngtcp2_memblock_hd *head; +  /* buf wraps the current memory block for allocation requests. */ +  ngtcp2_buf buf; +} ngtcp2_balloc; + +/* + * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the + * size of memory block. + */ +void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen, +                        const ngtcp2_mem *mem); + +/* + * ngtcp2_balloc_free releases all allocated memory blocks. + */ +void ngtcp2_balloc_free(ngtcp2_balloc *balloc); + +/* + * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its + * pointer to |*pbuf|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n); + +/* + * ngtcp2_balloc_clear releases all allocated memory blocks and + * initializes its state. + */ +void ngtcp2_balloc_clear(ngtcp2_balloc *balloc); + +#endif /* !defined(NGTCP2_BALLOC_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c new file mode 100644 index 00000000000..5a66c1870bf --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c @@ -0,0 +1,1425 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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_bbr.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" +#include "ngtcp2_conn_stat.h" + +#define NGTCP2_BBR_MAX_BW_FILTERLEN 2 + +#define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10 + +#define NGTCP2_BBR_STARTUP_PACING_GAIN_H 277 +#define NGTCP2_BBR_DRAIN_PACING_GAIN_H 35 + +#define NGTCP2_BBR_DEFAULT_CWND_GAIN_H 200 + +#define NGTCP2_BBR_PROBE_RTT_CWND_GAIN_H 50 + +#define NGTCP2_BBR_BETA_NUMER 7 +#define NGTCP2_BBR_BETA_DENOM 10 + +#define NGTCP2_BBR_LOSS_THRESH_NUMER 2 +#define NGTCP2_BBR_LOSS_THRESH_DENOM 100 + +#define NGTCP2_BBR_HEADROOM_NUMER 15 +#define NGTCP2_BBR_HEADROOM_DENOM 100 + +#define NGTCP2_BBR_PROBE_RTT_INTERVAL (5 * NGTCP2_SECONDS) +#define NGTCP2_BBR_MIN_RTT_FILTERLEN (10 * NGTCP2_SECONDS) + +#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS) + +#define NGTCP2_BBR_PACING_MARGIN_PERCENT 1 + +static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                        ngtcp2_tstamp initial_ts); + +static void bbr_on_transmit(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                            ngtcp2_tstamp ts); + +static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr); + +static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr); + +static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr); + +static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr); + +static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr, +                                          ngtcp2_conn_stat *cstat, +                                          uint64_t pacing_gain_h); + +static void bbr_set_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_enter_startup(ngtcp2_cc_bbr *bbr); + +static void bbr_check_startup_done(ngtcp2_cc_bbr *bbr); + +static void bbr_update_on_ack(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                              const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static void bbr_update_model_and_state(ngtcp2_cc_bbr *cc, +                                       ngtcp2_conn_stat *cstat, +                                       const ngtcp2_cc_ack *ack, +                                       ngtcp2_tstamp ts); + +static void bbr_update_control_parameters(ngtcp2_cc_bbr *cc, +                                          ngtcp2_conn_stat *cstat, +                                          const ngtcp2_cc_ack *ack); + +static void bbr_update_on_loss(ngtcp2_cc_bbr *cc, ngtcp2_conn_stat *cstat, +                               const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static void bbr_update_latest_delivery_signals(ngtcp2_cc_bbr *bbr, +                                               ngtcp2_conn_stat *cstat); + +static void bbr_advance_latest_delivery_signals(ngtcp2_cc_bbr *bbr, +                                                ngtcp2_conn_stat *cstat); + +static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr, +                                          ngtcp2_conn_stat *cstat, +                                          const ngtcp2_cc_ack *ack); + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr, +                                                   ngtcp2_conn_stat *cstat); + +static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr); + +static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr); + +static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                              const ngtcp2_cc_ack *ack); + +static void bbr_update_round(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack); + +static void bbr_start_round(ngtcp2_cc_bbr *bbr); + +static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr); + +static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, +                                       ngtcp2_conn_stat *cstat, +                                       const ngtcp2_cc_ack *ack, +                                       ngtcp2_tstamp ts); + +static void bbr_enter_drain(ngtcp2_cc_bbr *bbr); + +static void bbr_check_drain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                            ngtcp2_tstamp ts); + +static void bbr_enter_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_down(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts); + +static void bbr_start_probe_bw_cruise(ngtcp2_cc_bbr *bbr); + +static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr); + +static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, +                                            ngtcp2_conn_stat *cstat, +                                            const ngtcp2_cc_ack *ack, +                                            ngtcp2_tstamp ts); + +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                 ngtcp2_tstamp ts); + +static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_duration interval, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, +                                           ngtcp2_conn_stat *cstat); + +static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, +                                        ngtcp2_conn_stat *cstat); + +static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         const ngtcp2_cc_ack *ack); + +static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   ngtcp2_tstamp ts); + +static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr); + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, +                                              ngtcp2_conn_stat *cstat); + +static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_conn_stat *cstat); + +static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, +                                       ngtcp2_conn_stat *cstat, +                                       ngtcp2_tstamp ts); + +static int is_inflight_too_high(const ngtcp2_rs *rs); + +static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         const ngtcp2_rs *rs, ngtcp2_tstamp ts); + +static void bbr_note_loss(ngtcp2_cc_bbr *bbr); + +static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, +                                                 const ngtcp2_rs *rs, +                                                 const ngtcp2_cc_pkt *pkt); + +static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, +                               ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                ngtcp2_tstamp ts); + +static void bbr_enter_probe_rtt(ngtcp2_cc_bbr *bbr); + +static void bbr_handle_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                 ngtcp2_tstamp ts); + +static void bbr_check_probe_rtt_done(ngtcp2_cc_bbr *bbr, +                                     ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr, +                                            ngtcp2_conn_stat *cstat); + +static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts); + +static void bbr_handle_restart_from_idle(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp ts); + +static uint64_t bbr_bdp_multiple(ngtcp2_cc_bbr *bbr, uint64_t gain_h); + +static uint64_t bbr_quantization_budget(ngtcp2_cc_bbr *bbr, +                                        ngtcp2_conn_stat *cstat, +                                        uint64_t inflight); + +static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                             uint64_t gain_h); + +static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_conn_stat *cstat); + +static void bbr_update_offload_budget(ngtcp2_cc_bbr *bbr, +                                      ngtcp2_conn_stat *cstat); + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size); + +static void bbr_advance_max_bw_filter(ngtcp2_cc_bbr *bbr); + +static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_restore_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat); + +static void bbr_set_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                         const ngtcp2_cc_ack *ack); + +static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr, +                                     ngtcp2_conn_stat *cstat); + +static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, +                                  ngtcp2_tstamp sent_time); + +static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                const ngtcp2_cc_ack *ack); + +static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                        ngtcp2_tstamp initial_ts) { +  ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN); +  ngtcp2_window_filter_init(&bbr->extra_acked_filter, +                            NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); + +  bbr->min_rtt = UINT64_MAX; +  bbr->min_rtt_stamp = initial_ts; +  /* remark: Use UINT64_MAX instead of 0 for consistency. */ +  bbr->probe_rtt_done_stamp = UINT64_MAX; +  bbr->probe_rtt_round_done = 0; +  bbr->prior_cwnd = 0; +  bbr->idle_restart = 0; +  bbr->extra_acked_interval_start = initial_ts; +  bbr->extra_acked_delivered = 0; +  bbr->full_bw_reached = 0; + +  bbr_reset_congestion_signals(bbr); +  bbr_reset_lower_bounds(bbr); +  bbr_init_round_counting(bbr); +  bbr_reset_full_bw(bbr); +  bbr_init_pacing_rate(bbr, cstat); +  bbr_enter_startup(bbr); + +  cstat->send_quantum = cstat->max_tx_udp_payload_size * 10; + +  /* Missing in documentation */ +  bbr->loss_round_start = 0; +  bbr->loss_round_delivered = UINT64_MAX; + +  bbr->rounds_since_bw_probe = 0; + +  bbr->max_bw = 0; +  bbr->bw = 0; + +  bbr->cycle_count = 0; + +  bbr->extra_acked = 0; + +  bbr->bytes_lost_in_round = 0; +  bbr->loss_events_in_round = 0; + +  bbr->offload_budget = 0; + +  bbr->probe_up_cnt = UINT64_MAX; +  bbr->cycle_stamp = UINT64_MAX; +  bbr->ack_phase = 0; +  bbr->bw_probe_wait = 0; +  bbr->bw_probe_samples = 0; +  bbr->bw_probe_up_rounds = 0; +  bbr->bw_probe_up_acks = 0; + +  bbr->inflight_hi = UINT64_MAX; + +  bbr->probe_rtt_expired = 0; +  bbr->probe_rtt_min_delay = UINT64_MAX; +  bbr->probe_rtt_min_stamp = initial_ts; + +  bbr->in_loss_recovery = 0; +  bbr->round_count_at_recovery = UINT64_MAX; + +  bbr->max_inflight = 0; + +  bbr->congestion_recovery_start_ts = UINT64_MAX; +} + +static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) { +  bbr->loss_in_round = 0; +  bbr->bw_latest = 0; +  bbr->inflight_latest = 0; +} + +static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) { +  bbr->bw_lo = UINT64_MAX; +  bbr->inflight_lo = UINT64_MAX; +} + +static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr) { +  bbr->next_round_delivered = 0; +  bbr->round_start = 0; +  bbr->round_count = 0; +} + +static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr) { +  bbr->full_bw = 0; +  bbr->full_bw_count = 0; +  bbr->full_bw_now = 0; +} + +static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr, +                                      ngtcp2_conn_stat *cstat) { +  if (bbr->full_bw_now || bbr->rst->rs.is_app_limited) { +    return; +  } + +  if (cstat->delivery_rate_sec * 100 >= bbr->full_bw * 125) { +    bbr_reset_full_bw(bbr); +    bbr->full_bw = cstat->delivery_rate_sec; + +    return; +  } + +  if (!bbr->round_start) { +    return; +  } + +  ++bbr->full_bw_count; + +  bbr->full_bw_now = bbr->full_bw_count >= 3; +  if (!bbr->full_bw_now) { +    return; +  } + +  bbr->full_bw_reached = 1; + +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "bbr reached full bandwidth, full_bw=%" PRIu64, bbr->full_bw); +} + +static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) { +  if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 || +      (bbr->in_loss_recovery && +       bbr->round_count <= bbr->round_count_at_recovery) || +      !is_inflight_too_high(&bbr->rst->rs)) { +    return; +  } + +  bbr->full_bw_reached = 1; +  bbr->inflight_hi = ngtcp2_max_uint64(bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), +                                       bbr->inflight_latest); +} + +static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  cstat->pacing_interval = NGTCP2_MILLISECONDS * 100 / +                           NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd; +} + +static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr, +                                          ngtcp2_conn_stat *cstat, +                                          uint64_t pacing_gain_h) { +  ngtcp2_duration interval; + +  if (bbr->bw == 0) { +    return; +  } + +  interval = NGTCP2_SECONDS * 100 * 100 / pacing_gain_h / bbr->bw / +             (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT); + +  if (bbr->full_bw_reached || interval < cstat->pacing_interval) { +    cstat->pacing_interval = interval; +  } +} + +static void bbr_set_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain_h); +} + +static void bbr_enter_startup(ngtcp2_cc_bbr *bbr) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr enter Startup"); + +  bbr->state = NGTCP2_BBR_STATE_STARTUP; +  bbr->pacing_gain_h = NGTCP2_BBR_STARTUP_PACING_GAIN_H; +  bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; +} + +static void bbr_check_startup_done(ngtcp2_cc_bbr *bbr) { +  bbr_check_startup_high_loss(bbr); + +  if (bbr->state == NGTCP2_BBR_STATE_STARTUP && bbr->full_bw_reached) { +    bbr_enter_drain(bbr); +  } +} + +static void bbr_on_transmit(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                            ngtcp2_tstamp ts) { +  bbr_handle_restart_from_idle(bbr, cstat, ts); +} + +static void bbr_update_on_ack(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                              const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { +  bbr_update_model_and_state(bbr, cstat, ack, ts); +  bbr_update_control_parameters(bbr, cstat, ack); +} + +static void bbr_update_model_and_state(ngtcp2_cc_bbr *bbr, +                                       ngtcp2_conn_stat *cstat, +                                       const ngtcp2_cc_ack *ack, +                                       ngtcp2_tstamp ts) { +  bbr_update_latest_delivery_signals(bbr, cstat); +  bbr_update_congestion_signals(bbr, cstat, ack); +  bbr_update_ack_aggregation(bbr, cstat, ack, ts); +  bbr_check_full_bw_reached(bbr, cstat); +  bbr_check_startup_done(bbr); +  bbr_check_drain(bbr, cstat, ts); +  bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts); +  bbr_update_min_rtt(bbr, ack, ts); +  bbr_check_probe_rtt(bbr, cstat, ts); +  bbr_advance_latest_delivery_signals(bbr, cstat); +  bbr_bound_bw_for_model(bbr); +} + +static void bbr_update_control_parameters(ngtcp2_cc_bbr *bbr, +                                          ngtcp2_conn_stat *cstat, +                                          const ngtcp2_cc_ack *ack) { +  bbr_set_pacing_rate(bbr, cstat); +  bbr_set_send_quantum(bbr, cstat); +  bbr_set_cwnd(bbr, cstat, ack); +} + +static void bbr_update_on_loss(ngtcp2_cc_bbr *cc, ngtcp2_conn_stat *cstat, +                               const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { +  bbr_handle_lost_packet(cc, cstat, pkt, ts); +} + +static void bbr_update_latest_delivery_signals(ngtcp2_cc_bbr *bbr, +                                               ngtcp2_conn_stat *cstat) { +  bbr->loss_round_start = 0; +  bbr->bw_latest = ngtcp2_max_uint64(bbr->bw_latest, cstat->delivery_rate_sec); +  bbr->inflight_latest = +    ngtcp2_max_uint64(bbr->inflight_latest, bbr->rst->rs.delivered); + +  if (bbr->rst->rs.prior_delivered >= bbr->loss_round_delivered) { +    bbr->loss_round_delivered = bbr->rst->delivered; +    bbr->loss_round_start = 1; +  } +} + +static void bbr_advance_latest_delivery_signals(ngtcp2_cc_bbr *bbr, +                                                ngtcp2_conn_stat *cstat) { +  if (bbr->loss_round_start) { +    bbr->bw_latest = cstat->delivery_rate_sec; +    bbr->inflight_latest = bbr->rst->rs.delivered; +  } +} + +static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr, +                                          ngtcp2_conn_stat *cstat, +                                          const ngtcp2_cc_ack *ack) { +  bbr_update_max_bw(bbr, cstat, ack); + +  if (ack->bytes_lost) { +    bbr->bytes_lost_in_round += ack->bytes_lost; +    ++bbr->loss_events_in_round; +  } + +  if (!bbr->loss_round_start) { +    return; +  } + +  bbr_adapt_lower_bounds_from_congestion(bbr, cstat); + +  bbr->loss_in_round = 0; +} + +static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr, +                                                   ngtcp2_conn_stat *cstat) { +  if (bbr_is_in_probe_bw_state(bbr)) { +    return; +  } + +  if (bbr->loss_in_round) { +    bbr_init_lower_bounds(bbr, cstat); +    bbr_loss_lower_bounds(bbr); +  } +} + +static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  if (bbr->bw_lo == UINT64_MAX) { +    bbr->bw_lo = bbr->max_bw; +  } + +  if (bbr->inflight_lo == UINT64_MAX) { +    bbr->inflight_lo = cstat->cwnd; +  } +} + +static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr) { +  bbr->bw_lo = ngtcp2_max_uint64( +    bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); +  bbr->inflight_lo = ngtcp2_max_uint64( +    bbr->inflight_latest, +    bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); +} + +static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr) { +  bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_lo); +} + +static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                              const ngtcp2_cc_ack *ack) { +  bbr_update_round(bbr, ack); + +  if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) { +    ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec, +                                bbr->cycle_count); + +    bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter); +  } +} + +static void bbr_update_round(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack) { +  if (ack->pkt_delivered >= bbr->next_round_delivered) { +    bbr_start_round(bbr); + +    ++bbr->round_count; +    ++bbr->rounds_since_bw_probe; +    bbr->round_start = 1; + +    bbr->bytes_lost_in_round = 0; +    bbr->loss_events_in_round = 0; + +    bbr->rst->is_cwnd_limited = 0; + +    return; +  } + +  bbr->round_start = 0; +} + +static void bbr_start_round(ngtcp2_cc_bbr *bbr) { +  bbr->next_round_delivered = bbr->rst->delivered; +} + +static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr) { +  switch (bbr->state) { +  case NGTCP2_BBR_STATE_PROBE_BW_DOWN: +  case NGTCP2_BBR_STATE_PROBE_BW_CRUISE: +  case NGTCP2_BBR_STATE_PROBE_BW_REFILL: +  case NGTCP2_BBR_STATE_PROBE_BW_UP: +    return 1; +  default: +    return 0; +  } +} + +static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, +                                       ngtcp2_conn_stat *cstat, +                                       const ngtcp2_cc_ack *ack, +                                       ngtcp2_tstamp ts) { +  ngtcp2_duration interval = ts - bbr->extra_acked_interval_start; +  uint64_t expected_delivered = bbr->bw * interval / NGTCP2_SECONDS; +  uint64_t extra; + +  if (bbr->extra_acked_delivered <= expected_delivered) { +    bbr->extra_acked_delivered = 0; +    bbr->extra_acked_interval_start = ts; +    expected_delivered = 0; +  } + +  bbr->extra_acked_delivered += ack->bytes_delivered; +  extra = bbr->extra_acked_delivered - expected_delivered; +  extra = ngtcp2_min_uint64(extra, cstat->cwnd); + +  if (bbr->full_bw_reached) { +    bbr->extra_acked_filter.window_length = NGTCP2_BBR_EXTRA_ACKED_FILTERLEN; +  } else { +    bbr->extra_acked_filter.window_length = 1; +  } + +  ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra, +                              bbr->round_count); + +  bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter); +} + +static void bbr_enter_drain(ngtcp2_cc_bbr *bbr) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr enter Drain"); + +  bbr->state = NGTCP2_BBR_STATE_DRAIN; +  bbr->pacing_gain_h = NGTCP2_BBR_DRAIN_PACING_GAIN_H; +  bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; +} + +static void bbr_check_drain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                            ngtcp2_tstamp ts) { +  if (bbr->state == NGTCP2_BBR_STATE_DRAIN && +      cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) { +    bbr_enter_probe_bw(bbr, ts); +  } +} + +static void bbr_enter_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) { +  bbr_start_probe_bw_down(bbr, ts); +} + +static void bbr_start_probe_bw_down(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr start ProbeBW_DOWN"); + +  bbr_reset_congestion_signals(bbr); + +  bbr->probe_up_cnt = UINT64_MAX; + +  bbr_pick_probe_wait(bbr); + +  bbr->cycle_stamp = ts; +  bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STOPPING; + +  bbr_start_round(bbr); + +  bbr->state = NGTCP2_BBR_STATE_PROBE_BW_DOWN; +  bbr->pacing_gain_h = 90; +  bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; +} + +static void bbr_start_probe_bw_cruise(ngtcp2_cc_bbr *bbr) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "bbr start ProbeBW_CRUISE"); + +  bbr->state = NGTCP2_BBR_STATE_PROBE_BW_CRUISE; +  bbr->pacing_gain_h = 100; +  bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; +} + +static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "bbr start ProbeBW_REFILL"); + +  bbr_reset_lower_bounds(bbr); + +  bbr->bw_probe_up_rounds = 0; +  bbr->bw_probe_up_acks = 0; +  bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_REFILLING; + +  bbr_start_round(bbr); + +  bbr->state = NGTCP2_BBR_STATE_PROBE_BW_REFILL; +  bbr->pacing_gain_h = 100; +  bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; +} + +static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr start ProbeBW_UP"); + +  bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING; + +  bbr_start_round(bbr); +  bbr_reset_full_bw(bbr); + +  bbr->full_bw = cstat->delivery_rate_sec; +  bbr->state = NGTCP2_BBR_STATE_PROBE_BW_UP; +  bbr->pacing_gain_h = 125; +  bbr->cwnd_gain_h = 225; + +  bbr_raise_inflight_hi_slope(bbr, cstat); +} + +static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, +                                            ngtcp2_conn_stat *cstat, +                                            const ngtcp2_cc_ack *ack, +                                            ngtcp2_tstamp ts) { +  if (!bbr->full_bw_reached) { +    return; +  } + +  bbr_adapt_upper_bounds(bbr, cstat, ack, ts); + +  if (!bbr_is_in_probe_bw_state(bbr)) { +    return; +  } + +  switch (bbr->state) { +  case NGTCP2_BBR_STATE_PROBE_BW_DOWN: +    if (bbr_is_time_to_probe_bw(bbr, cstat, ts)) { +      return; +    } + +    if (bbr_is_time_to_cruise(bbr, cstat, ts)) { +      bbr_start_probe_bw_cruise(bbr); +    } + +    break; +  case NGTCP2_BBR_STATE_PROBE_BW_CRUISE: +    if (bbr_is_time_to_probe_bw(bbr, cstat, ts)) { +      return; +    } + +    break; +  case NGTCP2_BBR_STATE_PROBE_BW_REFILL: +    if (bbr->round_start) { +      bbr->bw_probe_samples = 1; +      bbr_start_probe_bw_up(bbr, cstat); +    } + +    break; +  case NGTCP2_BBR_STATE_PROBE_BW_UP: +    if (bbr_is_time_to_go_down(bbr, cstat)) { +      bbr_start_probe_bw_down(bbr, ts); +    } + +    break; +  default: +    break; +  } +} + +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                 ngtcp2_tstamp ts) { +  (void)ts; + +  if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { +    return 0; +  } + +  if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) { +    return 1; +  } + +  return 0; +} + +static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_hi) { +    bbr_reset_full_bw(bbr); +    bbr->full_bw = cstat->delivery_rate_sec; +  } else if (bbr->full_bw_now) { +    return 1; +  } + +  return 0; +} + +static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_duration interval, +                                    ngtcp2_tstamp ts) { +  return ts > bbr->cycle_stamp + interval; +} + +static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, +                                           ngtcp2_conn_stat *cstat) { +  uint64_t headroom; +  uint64_t mpcwnd; +  if (bbr->inflight_hi == UINT64_MAX) { +    return UINT64_MAX; +  } + +  headroom = ngtcp2_max_uint64(cstat->max_tx_udp_payload_size, +                               bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER / +                                 NGTCP2_BBR_HEADROOM_DENOM); +  mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); + +  if (bbr->inflight_hi > headroom) { +    return ngtcp2_max_uint64(bbr->inflight_hi - headroom, mpcwnd); +  } + +  return mpcwnd; +} + +static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, +                                        ngtcp2_conn_stat *cstat) { +  uint64_t growth_this_round = cstat->max_tx_udp_payload_size +                               << bbr->bw_probe_up_rounds; + +  bbr->bw_probe_up_rounds = ngtcp2_min_size(bbr->bw_probe_up_rounds + 1, 30); +  bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1) * +                      cstat->max_tx_udp_payload_size; +} + +static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         const ngtcp2_cc_ack *ack) { +  uint64_t delta; + +  if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) { +    return; +  } + +  bbr->bw_probe_up_acks += ack->bytes_delivered; + +  if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) { +    delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; +    bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; +    bbr->inflight_hi += delta * cstat->max_tx_udp_payload_size; +  } + +  if (bbr->round_start) { +    bbr_raise_inflight_hi_slope(bbr, cstat); +  } +} + +static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { +  if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING && +      bbr->round_start) { +    bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK; +  } + +  if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STOPPING && +      bbr->round_start) { +    if (bbr_is_in_probe_bw_state(bbr) && !bbr->rst->rs.is_app_limited) { +      bbr_advance_max_bw_filter(bbr); +    } +  } + +  if (!bbr_check_inflight_too_high(bbr, cstat, ts)) { +    if (bbr->inflight_hi == UINT64_MAX) { +      return; +    } + +    if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) { +      bbr->inflight_hi = bbr->rst->rs.tx_in_flight; +    } + +    if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) { +      bbr_probe_inflight_hi_upward(bbr, cstat, ack); +    } +  } +} + +static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   ngtcp2_tstamp ts) { +  if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) || +      bbr_is_reno_coexistence_probe_time(bbr, cstat)) { +    bbr_start_probe_bw_refill(bbr); + +    return 1; +  } + +  return 0; +} + +static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) { +  uint8_t rand; + +  bbr->rand(&rand, 1, &bbr->rand_ctx); + +  bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256); + +  bbr->rand(&rand, 1, &bbr->rand_ctx); + +  bbr->bw_probe_wait = +    2 * NGTCP2_SECONDS + (ngtcp2_tstamp)(NGTCP2_SECONDS * rand / 255); +} + +static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, +                                              ngtcp2_conn_stat *cstat) { +  uint64_t reno_rounds = +    bbr_target_inflight(bbr, cstat) / cstat->max_tx_udp_payload_size; + +  return bbr->rounds_since_bw_probe >= ngtcp2_min_uint64(reno_rounds, 63); +} + +static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_conn_stat *cstat) { +  uint64_t bdp = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h); + +  return ngtcp2_min_uint64(bdp, cstat->cwnd); +} + +static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, +                                       ngtcp2_conn_stat *cstat, +                                       ngtcp2_tstamp ts) { +  if (is_inflight_too_high(&bbr->rst->rs)) { +    if (bbr->bw_probe_samples) { +      bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts); +    } + +    return 1; +  } + +  return 0; +} + +static int is_inflight_too_high(const ngtcp2_rs *rs) { +  return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM > +         rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER; +} + +static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         const ngtcp2_rs *rs, +                                         ngtcp2_tstamp ts) { +  bbr->bw_probe_samples = 0; + +  if (!rs->is_app_limited) { +    bbr->inflight_hi = ngtcp2_max_uint64( +      rs->tx_in_flight, bbr_target_inflight(bbr, cstat) * +                          NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); +  } + +  if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) { +    bbr_start_probe_bw_down(bbr, ts); +  } +} + +static void bbr_note_loss(ngtcp2_cc_bbr *bbr) { +  if (bbr->loss_in_round) { +    return; +  } + +  bbr->loss_in_round = 1; +  bbr->loss_round_delivered = bbr->rst->delivered; +} + +static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                   const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { +  ngtcp2_rs rs = {0}; + +  bbr_note_loss(bbr); + +  if (!bbr->bw_probe_samples) { +    return; +  } + +  rs.tx_in_flight = pkt->tx_in_flight; +  /* bbr->rst->lost is not incremented for pkt yet */ +  rs.lost = bbr->rst->lost + pkt->pktlen - pkt->lost; +  rs.is_app_limited = pkt->is_app_limited; + +  if (is_inflight_too_high(&rs)) { +    rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt); + +    bbr_handle_inflight_too_high(bbr, cstat, &rs, ts); +  } +} + +static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, +                                                 const ngtcp2_rs *rs, +                                                 const ngtcp2_cc_pkt *pkt) { +  uint64_t inflight_prev, lost_prev, lost_prefix; +  (void)bbr; + +  assert(rs->tx_in_flight >= pkt->pktlen); + +  inflight_prev = rs->tx_in_flight - pkt->pktlen; + +  assert(rs->lost >= pkt->pktlen); + +  lost_prev = rs->lost - pkt->pktlen; + +  if (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER < +      lost_prev * NGTCP2_BBR_LOSS_THRESH_DENOM) { +    return inflight_prev; +  } + +  lost_prefix = (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER - +                 lost_prev * NGTCP2_BBR_LOSS_THRESH_DENOM) / +                (NGTCP2_BBR_LOSS_THRESH_DENOM - NGTCP2_BBR_LOSS_THRESH_NUMER); + +  return inflight_prev + lost_prefix; +} + +static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, +                               ngtcp2_tstamp ts) { +  int min_rtt_expired; + +  bbr->probe_rtt_expired = +    ts > bbr->probe_rtt_min_stamp + NGTCP2_BBR_PROBE_RTT_INTERVAL; + +  if (ack->rtt != UINT64_MAX && +      (ack->rtt < bbr->probe_rtt_min_delay || bbr->probe_rtt_expired)) { +    bbr->probe_rtt_min_delay = ack->rtt; +    bbr->probe_rtt_min_stamp = ts; +  } + +  min_rtt_expired = ts > bbr->min_rtt_stamp + NGTCP2_BBR_MIN_RTT_FILTERLEN; + +  if (bbr->probe_rtt_min_delay < bbr->min_rtt || min_rtt_expired) { +    bbr->min_rtt = bbr->probe_rtt_min_delay; +    bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp; + +    ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, +                    "bbr update min_rtt=%" PRIu64, bbr->min_rtt); +  } +} + +static void bbr_check_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                ngtcp2_tstamp ts) { +  if (bbr->state != NGTCP2_BBR_STATE_PROBE_RTT && bbr->probe_rtt_expired && +      !bbr->idle_restart) { +    bbr_enter_probe_rtt(bbr); +    bbr_save_cwnd(bbr, cstat); + +    bbr->probe_rtt_done_stamp = UINT64_MAX; +    bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STOPPING; + +    bbr_start_round(bbr); +  } + +  if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) { +    bbr_handle_probe_rtt(bbr, cstat, ts); +  } + +  if (bbr->rst->rs.delivered) { +    bbr->idle_restart = 0; +  } +} + +static void bbr_enter_probe_rtt(ngtcp2_cc_bbr *bbr) { +  ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr enter ProbeRTT"); + +  bbr->state = NGTCP2_BBR_STATE_PROBE_RTT; +  bbr->pacing_gain_h = 100; +  bbr->cwnd_gain_h = NGTCP2_BBR_PROBE_RTT_CWND_GAIN_H; +} + +static void bbr_handle_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                 ngtcp2_tstamp ts) { +  bbr_mark_connection_app_limited(bbr, cstat); + +  if (bbr->probe_rtt_done_stamp == UINT64_MAX && +      cstat->bytes_in_flight <= bbr_probe_rtt_cwnd(bbr, cstat)) { +    bbr->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION; +    bbr->probe_rtt_round_done = 0; + +    bbr_start_round(bbr); + +    return; +  } + +  if (bbr->probe_rtt_done_stamp != UINT64_MAX) { +    if (bbr->round_start) { +      bbr->probe_rtt_round_done = 1; +    } + +    if (bbr->probe_rtt_round_done) { +      bbr_check_probe_rtt_done(bbr, cstat, ts); +    } +  } +} + +static void bbr_check_probe_rtt_done(ngtcp2_cc_bbr *bbr, +                                     ngtcp2_conn_stat *cstat, +                                     ngtcp2_tstamp ts) { +  if (bbr->probe_rtt_done_stamp != UINT64_MAX && +      ts > bbr->probe_rtt_done_stamp) { +    bbr->probe_rtt_min_stamp = ts; +    bbr_restore_cwnd(bbr, cstat); +    bbr_exit_probe_rtt(bbr, ts); +  } +} + +static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr, +                                            ngtcp2_conn_stat *cstat) { +  uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight; + +  if (app_limited) { +    bbr->rst->app_limited = app_limited; +  } else { +    bbr->rst->app_limited = cstat->max_tx_udp_payload_size; +  } +} + +static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) { +  bbr_reset_lower_bounds(bbr); + +  if (bbr->full_bw_reached) { +    bbr_start_probe_bw_down(bbr, ts); +    bbr_start_probe_bw_cruise(bbr); +  } else { +    bbr_enter_startup(bbr); +  } +} + +static void bbr_handle_restart_from_idle(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp ts) { +  if (cstat->bytes_in_flight == 0 && bbr->rst->app_limited) { +    ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr restart from idle"); + +    bbr->idle_restart = 1; +    bbr->extra_acked_interval_start = ts; + +    if (bbr_is_in_probe_bw_state(bbr)) { +      bbr_set_pacing_rate_with_gain(bbr, cstat, 100); +    } else if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) { +      bbr_check_probe_rtt_done(bbr, cstat, ts); +    } +  } +} + +static uint64_t bbr_bdp_multiple(ngtcp2_cc_bbr *bbr, uint64_t gain_h) { +  uint64_t bdp; + +  if (bbr->min_rtt == UINT64_MAX) { +    return bbr->initial_cwnd; +  } + +  bdp = bbr->bw * bbr->min_rtt / NGTCP2_SECONDS; + +  return (uint64_t)(bdp * gain_h / 100); +} + +static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { +  return max_udp_payload_size * 4; +} + +static uint64_t bbr_quantization_budget(ngtcp2_cc_bbr *bbr, +                                        ngtcp2_conn_stat *cstat, +                                        uint64_t inflight) { +  bbr_update_offload_budget(bbr, cstat); + +  inflight = ngtcp2_max_uint64(inflight, bbr->offload_budget); +  inflight = +    ngtcp2_max_uint64(inflight, min_pipe_cwnd(cstat->max_tx_udp_payload_size)); + +  if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) { +    inflight += 2 * cstat->max_tx_udp_payload_size; +  } + +  return inflight; +} + +static uint64_t bbr_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                             uint64_t gain_h) { +  uint64_t inflight = bbr_bdp_multiple(bbr, gain_h); + +  return bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr, +                                    ngtcp2_conn_stat *cstat) { +  uint64_t inflight; + +  /* Not documented */ +  /* bbr_update_aggregation_budget(bbr); */ + +  inflight = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h) + bbr->extra_acked; +  bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight); +} + +static void bbr_update_offload_budget(ngtcp2_cc_bbr *bbr, +                                      ngtcp2_conn_stat *cstat) { +  bbr->offload_budget = 3 * cstat->send_quantum; +} + +static void bbr_advance_max_bw_filter(ngtcp2_cc_bbr *bbr) { +  ++bbr->cycle_count; +} + +static void bbr_save_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR_STATE_PROBE_RTT) { +    bbr->prior_cwnd = cstat->cwnd; +    return; +  } + +  bbr->prior_cwnd = ngtcp2_max_uint64(bbr->prior_cwnd, cstat->cwnd); +} + +static void bbr_restore_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  cstat->cwnd = ngtcp2_max_uint64(cstat->cwnd, bbr->prior_cwnd); +} + +static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr, +                                   ngtcp2_conn_stat *cstat) { +  uint64_t probe_rtt_cwnd = +    bbr_bdp_multiple(bbr, NGTCP2_BBR_PROBE_RTT_CWND_GAIN_H); +  uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); + +  return ngtcp2_max_uint64(probe_rtt_cwnd, mpcwnd); +} + +static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr, +                                         ngtcp2_conn_stat *cstat) { +  uint64_t probe_rtt_cwnd; + +  if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) { +    probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat); + +    cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, probe_rtt_cwnd); +  } +} + +static void bbr_set_cwnd(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                         const ngtcp2_cc_ack *ack) { +  uint64_t mpcwnd; + +  bbr_update_max_inflight(bbr, cstat); + +  if (bbr->full_bw_reached) { +    cstat->cwnd += ack->bytes_delivered; +    cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, bbr->max_inflight); +  } else if (cstat->cwnd < bbr->max_inflight || +             bbr->rst->delivered < bbr->initial_cwnd) { +    cstat->cwnd += ack->bytes_delivered; +  } + +  mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); +  cstat->cwnd = ngtcp2_max_uint64(cstat->cwnd, mpcwnd); + +  bbr_bound_cwnd_for_probe_rtt(bbr, cstat); +  bbr_bound_cwnd_for_model(bbr, cstat); +} + +static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr, +                                     ngtcp2_conn_stat *cstat) { +  uint64_t cap = UINT64_MAX; +  uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); + +  if (bbr_is_in_probe_bw_state(bbr) && +      bbr->state != NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { +    cap = bbr->inflight_hi; +  } else if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT || +             bbr->state == NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { +    cap = bbr_inflight_with_headroom(bbr, cstat); +  } + +  cap = ngtcp2_min_uint64(cap, bbr->inflight_lo); +  cap = ngtcp2_max_uint64(cap, mpcwnd); + +  cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, cap); +} + +static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { +  size_t send_quantum = 64 * 1024; +  (void)bbr; + +  if (cstat->pacing_interval) { +    send_quantum = ngtcp2_min_size( +      send_quantum, (size_t)(NGTCP2_MILLISECONDS / cstat->pacing_interval)); +  } + +  cstat->send_quantum = +    ngtcp2_max_size(send_quantum, 2 * cstat->max_tx_udp_payload_size); +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, +                                  ngtcp2_tstamp sent_time) { +  return cstat->congestion_recovery_start_ts != UINT64_MAX && +         sent_time <= cstat->congestion_recovery_start_ts; +} + +static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, +                                const ngtcp2_cc_ack *ack) { +  if (bbr->in_loss_recovery) { +    if (ack->largest_pkt_sent_ts != UINT64_MAX && +        !in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) { +      bbr->in_loss_recovery = 0; +      bbr->round_count_at_recovery = UINT64_MAX; +      bbr_restore_cwnd(bbr, cstat); +    } + +    return; +  } + +  if (bbr->congestion_recovery_start_ts != UINT64_MAX) { +    bbr->in_loss_recovery = 1; +    bbr->round_count_at_recovery = +      bbr->round_start ? bbr->round_count : bbr->round_count + 1; +    bbr_save_cwnd(bbr, cstat); +    cstat->cwnd = +      cstat->bytes_in_flight + +      ngtcp2_max_uint64(ack->bytes_delivered, cstat->max_tx_udp_payload_size); + +    cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts; +    bbr->congestion_recovery_start_ts = UINT64_MAX; +  } +} + +static void bbr_cc_on_pkt_lost(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                               const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); + +  if (bbr->state == NGTCP2_BBR_STATE_STARTUP) { +    return; +  } + +  bbr_update_on_loss(bbr, cstat, pkt, ts); +} + +static void bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                    ngtcp2_tstamp sent_ts, uint64_t bytes_lost, +                                    ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); +  (void)bytes_lost; + +  if (bbr->in_loss_recovery || +      bbr->congestion_recovery_start_ts != UINT64_MAX || +      in_congestion_recovery(cstat, sent_ts)) { +    return; +  } + +  bbr->congestion_recovery_start_ts = ts; +} + +static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc, +                                          ngtcp2_conn_stat *cstat, +                                          ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); +  (void)ts; + +  bbr->congestion_recovery_start_ts = UINT64_MAX; +  cstat->congestion_recovery_start_ts = UINT64_MAX; + +  if (bbr->in_loss_recovery) { +    bbr->in_loss_recovery = 0; +    bbr->round_count_at_recovery = UINT64_MAX; +    bbr_restore_cwnd(bbr, cstat); +  } +} + +static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc, +                                            ngtcp2_conn_stat *cstat, +                                            ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); +  (void)ts; + +  cstat->congestion_recovery_start_ts = UINT64_MAX; +  bbr->congestion_recovery_start_ts = UINT64_MAX; +  bbr->in_loss_recovery = 0; +  bbr->round_count_at_recovery = UINT64_MAX; + +  bbr_save_cwnd(bbr, cstat); +  cstat->cwnd = cstat->bytes_in_flight + cstat->max_tx_udp_payload_size; +  cstat->cwnd = ngtcp2_max_uint64( +    cstat->cwnd, min_pipe_cwnd(cstat->max_tx_udp_payload_size)); +} + +static void bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                               const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); + +  bbr_handle_recovery(bbr, cstat, ack); +  bbr_update_on_ack(bbr, cstat, ack, ts); +} + +static void bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                               const ngtcp2_cc_pkt *pkt) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); + +  bbr_on_transmit(bbr, cstat, pkt->sent_ts); +} + +static void bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                         ngtcp2_tstamp ts) { +  ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); + +  bbr_on_init(bbr, cstat, ts); +} + +void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, +                        ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, +                        ngtcp2_tstamp initial_ts, ngtcp2_rand rand, +                        const ngtcp2_rand_ctx *rand_ctx) { +  memset(bbr, 0, sizeof(*bbr)); + +  bbr->cc.log = log; +  bbr->cc.on_pkt_lost = bbr_cc_on_pkt_lost; +  bbr->cc.congestion_event = bbr_cc_congestion_event; +  bbr->cc.on_spurious_congestion = bbr_cc_on_spurious_congestion; +  bbr->cc.on_persistent_congestion = bbr_cc_on_persistent_congestion; +  bbr->cc.on_ack_recv = bbr_cc_on_ack_recv; +  bbr->cc.on_pkt_sent = bbr_cc_on_pkt_sent; +  bbr->cc.reset = bbr_cc_reset; + +  bbr->rst = rst; +  bbr->rand = rand; +  bbr->rand_ctx = *rand_ctx; +  bbr->initial_cwnd = cstat->cwnd; + +  bbr_on_init(bbr, cstat, initial_ts); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h new file mode 100644 index 00000000000..f266ec5d71e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h @@ -0,0 +1,141 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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_BBR_H +#define NGTCP2_BBR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_cc.h" +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rst ngtcp2_rst; + +typedef enum ngtcp2_bbr_state { +  NGTCP2_BBR_STATE_STARTUP, +  NGTCP2_BBR_STATE_DRAIN, +  NGTCP2_BBR_STATE_PROBE_BW_DOWN, +  NGTCP2_BBR_STATE_PROBE_BW_CRUISE, +  NGTCP2_BBR_STATE_PROBE_BW_REFILL, +  NGTCP2_BBR_STATE_PROBE_BW_UP, +  NGTCP2_BBR_STATE_PROBE_RTT, +} ngtcp2_bbr_state; + +typedef enum ngtcp2_bbr_ack_phase { +  NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING, +  NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STOPPING, +  NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK, +  NGTCP2_BBR_ACK_PHASE_ACKS_REFILLING, +} ngtcp2_bbr_ack_phase; + +/* + * ngtcp2_cc_bbr is BBR v2 congestion controller, described in + * https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01 + */ +typedef struct ngtcp2_cc_bbr { +  ngtcp2_cc cc; + +  uint64_t initial_cwnd; +  ngtcp2_rst *rst; +  ngtcp2_rand rand; +  ngtcp2_rand_ctx rand_ctx; + +  /* max_bw_filter for tracking the maximum recent delivery rate +    samples for estimating max_bw. */ +  ngtcp2_window_filter max_bw_filter; + +  ngtcp2_window_filter extra_acked_filter; + +  ngtcp2_duration min_rtt; +  ngtcp2_tstamp min_rtt_stamp; +  ngtcp2_tstamp probe_rtt_done_stamp; +  int probe_rtt_round_done; +  uint64_t prior_cwnd; +  int idle_restart; +  ngtcp2_tstamp extra_acked_interval_start; +  uint64_t extra_acked_delivered; + +  /* Congestion signals */ +  int loss_in_round; +  uint64_t bw_latest; +  uint64_t inflight_latest; + +  /* Lower bounds */ +  uint64_t bw_lo; +  uint64_t inflight_lo; + +  /* Round counting */ +  uint64_t next_round_delivered; +  int round_start; +  uint64_t round_count; + +  /* Full pipe */ +  uint64_t full_bw; +  size_t full_bw_count; +  int full_bw_reached; +  int full_bw_now; + +  /* Pacing rate */ +  uint64_t pacing_gain_h; + +  ngtcp2_bbr_state state; +  uint64_t cwnd_gain_h; + +  int loss_round_start; +  uint64_t loss_round_delivered; +  uint64_t rounds_since_bw_probe; +  uint64_t max_bw; +  uint64_t bw; +  uint64_t cycle_count; +  uint64_t extra_acked; +  uint64_t bytes_lost_in_round; +  size_t loss_events_in_round; +  uint64_t offload_budget; +  uint64_t probe_up_cnt; +  ngtcp2_tstamp cycle_stamp; +  ngtcp2_bbr_ack_phase ack_phase; +  ngtcp2_duration bw_probe_wait; +  int bw_probe_samples; +  size_t bw_probe_up_rounds; +  uint64_t bw_probe_up_acks; +  uint64_t inflight_hi; +  int probe_rtt_expired; +  ngtcp2_duration probe_rtt_min_delay; +  ngtcp2_tstamp probe_rtt_min_stamp; +  int in_loss_recovery; +  uint64_t round_count_at_recovery; +  uint64_t max_inflight; +  ngtcp2_tstamp congestion_recovery_start_ts; +} ngtcp2_cc_bbr; + +void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, +                        ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, +                        ngtcp2_tstamp initial_ts, ngtcp2_rand rand, +                        const ngtcp2_rand_ctx *rand_ctx); + +#endif /* !defined(NGTCP2_BBR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_buf.c b/contrib/libs/ngtcp2/lib/ngtcp2_buf.c new file mode 100644 index 00000000000..75326d6b76b --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_buf.c @@ -0,0 +1,56 @@ +/* + * 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" +#include "ngtcp2_mem.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_cap(const ngtcp2_buf *buf) { +  return (size_t)(buf->end - buf->begin); +} + +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, +                         const ngtcp2_mem *mem) { +  *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len); +  if (*pbufchain == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  (*pbufchain)->next = NULL; + +  ngtcp2_buf_init(&(*pbufchain)->buf, +                  (uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len); + +  return 0; +} + +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) { +  ngtcp2_mem_free(mem, bufchain); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_buf.h b/contrib/libs/ngtcp2/lib/ngtcp2_buf.h new file mode 100644 index 00000000000..e87adb11991 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_buf.h @@ -0,0 +1,108 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_BUF_H +#define NGTCP2_BUF_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_buf { +  /* 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 points 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. + */ +#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last) + +/* + * ngtcp2_buf_len returns the number of bytes left to read.  In other + * words, it returns buf->last - buf->pos. + */ +#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) + +/* + * 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); + +/* + * ngtcp2_buf_chain is a linked list of ngtcp2_buf. + */ +typedef struct ngtcp2_buf_chain ngtcp2_buf_chain; + +struct ngtcp2_buf_chain { +  ngtcp2_buf_chain *next; +  ngtcp2_buf buf; +}; + +/* + * ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes + * the internal buffer with |len| bytes space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, +                         const ngtcp2_mem *mem); + +/* + * ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|. + * It also deletes the memory pointed by |bufchain|. + */ +void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem); + +#endif /* !defined(NGTCP2_BUF_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_cc.c b/contrib/libs/ngtcp2/lib/ngtcp2_cc.c new file mode 100644 index 00000000000..1ff59f315c5 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_cc.c @@ -0,0 +1,511 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_cc.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_conn_stat.h" +#include "ngtcp2_rst.h" +#include "ngtcp2_unreachable.h" + +uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) { +  uint64_t n = 2 * max_udp_payload_size; +  n = ngtcp2_max_uint64(n, 14720); +  return ngtcp2_min_uint64(10 * max_udp_payload_size, n); +} + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, +                                  size_t pktlen, ngtcp2_pktns_id pktns_id, +                                  ngtcp2_tstamp sent_ts, uint64_t lost, +                                  uint64_t tx_in_flight, int is_app_limited) { +  pkt->pkt_num = pkt_num; +  pkt->pktlen = pktlen; +  pkt->pktns_id = pktns_id; +  pkt->sent_ts = sent_ts; +  pkt->lost = lost; +  pkt->tx_in_flight = tx_in_flight; +  pkt->is_app_limited = is_app_limited; + +  return pkt; +} + +static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; } + +void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) { +  memset(reno, 0, sizeof(*reno)); + +  reno->cc.log = log; +  reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked; +  reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event; +  reno->cc.on_persistent_congestion = +    ngtcp2_cc_reno_cc_on_persistent_congestion; +  reno->cc.reset = ngtcp2_cc_reno_cc_reset; + +  reno_cc_reset(reno); +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, +                                  ngtcp2_tstamp sent_time) { +  return cstat->congestion_recovery_start_ts != UINT64_MAX && +         sent_time <= cstat->congestion_recovery_start_ts; +} + +void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                    const ngtcp2_cc_pkt *pkt, +                                    ngtcp2_tstamp ts) { +  ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc); +  uint64_t m; +  (void)ts; + +  if (in_congestion_recovery(cstat, pkt->sent_ts) || pkt->is_app_limited) { +    return; +  } + +  if (cstat->cwnd < cstat->ssthresh) { +    cstat->cwnd += pkt->pktlen; +    ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA, +                    "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64, +                    pkt->pkt_num, cstat->cwnd); +    return; +  } + +  m = cstat->max_tx_udp_payload_size * pkt->pktlen + reno->pending_add; +  reno->pending_add = m % cstat->cwnd; + +  cstat->cwnd += m / cstat->cwnd; +} + +void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                        ngtcp2_tstamp sent_ts, +                                        uint64_t bytes_lost, ngtcp2_tstamp ts) { +  ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc); +  uint64_t min_cwnd; +  (void)bytes_lost; + +  if (in_congestion_recovery(cstat, sent_ts)) { +    return; +  } + +  cstat->congestion_recovery_start_ts = ts; +  cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS; +  min_cwnd = 2 * cstat->max_tx_udp_payload_size; +  cstat->cwnd = ngtcp2_max_uint64(cstat->cwnd, min_cwnd); +  cstat->ssthresh = cstat->cwnd; + +  reno->pending_add = 0; + +  ngtcp2_log_info(reno->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "reduce cwnd because of packet loss cwnd=%" PRIu64, +                  cstat->cwnd); +} + +void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, +                                                ngtcp2_conn_stat *cstat, +                                                ngtcp2_tstamp ts) { +  (void)cc; +  (void)ts; + +  cstat->cwnd = 2 * cstat->max_tx_udp_payload_size; +  cstat->congestion_recovery_start_ts = UINT64_MAX; +} + +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                             ngtcp2_tstamp ts) { +  ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc); +  (void)cstat; +  (void)ts; + +  reno_cc_reset(reno); +} + +static void cubic_vars_reset(ngtcp2_cubic_vars *v) { +  v->cwnd_prior = 0; +  v->w_max = 0; +  v->k = 0; +  v->epoch_start = UINT64_MAX; +  v->w_est = 0; + +  v->state = NGTCP2_CUBIC_STATE_INITIAL; +  v->app_limited_start_ts = UINT64_MAX; +  v->app_limited_duration = 0; +  v->pending_bytes_delivered = 0; +  v->pending_est_bytes_delivered = 0; +} + +static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) { +  cubic_vars_reset(&cubic->current); +  cubic_vars_reset(&cubic->undo.v); +  cubic->undo.cwnd = 0; +  cubic->undo.ssthresh = 0; + +  cubic->hs.current_round_min_rtt = UINT64_MAX; +  cubic->hs.last_round_min_rtt = UINT64_MAX; +  cubic->hs.curr_rtt = UINT64_MAX; +  cubic->hs.rtt_sample_count = 0; +  cubic->hs.css_baseline_min_rtt = UINT64_MAX; +  cubic->hs.css_round = 0; + +  cubic->next_round_delivered = 0; +} + +void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log, +                          ngtcp2_rst *rst) { +  memset(cubic, 0, sizeof(*cubic)); + +  cubic->cc.log = log; +  cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; +  cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event; +  cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion; +  cubic->cc.on_persistent_congestion = +    ngtcp2_cc_cubic_cc_on_persistent_congestion; +  cubic->cc.reset = ngtcp2_cc_cubic_cc_reset; + +  cubic->rst = rst; + +  cubic_cc_reset(cubic); +} + +uint64_t ngtcp2_cbrt(uint64_t n) { +  size_t s; +  uint64_t y = 0; +  uint64_t b; + +  for (s = 63; s > 0; s -= 3) { +    y <<= 1; +    b = 3 * y * (y + 1) + 1; +    if ((n >> s) >= b) { +      n -= b << s; +      y++; +    } +  } + +  y <<= 1; +  b = 3 * y * (y + 1) + 1; +  if (n >= b) { +    n -= b; +    y++; +  } + +  return y; +} + +/* RFC 9406 HyStart++ constants */ +#define NGTCP2_HS_MIN_RTT_THRESH (4 * NGTCP2_MILLISECONDS) +#define NGTCP2_HS_MAX_RTT_THRESH (16 * NGTCP2_MILLISECONDS) +#define NGTCP2_HS_MIN_RTT_DIVISOR 8 +#define NGTCP2_HS_N_RTT_SAMPLE 8 +#define NGTCP2_HS_CSS_GROWTH_DIVISOR 4 +#define NGTCP2_HS_CSS_ROUNDS 5 + +static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic, +                                         const ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp ts) { +  ngtcp2_duration t = ts - cubic->current.epoch_start; +  uint64_t delta; +  uint64_t tx = (t << 10) / NGTCP2_SECONDS; +  uint64_t kx = (cubic->current.k << 10) / NGTCP2_SECONDS; +  uint64_t time_delta; + +  if (tx < kx) { +    return UINT64_MAX; +  } + +  time_delta = tx - kx; + +  delta = cstat->max_tx_udp_payload_size * +          ((((time_delta * time_delta) >> 10) * time_delta) >> 10) * 4 / 10; + +  return cubic->current.w_max + (delta >> 10); +} + +void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                    const ngtcp2_cc_ack *ack, +                                    ngtcp2_tstamp ts) { +  ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); +  uint64_t w_cubic, w_cubic_next, target, m; +  ngtcp2_duration rtt_thresh; +  int round_start; + +  if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) { +    return; +  } + +  if (cubic->current.state == NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE) { +    if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) { +      if (cubic->current.app_limited_start_ts == UINT64_MAX) { +        cubic->current.app_limited_start_ts = ts; +      } + +      return; +    } + +    if (cubic->current.app_limited_start_ts != UINT64_MAX) { +      cubic->current.app_limited_duration += +        ts - cubic->current.app_limited_start_ts; +      cubic->current.app_limited_start_ts = UINT64_MAX; +    } +  } else if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) { +    return; +  } + +  round_start = ack->pkt_delivered >= cubic->next_round_delivered; +  if (round_start) { +    cubic->next_round_delivered = cubic->rst->delivered; + +    cubic->rst->is_cwnd_limited = 0; +  } + +  if (cstat->cwnd < cstat->ssthresh) { +    /* slow-start */ +    if (cubic->hs.css_round) { +      cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR; +    } else { +      cstat->cwnd += ack->bytes_delivered; +    } + +    ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, +                    "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64, +                    ack->bytes_delivered, cstat->cwnd); + +    if (round_start) { +      cubic->hs.last_round_min_rtt = cubic->hs.current_round_min_rtt; +      cubic->hs.current_round_min_rtt = UINT64_MAX; +      cubic->hs.rtt_sample_count = 0; + +      if (cubic->hs.css_round) { +        ++cubic->hs.css_round; +      } +    } + +    cubic->hs.current_round_min_rtt = +      ngtcp2_min_uint64(cubic->hs.current_round_min_rtt, ack->rtt); +    ++cubic->hs.rtt_sample_count; + +    if (cubic->hs.css_round) { +      if (cubic->hs.current_round_min_rtt < cubic->hs.css_baseline_min_rtt) { +        cubic->hs.css_baseline_min_rtt = UINT64_MAX; +        cubic->hs.css_round = 0; +        return; +      } + +      if (cubic->hs.css_round >= NGTCP2_HS_CSS_ROUNDS) { +        ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, +                        "HyStart++ exit slow start"); + +        cstat->ssthresh = cstat->cwnd; +      } + +      return; +    } + +    if (cubic->hs.rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE && +        cubic->hs.current_round_min_rtt != UINT64_MAX && +        cubic->hs.last_round_min_rtt != UINT64_MAX) { +      rtt_thresh = +        ngtcp2_max_uint64(NGTCP2_HS_MIN_RTT_THRESH, +                          ngtcp2_min_uint64(cubic->hs.last_round_min_rtt / +                                              NGTCP2_HS_MIN_RTT_DIVISOR, +                                            NGTCP2_HS_MAX_RTT_THRESH)); + +      if (cubic->hs.current_round_min_rtt >= +          cubic->hs.last_round_min_rtt + rtt_thresh) { +        cubic->hs.css_baseline_min_rtt = cubic->hs.current_round_min_rtt; +        cubic->hs.css_round = 1; +      } +    } + +    return; +  } + +  /* congestion avoidance */ + +  switch (cubic->current.state) { +  case NGTCP2_CUBIC_STATE_INITIAL: +    m = cstat->max_tx_udp_payload_size * ack->bytes_delivered + +        cubic->current.pending_bytes_delivered; +    cstat->cwnd += m / cstat->cwnd; +    cubic->current.pending_bytes_delivered = m % cstat->cwnd; +    return; +  case NGTCP2_CUBIC_STATE_RECOVERY: +    cubic->current.state = NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE; +    cubic->current.epoch_start = ts; +    break; +  default: +    break; +  } + +  w_cubic = cubic_cc_compute_w_cubic(cubic, cstat, +                                     ts - cubic->current.app_limited_duration); +  w_cubic_next = cubic_cc_compute_w_cubic( +    cubic, cstat, +    ts - cubic->current.app_limited_duration + cstat->smoothed_rtt); + +  if (w_cubic_next == UINT64_MAX || w_cubic_next < cstat->cwnd) { +    target = cstat->cwnd; +  } else if (2 * w_cubic_next > 3 * cstat->cwnd) { +    target = cstat->cwnd * 3 / 2; +  } else { +    target = w_cubic_next; +  } + +  m = ack->bytes_delivered * cstat->max_tx_udp_payload_size + +      cubic->current.pending_est_bytes_delivered; +  cubic->current.pending_est_bytes_delivered = m % cstat->cwnd; + +  if (cubic->current.w_est < cubic->current.cwnd_prior) { +    cubic->current.w_est += m * 9 / 17 / cstat->cwnd; +  } else { +    cubic->current.w_est += m / cstat->cwnd; +  } + +  if (w_cubic == UINT64_MAX || cubic->current.w_est > w_cubic) { +    cstat->cwnd = cubic->current.w_est; +  } else { +    m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size + +        cubic->current.pending_bytes_delivered; +    cstat->cwnd += m / cstat->cwnd; +    cubic->current.pending_bytes_delivered = m % cstat->cwnd; +  } + +  ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64 +                  " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64, +                  ack->bytes_delivered, cstat->cwnd, cubic->current.k, target, +                  cubic->current.w_est); +} + +void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp sent_ts, +                                         uint64_t bytes_lost, +                                         ngtcp2_tstamp ts) { +  ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); +  uint64_t flight_size; + +  if (in_congestion_recovery(cstat, sent_ts)) { +    return; +  } + +  if (cubic->undo.cwnd < cstat->cwnd) { +    cubic->undo.v = cubic->current; +    cubic->undo.cwnd = cstat->cwnd; +    cubic->undo.ssthresh = cstat->ssthresh; +  } + +  cstat->congestion_recovery_start_ts = ts; + +  cubic->current.state = NGTCP2_CUBIC_STATE_RECOVERY; +  cubic->current.epoch_start = UINT64_MAX; +  cubic->current.app_limited_start_ts = UINT64_MAX; +  cubic->current.app_limited_duration = 0; +  cubic->current.pending_bytes_delivered = 0; +  cubic->current.pending_est_bytes_delivered = 0; + +  if (cstat->cwnd < cubic->current.w_max) { +    cubic->current.w_max = cstat->cwnd * 17 / 20; +  } else { +    cubic->current.w_max = cstat->cwnd; +  } + +  cstat->ssthresh = cstat->cwnd * 7 / 10; + +  if (cubic->rst->rs.delivered * 2 < cstat->cwnd) { +    flight_size = cstat->bytes_in_flight + bytes_lost; +    cstat->ssthresh = ngtcp2_min_uint64( +      cstat->ssthresh, +      ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size) * 7 / 10); +  } + +  cstat->ssthresh = +    ngtcp2_max_uint64(cstat->ssthresh, 2 * cstat->max_tx_udp_payload_size); + +  cubic->current.cwnd_prior = cstat->cwnd; +  cstat->cwnd = cstat->ssthresh; + +  cubic->current.w_est = cstat->cwnd; + +  if (cstat->cwnd < cubic->current.w_max) { +    cubic->current.k = +      ngtcp2_cbrt(((cubic->current.w_max - cstat->cwnd) << 10) * 10 / 4 / +                  cstat->max_tx_udp_payload_size) * +      NGTCP2_SECONDS; +    cubic->current.k >>= 10; +  } else { +    cubic->current.k = 0; +  } + +  ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, +                  "reduce cwnd because of packet loss cwnd=%" PRIu64, +                  cstat->cwnd); +} + +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc, +                                               ngtcp2_conn_stat *cstat, +                                               ngtcp2_tstamp ts) { +  ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); +  (void)ts; + +  cstat->congestion_recovery_start_ts = UINT64_MAX; + +  if (cstat->cwnd < cubic->undo.cwnd) { +    cubic->current = cubic->undo.v; +    cstat->cwnd = cubic->undo.cwnd; +    cstat->ssthresh = cubic->undo.ssthresh; + +    ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, +                    "spurious congestion is detected and congestion state is " +                    "restored cwnd=%" PRIu64, +                    cstat->cwnd); +  } + +  cubic_vars_reset(&cubic->undo.v); +  cubic->undo.cwnd = 0; +  cubic->undo.ssthresh = 0; +} + +void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, +                                                 ngtcp2_conn_stat *cstat, +                                                 ngtcp2_tstamp ts) { +  ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); +  (void)ts; + +  cubic_cc_reset(cubic); + +  cstat->cwnd = 2 * cstat->max_tx_udp_payload_size; +  cstat->congestion_recovery_start_ts = UINT64_MAX; +} + +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                              ngtcp2_tstamp ts) { +  ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); +  (void)cstat; +  (void)ts; + +  cubic_cc_reset(cubic); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_cc.h b/contrib/libs/ngtcp2/lib/ngtcp2_cc.h new file mode 100644 index 00000000000..e3c363a51bb --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_cc.h @@ -0,0 +1,409 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CC_H +#define NGTCP2_CC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pktns_id.h" + +#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1 +#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3 + +typedef struct ngtcp2_log ngtcp2_log; +typedef struct ngtcp2_conn_stat ngtcp2_conn_stat; +typedef struct ngtcp2_rst ngtcp2_rst; + +/** + * @struct + * + * :type:`ngtcp2_cc_pkt` is a convenient structure to include + * acked/lost/sent packet. + */ +typedef struct ngtcp2_cc_pkt { +  /** +   * :member:`pkt_num` is the packet number +   */ +  int64_t pkt_num; +  /** +   * :member:`pktlen` is the length of packet. +   */ +  size_t pktlen; +  /** +   * :member:`pktns_id` is the ID of packet number space which this +   * packet belongs to. +   */ +  ngtcp2_pktns_id pktns_id; +  /** +   * :member:`sent_ts` is the timestamp when packet is sent. +   */ +  ngtcp2_tstamp sent_ts; +  /** +   * :member:`lost` is the number of bytes lost when this packet was +   * sent. +   */ +  uint64_t lost; +  /** +   * :member:`tx_in_flight` is the bytes in flight when this packet +   * was sent. +   */ +  uint64_t tx_in_flight; +  /** +   * :member:`is_app_limited` is nonzero if the connection is +   * app-limited when this packet was sent. +   */ +  int is_app_limited; +} ngtcp2_cc_pkt; + +/** + * @struct + * + * :type:`ngtcp2_cc_ack` is a convenient structure which stores + * acknowledged and lost bytes. + */ +typedef struct ngtcp2_cc_ack { +  /** +   * :member:`prior_bytes_in_flight` is the in-flight bytes before +   * processing this ACK. +   */ +  uint64_t prior_bytes_in_flight; +  /** +   * :member:`bytes_delivered` is the number of bytes acknowledged. +   */ +  uint64_t bytes_delivered; +  /** +   * :member:`bytes_lost` is the number of bytes declared lost. +   */ +  uint64_t bytes_lost; +  /** +   * :member:`pkt_delivered` is the cumulative acknowledged bytes when +   * the last packet acknowledged by this ACK was sent. +   */ +  uint64_t pkt_delivered; +  /** +   * :member:`largest_pkt_sent_ts` is the time when the largest +   * acknowledged packet was sent.  It is UINT64_MAX if it is unknown. +   */ +  ngtcp2_tstamp largest_pkt_sent_ts; +  /** +   * :member:`rtt` is the RTT sample.  It is UINT64_MAX if no RTT +   * sample is available. +   */ +  ngtcp2_duration rtt; +} ngtcp2_cc_ack; + +typedef struct ngtcp2_cc ngtcp2_cc; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is + * called with an acknowledged packet. + */ +typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                       const ngtcp2_cc_pkt *pkt, +                                       ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is + * called with a lost packet. + */ +typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                      const ngtcp2_cc_pkt *pkt, +                                      ngtcp2_tstamp ts); +/** + * @functypedef + * + * :type:`ngtcp2_cc_congestion_event` is a callback function which is + * called when congestion event happens (e.g., when packet is lost). + * |bytes_lost| is the number of bytes lost in this congestion event. + */ +typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, +                                           ngtcp2_conn_stat *cstat, +                                           ngtcp2_tstamp sent_ts, +                                           uint64_t bytes_lost, +                                           ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_spurious_congestion` is a callback function + * which is called when a spurious congestion is detected. + */ +typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc, +                                                 ngtcp2_conn_stat *cstat, +                                                 ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function + * which is called when persistent congestion is established. + */ +typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, +                                                   ngtcp2_conn_stat *cstat, +                                                   ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is + * called when an acknowledgement is received. + */ +typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                      const ngtcp2_cc_ack *ack, +                                      ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is + * called when an ack-eliciting packet is sent. + */ +typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                      const ngtcp2_cc_pkt *pkt); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is + * called when new RTT sample is obtained. + */ +typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_reset` is a callback function which is called when + * congestion state must be reset. + */ +typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                ngtcp2_tstamp ts); + +/** + * @enum + * + * :type:`ngtcp2_cc_event_type` defines congestion control events. + */ +typedef enum ngtcp2_cc_event_type { +  /** +   * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet +   * is sent and no other ack-eliciting packet is present. +   */ +  NGTCP2_CC_EVENT_TYPE_TX_START +} ngtcp2_cc_event_type; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_event` is a callback function which is called when + * a specific event happens. + */ +typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +/** + * @struct + * + * :type:`ngtcp2_cc` is congestion control algorithm interface shared + * by implementations.  All callback functions are optional. + */ +typedef struct ngtcp2_cc { +  /** +   * :member:`log` is ngtcp2 library internal logger. +   */ +  ngtcp2_log *log; +  /** +   * :member:`on_pkt_acked` is a callback function which is called +   * when a packet is acknowledged. +   */ +  ngtcp2_cc_on_pkt_acked on_pkt_acked; +  /** +   * :member:`on_pkt_lost` is a callback function which is called when +   * a packet is lost. +   */ +  ngtcp2_cc_on_pkt_lost on_pkt_lost; +  /** +   * :member:`congestion_event` is a callback function which is called +   * when congestion event happens (.e.g, packet is lost). +   */ +  ngtcp2_cc_congestion_event congestion_event; +  /** +   * :member:`on_spurious_congestion` is a callback function which is +   * called when a spurious congestion is detected. +   */ +  ngtcp2_cc_on_spurious_congestion on_spurious_congestion; +  /** +   * :member:`on_persistent_congestion` is a callback function which +   * is called when persistent congestion is established. +   */ +  ngtcp2_cc_on_persistent_congestion on_persistent_congestion; +  /** +   * :member:`on_ack_recv` is a callback function which is called when +   * an acknowledgement is received. +   */ +  ngtcp2_cc_on_ack_recv on_ack_recv; +  /** +   * :member:`on_pkt_sent` is a callback function which is called when +   * ack-eliciting packet is sent. +   */ +  ngtcp2_cc_on_pkt_sent on_pkt_sent; +  /** +   * :member:`new_rtt_sample` is a callback function which is called +   * when new RTT sample is obtained. +   */ +  ngtcp2_cc_new_rtt_sample new_rtt_sample; +  /** +   * :member:`reset` is a callback function which is called when +   * congestion control state must be reset. +   */ +  ngtcp2_cc_reset reset; +  /** +   * :member:`event` is a callback function which is called when a +   * specific event happens. +   */ +  ngtcp2_cc_event event; +} ngtcp2_cc; + +/* + * ngtcp2_cc_compute_initcwnd computes initial cwnd. + */ +uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size); + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, +                                  size_t pktlen, ngtcp2_pktns_id pktns_id, +                                  ngtcp2_tstamp sent_ts, uint64_t lost, +                                  uint64_t tx_in_flight, int is_app_limited); + +/* ngtcp2_cc_reno is the RENO congestion controller. */ +typedef struct ngtcp2_cc_reno { +  ngtcp2_cc cc; +  uint64_t pending_add; +} ngtcp2_cc_reno; + +void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log); + +void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                    const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                        ngtcp2_tstamp sent_ts, +                                        uint64_t bytes_lost, ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, +                                                ngtcp2_conn_stat *cstat, +                                                ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                             ngtcp2_tstamp ts); + +typedef enum ngtcp2_cubic_state { +  /* NGTCP2_CUBIC_STATE_INITIAL is the state where CUBIC is in slow +     start phase, or congestion avoidance phase before congestion +     events occur. */ +  NGTCP2_CUBIC_STATE_INITIAL, +  /* NGTCP2_CUBIC_STATE_RECOVERY is the state that a connection is in +     recovery period. */ +  NGTCP2_CUBIC_STATE_RECOVERY, +  /* NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE is the state where CUBIC +     is in congestion avoidance phase after recovery period ends. */ +  NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE, +} ngtcp2_cubic_state; + +typedef struct ngtcp2_cubic_vars { +  uint64_t cwnd_prior; +  uint64_t w_max; +  ngtcp2_duration k; +  ngtcp2_tstamp epoch_start; +  uint64_t w_est; + +  ngtcp2_cubic_state state; +  /* app_limited_start_ts is the timestamp where app limited period +     started. */ +  ngtcp2_tstamp app_limited_start_ts; +  /* app_limited_duration is the cumulative duration where a +     connection is under app limited when ACK is received. */ +  ngtcp2_duration app_limited_duration; +  uint64_t pending_bytes_delivered; +  uint64_t pending_est_bytes_delivered; +} ngtcp2_cubic_vars; + +/* ngtcp2_cc_cubic is CUBIC congestion controller. */ +typedef struct ngtcp2_cc_cubic { +  ngtcp2_cc cc; +  ngtcp2_rst *rst; +  /* current is a set of variables that are currently in effect. */ +  ngtcp2_cubic_vars current; +  /* undo stores the congestion state when a congestion event occurs +     in order to restore the state when it turns out that the event is +     spurious. */ +  struct { +    ngtcp2_cubic_vars v; +    uint64_t cwnd; +    uint64_t ssthresh; +  } undo; +  /* HyStart++ variables */ +  struct { +    ngtcp2_duration current_round_min_rtt; +    ngtcp2_duration last_round_min_rtt; +    ngtcp2_duration curr_rtt; +    size_t rtt_sample_count; +    ngtcp2_duration css_baseline_min_rtt; +    size_t css_round; +  } hs; +  uint64_t next_round_delivered; +} ngtcp2_cc_cubic; + +void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cc, ngtcp2_log *log, +                          ngtcp2_rst *rst); + +void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                    const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                                         ngtcp2_tstamp sent_ts, +                                         uint64_t bytes_lost, ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx, +                                               ngtcp2_conn_stat *cstat, +                                               ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, +                                                 ngtcp2_conn_stat *cstat, +                                                 ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                              ngtcp2_tstamp ts); + +uint64_t ngtcp2_cbrt(uint64_t n); + +#endif /* !defined(NGTCP2_CC_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_cid.c b/contrib/libs/ngtcp2/lib/ngtcp2_cid.c new file mode 100644 index 00000000000..181850cfcbc --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_cid.c @@ -0,0 +1,149 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_cid.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_path.h" +#include "ngtcp2_str.h" + +void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); } + +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) { +  scid->pe.index = NGTCP2_PQ_BAD_INDEX; +  scid->seq = seq; +  scid->cid = *cid; +  scid->retired_ts = UINT64_MAX; +  scid->flags = NGTCP2_SCID_FLAG_NONE; +} + +void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { +  ngtcp2_scid_init(dest, src->seq, &src->cid); +  dest->retired_ts = src->retired_ts; +  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); +    dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT; +  } else { +    dcid->flags = NGTCP2_DCID_FLAG_NONE; +  } + +  ngtcp2_path_storage_zero(&dcid->ps); +  dcid->retired_ts = UINT64_MAX; +  dcid->bound_ts = UINT64_MAX; +  dcid->bytes_sent = 0; +  dcid->bytes_recv = 0; +  dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE; +} + +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) { +  assert(token); + +  dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; +  memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) { +  ngtcp2_path_copy(&dcid->ps.path, path); +} + +void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { +  ngtcp2_dcid_init(dest, src->seq, &src->cid, +                   (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token +                                                                 : NULL); +  ngtcp2_path_copy(&dest->ps.path, &src->ps.path); +  dest->retired_ts = src->retired_ts; +  dest->bound_ts = src->bound_ts; +  dest->flags = src->flags; +  dest->bytes_sent = src->bytes_sent; +  dest->bytes_recv = src->bytes_recv; +  dest->max_udp_payload_size = src->max_udp_payload_size; +} + +void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { +  dest->seq = src->seq; +  dest->cid = src->cid; + +  if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { +    dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; +    memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); +  } else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) { +    dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT; +  } +} + +int ngtcp2_dcid_verify_uniqueness(const 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) && +               (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && +               memcmp(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) == 0 +             ? 0 +             : NGTCP2_ERR_PROTO; +  } + +  return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; +} + +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, +                                             const uint8_t *token) { +  return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && +             ngtcp2_cmemeq(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) +           ? 0 +           : NGTCP2_ERR_INVALID_ARGUMENT; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_cid.h b/contrib/libs/ngtcp2/lib/ngtcp2_cid.h new file mode 100644 index 00000000000..6372ef113d6 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_cid.h @@ -0,0 +1,176 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CID_H +#define NGTCP2_CID_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pq.h" +#include "ngtcp2_path.h" + +/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_SCID_FLAG_NONE 0x00u +/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that +   a remote endpoint uses this particular Connection ID. */ +#define NGTCP2_SCID_FLAG_USED 0x01u +/* NGTCP2_SCID_FLAG_RETIRED indicates that this particular Connection +   ID is retired. */ +#define NGTCP2_SCID_FLAG_RETIRED 0x02u + +typedef struct ngtcp2_scid { +  ngtcp2_pq_entry pe; +  /* seq is the sequence number associated to the Connection ID. */ +  uint64_t seq; +  /* cid is a connection ID */ +  ngtcp2_cid cid; +  /* retired_ts is the timestamp when a remote endpoint tells that +     this Connection ID is retired. */ +  ngtcp2_tstamp retired_ts; +  /* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */ +  uint8_t flags; +} ngtcp2_scid; + +/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_DCID_FLAG_NONE 0x00u +/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path +   has been validated. */ +#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u +/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset +   token is set in token field. */ +#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u + +typedef struct ngtcp2_dcid { +  /* seq is the sequence number associated to the Connection ID. */ +  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; +  /* retired_ts is the timestamp when this Connection ID is +     retired. */ +  ngtcp2_tstamp retired_ts; +  /* bound_ts is the timestamp when this Connection ID is bound to a +     particular path.  It is only assigned when a Connection ID is +     used just for sending PATH_RESPONSE, and is not zero-length. */ +  ngtcp2_tstamp bound_ts; +  /* bytes_sent is the number of bytes sent to an associated path. */ +  uint64_t bytes_sent; +  /* bytes_recv is the number of bytes received from an associated +     path. */ +  uint64_t bytes_recv; +  /* max_udp_payload_size is the maximum size of UDP datagram payload +     that is allowed to be sent to this path. */ +  size_t max_udp_payload_size; +  /* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */ +  uint8_t flags; +  /* token is a stateless reset token received along with this +     Connection ID.  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_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. + */ +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid); + +/* + * 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 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_set_token sets |token| to |dcid|.  |token| must not be + * NULL, and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token); + +/* + * ngtcp2_dcid_set_path sets |path| to |dcid|.  It sets + * max_udp_payload_size to the minimum UDP datagram payload size + * supported by the IP protocol version. + */ +void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path); + +/* + * ngtcp2_dcid_copy copies |src| into |dest|. + */ +void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src); + +/* + * ngtcp2_dcid_copy_cid_token behaves like ngtcp2_dcid_copy, but it + * only copies cid, seq, and token. + */ +void ngtcp2_dcid_copy_cid_token(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(const ngtcp2_dcid *dcid, uint64_t seq, +                                  const ngtcp2_cid *cid, const uint8_t *token); + +/* + * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset + * token |token| against the one included in |dcid|.  Tokens are + * compared in constant time.  This function returns 0 if the + * verification succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + *     Tokens do not match; or |dcid| does not contain a token. + */ +int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, +                                             const uint8_t *token); + +#endif /* !defined(NGTCP2_CID_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conn.c b/contrib/libs/ngtcp2/lib/ngtcp2_conn.c new file mode 100644 index 00000000000..dff8d594595 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_conn.c @@ -0,0 +1,13763 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conn.h" + +#include <string.h> +#include <assert.h> + +#include "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" +#include "ngtcp2_unreachable.h" +#include "ngtcp2_net.h" +#include "ngtcp2_transport_params.h" +#include "ngtcp2_settings.h" +#include "ngtcp2_tstamp.h" +#include "ngtcp2_frame_chain.h" + +/* NGTCP2_FLOW_WINDOW_RTT_FACTOR is the factor of RTT when flow +   control window auto-tuning is triggered. */ +#define NGTCP2_FLOW_WINDOW_RTT_FACTOR 2 +/* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow +   control window. */ +#define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2 +/* NGTCP2_MIN_COALESCED_PAYLOADLEN is the minimum length of QUIC +   packet payload that should be coalesced to a long packet. */ +#define NGTCP2_MIN_COALESCED_PAYLOADLEN 128 + +ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent); + +/* + * 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 void conn_update_timestamp(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  assert(conn->log.last_ts <= ts); +  assert(conn->qlog.last_ts <= ts); + +  conn->log.last_ts = ts; +  conn->qlog.last_ts = ts; +} + +/* + * conn_is_tls_handshake_completed returns nonzero if TLS handshake + * has completed and 1 RTT keys are available. + */ +static int conn_is_tls_handshake_completed(ngtcp2_conn *conn) { +  return (conn->flags & NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED) && +         conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm; +} + +static int conn_call_recv_client_initial(ngtcp2_conn *conn, +                                         const ngtcp2_cid *dcid) { +  int rv; + +  assert(conn->callbacks.recv_client_initial); + +  rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_handshake_completed(ngtcp2_conn *conn) { +  int rv; + +  if (!conn->callbacks.handshake_completed) { +    return 0; +  } + +  rv = conn->callbacks.handshake_completed(conn, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, +                                      uint32_t flags, uint64_t offset, +                                      const uint8_t *data, size_t datalen) { +  int rv; + +  if (!conn->callbacks.recv_stream_data) { +    return 0; +  } + +  rv = conn->callbacks.recv_stream_data(conn, flags, strm->stream_id, offset, +                                        data, datalen, conn->user_data, +                                        strm->stream_user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_crypto_data(ngtcp2_conn *conn, +                                      ngtcp2_encryption_level encryption_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, encryption_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_VERSION_NEGOTIATION_FAILURE: +  case NGTCP2_ERR_NOMEM: +  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) { +  int rv; +  uint32_t flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; + +  if (!conn->callbacks.stream_close) { +    return 0; +  } + +  if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { +    flags |= NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET; +  } + +  rv = conn->callbacks.stream_close(conn, flags, strm->stream_id, +                                    strm->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_pv *pv, +                                     ngtcp2_path_validation_result res) { +  int rv; +  uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; +  const ngtcp2_path *old_path = NULL; + +  if (!conn->callbacks.path_validation) { +    return 0; +  } + +  if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) { +    flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; +  } + +  if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { +    old_path = &pv->fallback_dcid.ps.path; +  } + +  if (conn->server && old_path && +      (ngtcp2_addr_compare(&pv->dcid.ps.path.remote, &old_path->remote) & +       (NGTCP2_ADDR_COMPARE_FLAG_ADDR | NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { +    flags |= NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN; +  } + +  rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, old_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_path *dest) { +  int rv; + +  if (!conn->callbacks.select_preferred_addr) { +    return 0; +  } + +  assert(conn->remote.transport_params); +  assert(conn->remote.transport_params->preferred_addr_present); + +  rv = conn->callbacks.select_preferred_addr( +    conn, dest, &conn->remote.transport_params->preferred_addr, +    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, type, dcid->seq, &dcid->cid, +    (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL, +    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 conn_call_stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id, +                                         uint64_t app_error_code, +                                         void *stream_user_data) { +  int rv; + +  if (!conn->callbacks.stream_stop_sending) { +    return 0; +  } + +  rv = conn->callbacks.stream_stop_sending(conn, stream_id, app_error_code, +                                           conn->user_data, stream_user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn, +                                             ngtcp2_crypto_aead_ctx *aead_ctx) { +  if (!aead_ctx->native_handle) { +    return; +  } + +  assert(conn->callbacks.delete_crypto_aead_ctx); + +  conn->callbacks.delete_crypto_aead_ctx(conn, aead_ctx, conn->user_data); +} + +static void +conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn, +                                   ngtcp2_crypto_cipher_ctx *cipher_ctx) { +  if (!cipher_ctx->native_handle) { +    return; +  } + +  assert(conn->callbacks.delete_crypto_cipher_ctx); + +  conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data); +} + +static int conn_call_client_initial(ngtcp2_conn *conn) { +  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 0; +} + +static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) { +  int rv; + +  assert(conn->callbacks.get_path_challenge_data); + +  rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_version_negotiation(ngtcp2_conn *conn, +                                              const ngtcp2_pkt_hd *hd, +                                              const uint32_t *sv, size_t nsv) { +  int rv; + +  if (!conn->callbacks.recv_version_negotiation) { +    return 0; +  } + +  rv = conn->callbacks.recv_version_negotiation(conn, hd, sv, nsv, +                                                conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { +  int rv; + +  assert(conn->callbacks.recv_retry); + +  rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int +conn_call_recv_stateless_reset(ngtcp2_conn *conn, +                               const ngtcp2_pkt_stateless_reset *sr) { +  int rv; + +  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; +} + +static int conn_call_recv_new_token(ngtcp2_conn *conn, const uint8_t *token, +                                    size_t tokenlen) { +  int rv; + +  if (!conn->callbacks.recv_new_token) { +    return 0; +  } + +  rv = conn->callbacks.recv_new_token(conn, token, tokenlen, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_handshake_confirmed(ngtcp2_conn *conn) { +  int rv; + +  if (!conn->callbacks.handshake_confirmed) { +    return 0; +  } + +  rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_datagram(ngtcp2_conn *conn, +                                   const ngtcp2_datagram *fr) { +  const uint8_t *data; +  size_t datalen; +  int rv; +  uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; + +  if (!conn->callbacks.recv_datagram) { +    return 0; +  } + +  if (fr->datacnt) { +    assert(fr->datacnt == 1); + +    data = fr->data->base; +    datalen = fr->data->len; +  } else { +    data = NULL; +    datalen = 0; +  } + +  if (!conn_is_tls_handshake_completed(conn)) { +    flags |= NGTCP2_DATAGRAM_FLAG_0RTT; +  } + +  rv = +    conn->callbacks.recv_datagram(conn, flags, data, datalen, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int +conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +                     ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, +                     ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, +                     const uint8_t *current_rx_secret, +                     const uint8_t *current_tx_secret, size_t secretlen) { +  int rv; + +  assert(conn->callbacks.update_key); + +  rv = conn->callbacks.update_key( +    conn, rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx, tx_iv, +    current_rx_secret, current_tx_secret, secretlen, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version, +                                         const ngtcp2_cid *dcid) { +  int rv; + +  assert(conn->callbacks.version_negotiation); + +  rv = +    conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_rx_key(ngtcp2_conn *conn, +                                 ngtcp2_encryption_level level) { +  int rv; + +  if (!conn->callbacks.recv_rx_key) { +    return 0; +  } + +  rv = conn->callbacks.recv_rx_key(conn, level, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static int conn_call_recv_tx_key(ngtcp2_conn *conn, +                                 ngtcp2_encryption_level level) { +  int rv; + +  if (!conn->callbacks.recv_tx_key) { +    return 0; +  } + +  rv = conn->callbacks.recv_tx_key(conn, level, conn->user_data); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, +                       ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num, +                       ngtcp2_log *log, ngtcp2_qlog *qlog, +                       ngtcp2_objalloc *rtb_entry_objalloc, +                       ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { +  memset(pktns, 0, sizeof(*pktns)); + +  ngtcp2_gaptr_init(&pktns->rx.pngap, mem); + +  pktns->tx.last_pkt_num = initial_pkt_num - 1; +  pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; +  pktns->rx.max_pkt_num = -1; +  pktns->rx.max_ack_eliciting_pkt_num = -1; +  pktns->id = pktns_id; + +  ngtcp2_acktr_init(&pktns->acktr, log, mem); + +  ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, +                   frc_objalloc, mem); + +  ngtcp2_rtb_init(&pktns->rtb, rst, cc, initial_pkt_num, log, qlog, +                  rtb_entry_objalloc, frc_objalloc, mem); +} + +static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, +                     ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num, +                     ngtcp2_log *log, ngtcp2_qlog *qlog, +                     ngtcp2_objalloc *rtb_entry_objalloc, +                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { +  *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); +  if (*ppktns == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  pktns_init(*ppktns, pktns_id, rst, cc, initial_pkt_num, log, qlog, +             rtb_entry_objalloc, frc_objalloc, mem); + +  return 0; +} + +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 delete_buf_chain(ngtcp2_buf_chain *bufchain, +                             const ngtcp2_mem *mem) { +  ngtcp2_buf_chain *next; + +  for (; bufchain;) { +    next = bufchain->next; +    ngtcp2_buf_chain_del(bufchain, mem); +    bufchain = next; +  } +} + +static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { +  delete_buf_chain(pktns->crypto.tx.data, mem); + +  delete_buffed_pkts(pktns->rx.buffed_pkts, mem); + +  ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc, +                                       mem); + +  ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); +  ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); + +  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, rhs); +} + +static int retired_ts_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->retired_ts < b->retired_ts; +} + +/* + * conn_reset_conn_stat_cc resets congestion state in |cstat|. + */ +static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, +                                    ngtcp2_conn_stat *cstat) { +  cstat->latest_rtt = 0; +  cstat->min_rtt = UINT64_MAX; +  cstat->smoothed_rtt = conn->local.settings.initial_rtt; +  cstat->rttvar = conn->local.settings.initial_rtt / 2; +  cstat->first_rtt_sample_ts = UINT64_MAX; +  cstat->pto_count = 0; +  cstat->loss_detection_timer = UINT64_MAX; +  cstat->max_tx_udp_payload_size = +    ngtcp2_conn_get_path_max_tx_udp_payload_size(conn); +  cstat->cwnd = ngtcp2_cc_compute_initcwnd(cstat->max_tx_udp_payload_size); +  cstat->ssthresh = UINT64_MAX; +  cstat->congestion_recovery_start_ts = UINT64_MAX; +  cstat->bytes_in_flight = 0; +  cstat->delivery_rate_sec = 0; +  cstat->pacing_interval = 0; +  cstat->send_quantum = 64 * 1024; +} + +/* + * reset_conn_stat_recovery resets the fields related to the recovery + * function + */ +static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) { +  /* Initializes them with UINT64_MAX. */ +  memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time)); +  memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts)); +} + +/* + * conn_reset_conn_stat resets |cstat|.  The following fields are not + * reset: initial_rtt and max_udp_payload_size. + */ +static void conn_reset_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { +  conn_reset_conn_stat_cc(conn, cstat); +  reset_conn_stat_recovery(cstat); +} + +static void 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)); +  } +} + +/* + * compute_pto computes PTO. + */ +static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt, +                                   ngtcp2_duration rttvar, +                                   ngtcp2_duration max_ack_delay) { +  ngtcp2_duration var = ngtcp2_max_uint64(4 * rttvar, NGTCP2_GRANULARITY); +  return smoothed_rtt + var + max_ack_delay; +} + +/* + * conn_compute_initial_pto computes PTO using the initial RTT. + */ +static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, +                                                ngtcp2_pktns *pktns) { +  ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt; +  ngtcp2_duration max_ack_delay; + +  if (pktns->id == NGTCP2_PKTNS_ID_APPLICATION && +      conn->remote.transport_params) { +    max_ack_delay = conn->remote.transport_params->max_ack_delay; +  } else { +    max_ack_delay = 0; +  } +  return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay); +} + +/* + * conn_compute_pto computes the current PTO. + */ +static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn, +                                        ngtcp2_pktns *pktns) { +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_duration max_ack_delay; + +  if (pktns->id == NGTCP2_PKTNS_ID_APPLICATION && +      conn->remote.transport_params) { +    max_ack_delay = conn->remote.transport_params->max_ack_delay; +  } else { +    max_ack_delay = 0; +  } +  return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay); +} + +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, +                                        ngtcp2_pktns *pktns) { +  return conn_compute_pto(conn, pktns); +} + +/* + * conn_compute_pv_timeout_pto returns path validation timeout using + * the given |pto|. + */ +static ngtcp2_duration conn_compute_pv_timeout_pto(ngtcp2_conn *conn, +                                                   ngtcp2_duration pto) { +  ngtcp2_duration initial_pto = conn_compute_initial_pto(conn, &conn->pktns); + +  return 3 * ngtcp2_max_uint64(pto, initial_pto); +} + +/* + * conn_compute_pv_timeout returns path validation timeout. + */ +static ngtcp2_duration conn_compute_pv_timeout(ngtcp2_conn *conn) { +  return conn_compute_pv_timeout_pto(conn, +                                     conn_compute_pto(conn, &conn->pktns)); +} + +static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, +                               uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns, +                               const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) { +  assert(pi); + +  if (pi->ecn != NGTCP2_ECN_NOT_ECT) { +    /* We have already made a transition of validation state and +      deceided to send UDP datagram with ECN bit set.  Coalesced QUIC +      packets also bear ECN bits set. */ +    if (pktns->tx.ecn.start_pkt_num == INT64_MAX) { +      pktns->tx.ecn.start_pkt_num = hd->pkt_num; +    } + +    ++pktns->tx.ecn.validation_pkt_sent; + +    if (prtb_entry_flags) { +      *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN; +    } + +    ++pktns->tx.ecn.ect0; + +    return; +  } + +  switch (conn->tx.ecn.state) { +  case NGTCP2_ECN_STATE_TESTING: +    if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { +      assert(0 == pktns->tx.ecn.validation_pkt_sent); +      assert(0 == pktns->tx.ecn.validation_pkt_lost); + +      conn->tx.ecn.validation_start_ts = ts; +    } else if (ts - conn->tx.ecn.validation_start_ts >= +               3 * conn_compute_pto(conn, pktns)) { +      conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; +      break; +    } + +    if (pktns->tx.ecn.start_pkt_num == INT64_MAX) { +      pktns->tx.ecn.start_pkt_num = hd->pkt_num; +    } + +    ++pktns->tx.ecn.validation_pkt_sent; + +    if (++conn->tx.ecn.dgram_sent == NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS) { +      conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; +    } +    /* fall through */ +  case NGTCP2_ECN_STATE_CAPABLE: +    /* pi is provided per UDP datagram. */ +    assert(NGTCP2_ECN_NOT_ECT == pi->ecn); + +    pi->ecn = NGTCP2_ECN_ECT_0; + +    if (prtb_entry_flags) { +      *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN; +    } + +    ++pktns->tx.ecn.ect0; +    break; +  case NGTCP2_ECN_STATE_UNKNOWN: +  case NGTCP2_ECN_STATE_FAILED: +    break; +  default: +    ngtcp2_unreachable(); +  } +} + +static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) { +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_pktns *hs_pktns = conn->hs_pktns; +  ngtcp2_pktns *pktns = &conn->pktns; + +  conn->tx.ecn.state = NGTCP2_ECN_STATE_TESTING; +  conn->tx.ecn.validation_start_ts = UINT64_MAX; +  conn->tx.ecn.dgram_sent = 0; + +  if (in_pktns) { +    in_pktns->tx.ecn.start_pkt_num = INT64_MAX; +    in_pktns->tx.ecn.validation_pkt_sent = 0; +    in_pktns->tx.ecn.validation_pkt_lost = 0; +  } + +  if (hs_pktns) { +    hs_pktns->tx.ecn.start_pkt_num = INT64_MAX; +    hs_pktns->tx.ecn.validation_pkt_sent = 0; +    hs_pktns->tx.ecn.validation_pkt_lost = 0; +  } + +  pktns->tx.ecn.start_pkt_num = INT64_MAX; +  pktns->tx.ecn.validation_pkt_sent = 0; +  pktns->tx.ecn.validation_pkt_lost = 0; +} + +/* server_default_available_versions is the default available_versions +   field sent by server. */ +static uint8_t server_default_available_versions[] = {0, 0, 0, 1}; + +/* + * available_versions_init writes |versions| of length |versionslen| + * in network byte order to the buffer pointed by |buf|, suitable for + * sending in available_versions field of version_information QUIC + * transport parameter.  This function returns the pointer to the one + * beyond the last byte written. + */ +static void *available_versions_init(void *buf, const uint32_t *versions, +                                     size_t versionslen) { +  size_t i; + +  for (i = 0; i < versionslen; ++i) { +    buf = ngtcp2_put_uint32be(buf, versions[i]); +  } + +  return 0; +} + +static void +conn_set_local_transport_params(ngtcp2_conn *conn, +                                const ngtcp2_transport_params *params) { +  ngtcp2_transport_params *p = &conn->local.transport_params; +  uint32_t chosen_version = p->version_info.chosen_version; + +  *p = *params; + +  if (conn->server) { +    p->version_info.chosen_version = chosen_version; +  } else { +    p->version_info.chosen_version = conn->client_chosen_version; +  } +  p->version_info.available_versions = conn->vneg.available_versions; +  p->version_info.available_versionslen = conn->vneg.available_versionslen; +  p->version_info_present = 1; +} + +static size_t buflen_align(size_t buflen) { +  return (buflen + 0x7) & (size_t)~0x7; +} + +static void *buf_align(void *buf) { +  return (void *)((uintptr_t)((uint8_t *)buf + 0x7) & (uintptr_t)~0x7); +} + +static void *buf_advance(void *buf, size_t n) { return (uint8_t *)buf + n; } + +static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, +                    const ngtcp2_cid *scid, const ngtcp2_path *path, +                    uint32_t client_chosen_version, int callbacks_version, +                    const ngtcp2_callbacks *callbacks, int settings_version, +                    const ngtcp2_settings *settings, +                    int transport_params_version, +                    const ngtcp2_transport_params *params, +                    const ngtcp2_mem *mem, void *user_data, int server) { +  int rv; +  ngtcp2_scid *scident; +  void *buf, *tokenbuf; +  size_t buflen; +  uint8_t fixed_bit_byte; +  size_t i; +  uint32_t *preferred_versions; +  ngtcp2_settings settingsbuf; +  ngtcp2_transport_params paramsbuf; +  (void)callbacks_version; +  (void)settings_version; + +  settings = +    ngtcp2_settings_convert_to_latest(&settingsbuf, settings_version, settings); +  params = ngtcp2_transport_params_convert_to_latest( +    ¶msbuf, transport_params_version, params); + +  assert(settings->max_window <= NGTCP2_MAX_VARINT); +  assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); +  assert(settings->max_tx_udp_payload_size); +  assert(settings->max_tx_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE); +  assert(settings->initial_pkt_num <= INT32_MAX); +  assert(params->active_connection_id_limit >= +         NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT); +  assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); +  assert(params->initial_max_data <= NGTCP2_MAX_VARINT); +  assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT); +  assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT); +  assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT); +  assert((server && params->original_dcid_present) || +         (!server && !params->original_dcid_present)); +  assert(!params->initial_scid_present); +  assert(server || !params->stateless_reset_token_present); +  assert(server || !params->preferred_addr_present); +  assert(server || !params->retry_scid_present); +  assert(params->max_idle_timeout != UINT64_MAX); +  assert(params->max_ack_delay < (1 << 14) * NGTCP2_MILLISECONDS); +  assert(server || callbacks->client_initial); +  assert(!server || callbacks->recv_client_initial); +  assert(callbacks->recv_crypto_data); +  assert(callbacks->encrypt); +  assert(callbacks->decrypt); +  assert(callbacks->hp_mask); +  assert(server || callbacks->recv_retry); +  assert(callbacks->rand); +  assert(callbacks->get_new_connection_id); +  assert(callbacks->update_key); +  assert(callbacks->delete_crypto_aead_ctx); +  assert(callbacks->delete_crypto_cipher_ctx); +  assert(callbacks->get_path_challenge_data); +  assert(!server || !ngtcp2_is_reserved_version(client_chosen_version)); + +  for (i = 0; i < settings->pmtud_probeslen; ++i) { +    assert(settings->pmtud_probes[i] > NGTCP2_MAX_UDP_PAYLOAD_SIZE); +  } + +  if (mem == NULL) { +    mem = ngtcp2_mem_default(); +  } + +  buflen = sizeof(ngtcp2_conn); +  if (settings->qlog_write) { +    buflen = buflen_align(buflen); +    buflen += NGTCP2_QLOG_BUFLEN; +  } + +  if (settings->pmtud_probeslen) { +    buflen = buflen_align(buflen); +    buflen += sizeof(settings->pmtud_probes[0]) * settings->pmtud_probeslen; +  } + +  if (settings->preferred_versionslen) { +    buflen = buflen_align(buflen); +    buflen += +      sizeof(settings->preferred_versions[0]) * settings->preferred_versionslen; +  } + +  if (settings->available_versionslen) { +    buflen = buflen_align(buflen); +    buflen += +      sizeof(settings->available_versions[0]) * settings->available_versionslen; +  } else if (server) { +    if (settings->preferred_versionslen) { +      buflen = buflen_align(buflen); +      buflen += sizeof(settings->preferred_versions[0]) * +                settings->preferred_versionslen; +    } +  } else if (!ngtcp2_is_reserved_version(client_chosen_version)) { +    buflen = buflen_align(buflen); +    buflen += sizeof(client_chosen_version); +  } + +  buf = ngtcp2_mem_calloc(mem, 1, buflen); +  if (buf == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  *pconn = buf; +  buf = buf_advance(buf, sizeof(ngtcp2_conn)); + +  (*pconn)->server = server; + +  ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 16, mem); +  ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 16, mem); +  ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 16, mem); + +  ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound); + +  ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused); + +  ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired); + +  ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); + +  ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); + +  ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); + +  ngtcp2_map_init(&(*pconn)->strms, mem); + +  ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); + +  ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, mem); + +  ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, mem); + +  ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge); + +  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 = buf_align(buf); +    ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); +    buf = buf_advance(buf, NGTCP2_QLOG_BUFLEN); +  } + +  (*pconn)->local.settings = *settings; + +  if (settings->tokenlen) { +    tokenbuf = ngtcp2_mem_malloc(mem, settings->tokenlen); +    if (tokenbuf == NULL) { +      rv = NGTCP2_ERR_NOMEM; +      goto fail_token; +    } +    memcpy(tokenbuf, settings->token, settings->tokenlen); +    (*pconn)->local.settings.token = tokenbuf; +  } else { +    (*pconn)->local.settings.token = NULL; +  } + +  if (settings->pmtud_probeslen) { +    (*pconn)->local.settings.pmtud_probes = buf_align(buf); +    buf = ngtcp2_cpymem( +      (uint16_t *)(*pconn)->local.settings.pmtud_probes, settings->pmtud_probes, +      sizeof(settings->pmtud_probes[0]) * settings->pmtud_probeslen); +  } + +  if (!(*pconn)->local.settings.original_version) { +    (*pconn)->local.settings.original_version = client_chosen_version; +  } + +  ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); +  ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path); + +  rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); +  if (rv != 0) { +    goto fail_seqgap_push; +  } + +  conn_reset_conn_stat(*pconn, &(*pconn)->cstat); +  (*pconn)->cstat.initial_rtt = settings->initial_rtt; + +  ngtcp2_rst_init(&(*pconn)->rst); + +  (*pconn)->cc_algo = settings->cc_algo; + +  switch (settings->cc_algo) { +  case NGTCP2_CC_ALGO_RENO: +    ngtcp2_cc_reno_init(&(*pconn)->reno, &(*pconn)->log); + +    break; +  case NGTCP2_CC_ALGO_CUBIC: +    ngtcp2_cc_cubic_init(&(*pconn)->cubic, &(*pconn)->log, &(*pconn)->rst); + +    break; +  case NGTCP2_CC_ALGO_BBR: +    ngtcp2_cc_bbr_init(&(*pconn)->bbr, &(*pconn)->log, &(*pconn)->cstat, +                       &(*pconn)->rst, settings->initial_ts, callbacks->rand, +                       &settings->rand_ctx); + +    break; +  default: +    ngtcp2_unreachable(); +  } + +  rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, +                 &(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log, +                 &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, +                 &(*pconn)->frc_objalloc, mem); +  if (rv != 0) { +    goto fail_in_pktns_init; +  } + +  rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, +                 &(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log, +                 &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, +                 &(*pconn)->frc_objalloc, mem); +  if (rv != 0) { +    goto fail_hs_pktns_init; +  } + +  pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst, +             &(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log, +             &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, +             &(*pconn)->frc_objalloc, mem); + +  scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); +  if (scident == NULL) { +    rv = NGTCP2_ERR_NOMEM; +    goto fail_scident; +  } + +  /* Set stateless reset token later if it is available in the local +     transport parameters */ +  ngtcp2_scid_init(scident, 0, scid); + +  rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident); +  if (rv != 0) { +    goto fail_scid_set_insert; +  } + +  scident = NULL; + +  if (settings->preferred_versionslen) { +    if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { +      for (i = 0; i < settings->preferred_versionslen; ++i) { +        if (settings->preferred_versions[i] == client_chosen_version) { +          break; +        } +      } + +      assert(i < settings->preferred_versionslen); +    } + +    preferred_versions = buf_align(buf); +    buf = buf_advance(preferred_versions, sizeof(preferred_versions[0]) * +                                            settings->preferred_versionslen); + +    for (i = 0; i < settings->preferred_versionslen; ++i) { +      assert(ngtcp2_is_supported_version(settings->preferred_versions[i])); + +      preferred_versions[i] = settings->preferred_versions[i]; +    } + +    (*pconn)->vneg.preferred_versions = preferred_versions; +    (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen; +  } + +  (*pconn)->local.settings.preferred_versions = NULL; +  (*pconn)->local.settings.preferred_versionslen = 0; + +  if (settings->available_versionslen) { +    if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) { +      for (i = 0; i < settings->available_versionslen; ++i) { +        if (settings->available_versions[i] == client_chosen_version) { +          break; +        } +      } + +      assert(i < settings->available_versionslen); +    } + +    for (i = 0; i < settings->available_versionslen; ++i) { +      assert(ngtcp2_is_reserved_version(settings->available_versions[i]) || +             ngtcp2_is_supported_version(settings->available_versions[i])); +    } + +    (*pconn)->vneg.available_versions = buf_align(buf); +    (*pconn)->vneg.available_versionslen = +      sizeof(uint32_t) * settings->available_versionslen; + +    buf = available_versions_init((*pconn)->vneg.available_versions, +                                  settings->available_versions, +                                  settings->available_versionslen); +  } else if (server) { +    if (settings->preferred_versionslen) { +      (*pconn)->vneg.available_versions = buf_align(buf); +      (*pconn)->vneg.available_versionslen = +        sizeof(uint32_t) * settings->preferred_versionslen; + +      buf = available_versions_init((*pconn)->vneg.available_versions, +                                    settings->preferred_versions, +                                    settings->preferred_versionslen); +    } else { +      (*pconn)->vneg.available_versions = server_default_available_versions; +      (*pconn)->vneg.available_versionslen = +        sizeof(server_default_available_versions); +    } +  } else if (!ngtcp2_is_reserved_version(client_chosen_version)) { +    (*pconn)->vneg.available_versions = buf_align(buf); +    (*pconn)->vneg.available_versionslen = sizeof(uint32_t); + +    buf = available_versions_init((*pconn)->vneg.available_versions, +                                  &client_chosen_version, 1); +  } + +  (*pconn)->local.settings.available_versions = NULL; +  (*pconn)->local.settings.available_versionslen = 0; + +  (*pconn)->client_chosen_version = client_chosen_version; + +  conn_set_local_transport_params(*pconn, params); + +  callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx); +  if (fixed_bit_byte & 1) { +    (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT; +  } + +  (*pconn)->keep_alive.last_ts = UINT64_MAX; +  (*pconn)->keep_alive.timeout = UINT64_MAX; + +  (*pconn)->oscid = *scid; +  (*pconn)->callbacks = *callbacks; +  (*pconn)->mem = mem; +  (*pconn)->user_data = user_data; +  (*pconn)->idle_ts = settings->initial_ts; +  (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; +  (*pconn)->tx.last_max_data_ts = UINT64_MAX; +  (*pconn)->tx.pacing.next_ts = UINT64_MAX; +  (*pconn)->tx.last_blocked_offset = UINT64_MAX; +  (*pconn)->early.discard_started_ts = UINT64_MAX; + +  conn_reset_ecn_validation_state(*pconn); + +  ngtcp2_qlog_start(&(*pconn)->qlog, +                    server +                      ? ((*pconn)->local.transport_params.retry_scid_present +                           ? &(*pconn)->local.transport_params.retry_scid +                           : &(*pconn)->local.transport_params.original_dcid) +                      : dcid, +                    server); + +  return 0; + +fail_scid_set_insert: +  ngtcp2_mem_free(mem, scident); +fail_scident: +  pktns_del((*pconn)->hs_pktns, mem); +fail_hs_pktns_init: +  pktns_del((*pconn)->in_pktns, mem); +fail_in_pktns_init: +  ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); +fail_seqgap_push: +  ngtcp2_mem_free(mem, (uint8_t *)(*pconn)->local.settings.token); +fail_token: +  ngtcp2_mem_free(mem, *pconn); + +  return rv; +} + +int ngtcp2_conn_client_new_versioned( +  ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, +  const ngtcp2_path *path, uint32_t client_chosen_version, +  int callbacks_version, const ngtcp2_callbacks *callbacks, +  int settings_version, const ngtcp2_settings *settings, +  int transport_params_version, const ngtcp2_transport_params *params, +  const ngtcp2_mem *mem, void *user_data) { +  int rv; + +  rv = conn_new(pconn, dcid, scid, path, client_chosen_version, +                callbacks_version, callbacks, settings_version, settings, +                transport_params_version, params, mem, user_data, 0); +  if (rv != 0) { +    return rv; +  } +  (*pconn)->rcid = *dcid; +  (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; +  (*pconn)->local.bidi.next_stream_id = 0; +  (*pconn)->local.uni.next_stream_id = 2; + +  rv = ngtcp2_conn_commit_local_transport_params(*pconn); +  if (rv != 0) { +    ngtcp2_conn_del(*pconn); +    return rv; +  } + +  return 0; +} + +int ngtcp2_conn_server_new_versioned( +  ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, +  const ngtcp2_path *path, uint32_t client_chosen_version, +  int callbacks_version, const ngtcp2_callbacks *callbacks, +  int settings_version, const ngtcp2_settings *settings, +  int transport_params_version, const ngtcp2_transport_params *params, +  const ngtcp2_mem *mem, void *user_data) { +  int rv; + +  rv = conn_new(pconn, dcid, scid, path, client_chosen_version, +                callbacks_version, callbacks, settings_version, settings, +                transport_params_version, params, mem, user_data, 1); +  if (rv != 0) { +    return rv; +  } + +  (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; +  (*pconn)->local.bidi.next_stream_id = 1; +  (*pconn)->local.uni.next_stream_id = 3; + +  if ((*pconn)->local.settings.tokenlen) { +    /* Usage of token lifts amplification limit */ +    (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; +  } + +  return 0; +} + +/* + * conn_fc_credits returns the number of bytes allowed to be sent to + * the given stream.  Both connection and stream level flow control + * credits are considered. + */ +static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { +  return ngtcp2_min_uint64(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 uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, +                                          uint64_t len) { +  uint64_t fc_credits = conn_fc_credits(conn, strm); +  return ngtcp2_min_uint64(len, fc_credits); +} + +static int delete_strms_each(void *data, void *ptr) { +  ngtcp2_conn *conn = ptr; +  ngtcp2_strm *s = data; + +  ngtcp2_strm_free(s); +  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); + +  return 0; +} + +static void conn_vneg_crypto_free(ngtcp2_conn *conn) { +  if (conn->vneg.rx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); +  } +  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); + +  if (conn->vneg.tx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); +  } +  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); + +  ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); +  ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); +} + +void ngtcp2_conn_del(ngtcp2_conn *conn) { +  if (conn == NULL) { +    return; +  } + +  ngtcp2_qlog_end(&conn->qlog); + +  if (conn->early.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx); +  } +  conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx); + +  if (conn->crypto.key_update.old_rx_ckm) { +    conn_call_delete_crypto_aead_ctx( +      conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); +  } +  if (conn->crypto.key_update.new_rx_ckm) { +    conn_call_delete_crypto_aead_ctx( +      conn, &conn->crypto.key_update.new_rx_ckm->aead_ctx); +  } +  if (conn->crypto.key_update.new_tx_ckm) { +    conn_call_delete_crypto_aead_ctx( +      conn, &conn->crypto.key_update.new_tx_ckm->aead_ctx); +  } + +  if (conn->pktns.crypto.rx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, +                                     &conn->pktns.crypto.rx.ckm->aead_ctx); +  } +  conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.rx.hp_ctx); + +  if (conn->pktns.crypto.tx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, +                                     &conn->pktns.crypto.tx.ckm->aead_ctx); +  } +  conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.tx.hp_ctx); + +  if (conn->hs_pktns) { +    if (conn->hs_pktns->crypto.rx.ckm) { +      conn_call_delete_crypto_aead_ctx( +        conn, &conn->hs_pktns->crypto.rx.ckm->aead_ctx); +    } +    conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.rx.hp_ctx); + +    if (conn->hs_pktns->crypto.tx.ckm) { +      conn_call_delete_crypto_aead_ctx( +        conn, &conn->hs_pktns->crypto.tx.ckm->aead_ctx); +    } +    conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.tx.hp_ctx); +  } +  if (conn->in_pktns) { +    if (conn->in_pktns->crypto.rx.ckm) { +      conn_call_delete_crypto_aead_ctx( +        conn, &conn->in_pktns->crypto.rx.ckm->aead_ctx); +    } +    conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.rx.hp_ctx); + +    if (conn->in_pktns->crypto.tx.ckm) { +      conn_call_delete_crypto_aead_ctx( +        conn, &conn->in_pktns->crypto.tx.ckm->aead_ctx); +    } +    conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.tx.hp_ctx); +  } + +  conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); + +  ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); +  ngtcp2_transport_params_del(conn->remote.pending_transport_params, conn->mem); + +  conn_vneg_crypto_free(conn); + +  ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); +  ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base); +  ngtcp2_mem_free(conn->mem, (uint8_t *)conn->local.settings.token); + +  ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); +  ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem); +  ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem); +  ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + +  pktns_free(&conn->pktns, conn->mem); +  pktns_del(conn->hs_pktns, conn->mem); +  pktns_del(conn->in_pktns, conn->mem); + +  ngtcp2_pmtud_del(conn->pmtud); +  ngtcp2_pv_del(conn->pv); + +  ngtcp2_mem_free(conn->mem, (uint8_t *)conn->rx.ccerr.reason); + +  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(&conn->strms, delete_strms_each, (void *)conn); +  ngtcp2_map_free(&conn->strms); + +  ngtcp2_pq_free(&conn->scid.used); +  delete_scid(&conn->scid.set, conn->mem); +  ngtcp2_ksl_free(&conn->scid.set); +  ngtcp2_gaptr_free(&conn->dcid.seqgap); + +  ngtcp2_objalloc_free(&conn->strm_objalloc); +  ngtcp2_objalloc_free(&conn->rtb_entry_objalloc); +  ngtcp2_objalloc_free(&conn->frc_objalloc); + +  ngtcp2_mem_free(conn->mem, conn); +} + +/* + * conn_ensure_ack_ranges makes sure that conn->tx.ack->ack.ranges can + * contain at least |n| additional ngtcp2_ack_range. + * + * 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_ranges(ngtcp2_conn *conn, size_t n) { +  ngtcp2_frame *fr; +  size_t max = conn->tx.max_ack_ranges; + +  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_range) * max); +  if (fr == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  conn->tx.ack = fr; +  conn->tx.max_ack_ranges = max; + +  return 0; +} + +/* + * conn_compute_ack_delay computes ACK delay for outgoing protected + * ACK. + */ +static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) { +  return ngtcp2_min_uint64(conn->local.transport_params.max_ack_delay, +                           conn->cstat.smoothed_rtt / 8); +} + +int ngtcp2_conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, +                                 ngtcp2_pktns *pktns, uint8_t type, +                                 ngtcp2_tstamp ts, ngtcp2_duration ack_delay, +                                 uint64_t ack_delay_exponent) { +  /* TODO Measure an actual size of ACK blocks to find the best +     default value. */ +  const size_t initial_max_ack_ranges = 8; +  int64_t last_pkt_num; +  ngtcp2_acktr *acktr = &pktns->acktr; +  ngtcp2_ack_range *range; +  ngtcp2_ksl_it it; +  ngtcp2_acktr_entry *rpkt; +  ngtcp2_ack *ack; +  size_t range_idx; +  ngtcp2_tstamp largest_ack_ts; +  int rv; + +  if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) { +    ack_delay = 0; +  } + +  if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) { +    return 0; +  } + +  it = ngtcp2_acktr_get(acktr); +  if (ngtcp2_ksl_it_end(&it)) { +    ngtcp2_acktr_commit_ack(acktr); +    return 0; +  } + +  if (conn->tx.ack == NULL) { +    conn->tx.ack = ngtcp2_mem_malloc( +      conn->mem, +      sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_range) * initial_max_ack_ranges); +    if (conn->tx.ack == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +    conn->tx.max_ack_ranges = initial_max_ack_ranges; +  } + +  ack = &conn->tx.ack->ack; + +  if (pktns->rx.ecn.ect0 || pktns->rx.ecn.ect1 || pktns->rx.ecn.ce) { +    ack->type = NGTCP2_FRAME_ACK_ECN; +    ack->ecn.ect0 = pktns->rx.ecn.ect0; +    ack->ecn.ect1 = pktns->rx.ecn.ect1; +    ack->ecn.ce = pktns->rx.ecn.ce; +  } else { +    ack->type = NGTCP2_FRAME_ACK; +  } +  ack->rangecnt = 0; + +  rpkt = ngtcp2_ksl_it_get(&it); + +  if (rpkt->pkt_num == pktns->rx.max_pkt_num) { +    last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); +    largest_ack_ts = rpkt->tstamp; +    ack->largest_ack = rpkt->pkt_num; +    ack->first_ack_range = rpkt->len - 1; + +    ngtcp2_ksl_it_next(&it); +  } else if (rpkt->pkt_num + 1 == pktns->rx.max_pkt_num) { +    last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); +    largest_ack_ts = pktns->rx.max_pkt_ts; +    ack->largest_ack = pktns->rx.max_pkt_num; +    ack->first_ack_range = rpkt->len; + +    ngtcp2_ksl_it_next(&it); +  } else { +    assert(rpkt->pkt_num < pktns->rx.max_pkt_num); + +    last_pkt_num = pktns->rx.max_pkt_num; +    largest_ack_ts = pktns->rx.max_pkt_ts; +    ack->largest_ack = pktns->rx.max_pkt_num; +    ack->first_ack_range = 0; +  } + +  if (type == NGTCP2_PKT_1RTT) { +    ack->ack_delay_unscaled = ts - largest_ack_ts; +    ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / +                     (1ULL << ack_delay_exponent); +  } else { +    ack->ack_delay_unscaled = 0; +    ack->ack_delay = 0; +  } + +  for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { +    if (ack->rangecnt == NGTCP2_MAX_ACK_RANGES) { +      break; +    } + +    rpkt = ngtcp2_ksl_it_get(&it); + +    range_idx = ack->rangecnt++; +    rv = conn_ensure_ack_ranges(conn, ack->rangecnt); +    if (rv != 0) { +      return rv; +    } +    ack = &conn->tx.ack->ack; +    range = &ack->ranges[range_idx]; +    range->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); +    range->len = 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); +  } + +  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_pktns *pktns, +                            ngtcp2_rtb_entry *ent) { +  ngtcp2_rtb *rtb = &pktns->rtb; +  int rv; + +  /* This function implements OnPacketSent, but it handles only +     non-ACK-only packet. */ +  rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat); +  if (rv != 0) { +    return rv; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +    conn->cstat.last_tx_pkt_ts[pktns->id] = ent->ts; +  } + +  ngtcp2_conn_set_loss_detection_timer(conn, ent->ts); + +  return 0; +} + +/* + * pktns_select_pkt_numlen selects shortest packet number encoding for + * the next packet number based on the largest acknowledged packet + * number.  It returns the number of bytes to encode the packet + * number. + */ +static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { +  int64_t pkt_num = pktns->tx.last_pkt_num + 1; +  ngtcp2_rtb *rtb = &pktns->rtb; +  int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; + +  if (NGTCP2_MAX_PKT_NUM / 2 < n) { +    return 4; +  } + +  n = n * 2 - 1; + +  if (n > 0xffffff) { +    return 4; +  } +  if (n > 0xffff) { +    return 3; +  } +  if (n > 0xff) { +    return 2; +  } +  return 1; +} + +/* + * conn_get_cwnd returns cwnd for the current path. + */ +static uint64_t conn_get_cwnd(ngtcp2_conn *conn) { +  return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) +           ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_tx_udp_payload_size) +           : conn->cstat.cwnd; +} + +/* + * conn_cwnd_is_zero returns nonzero if the number of bytes the local + * endpoint can sent at this time is zero. + */ +static int conn_cwnd_is_zero(ngtcp2_conn *conn) { +  uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; +  uint64_t cwnd = conn_get_cwnd(conn); + +  if (bytes_in_flight >= cwnd) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, +                    "cwnd limited bytes_in_flight=%lu cwnd=%lu", +                    bytes_in_flight, cwnd); +  } + +  return bytes_in_flight >= cwnd; +} + +/* + * conn_retry_early_payloadlen returns the estimated wire length of + * the first STREAM frame of 0-RTT packet which should be + * retransmitted due to Retry packet. + */ +static uint64_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { +  ngtcp2_frame_chain *frc; +  ngtcp2_strm *strm; +  uint64_t len; + +  if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { +    return 0; +  } + +  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); + +    len = ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + +          NGTCP2_STREAM_OVERHEAD; + +    /* Take the min because in conn_should_pad_pkt we take max in +       order to deal with unbreakable DATAGRAM. */ +    return ngtcp2_min_uint64(len, NGTCP2_MIN_COALESCED_PAYLOADLEN); +  } + +  return 0; +} + +/* + * conn_verify_dcid verifies that destination connection ID in |hd| is + * valid for the connection.  If it is successfully verified and the + * remote endpoint uses new DCID in the packet, nonzero value is + * assigned to |*pnew_cid_used| if it is not NULL.  Otherwise 0 is + * assigned to it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + *     |dcid| is not known to the local endpoint. + */ +static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, +                            const ngtcp2_pkt_hd *hd) { +  ngtcp2_ksl_it it; +  ngtcp2_scid *scid; +  int rv; + +  it = ngtcp2_ksl_lower_bound(&conn->scid.set, &hd->dcid); +  if (ngtcp2_ksl_it_end(&it)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  scid = ngtcp2_ksl_it_get(&it); +  if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) { +    scid->flags |= NGTCP2_SCID_FLAG_USED; + +    if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) { +      rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe); +      if (rv != 0) { +        return rv; +      } +    } + +    if (pnew_cid_used) { +      *pnew_cid_used = 1; +    } +  } else if (pnew_cid_used) { +    *pnew_cid_used = 0; +  } + +  return 0; +} + +/* + * conn_should_pad_pkt returns nonzero if the packet should be padded. + * |type| is the type of packet.  |left| is the space left in packet + * buffer.  |write_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, +                               uint64_t write_datalen, int ack_eliciting, +                               int require_padding) { +  uint64_t min_payloadlen; + +  if (type == NGTCP2_PKT_INITIAL) { +    if (conn->server) { +      if (!ack_eliciting) { +        return 0; +      } + +      if ((conn->hs_pktns->crypto.tx.ckm && +           (conn->hs_pktns->rtb.probe_pkt_left || +            !ngtcp2_strm_streamfrq_empty(&conn->hs_pktns->crypto.strm) || +            !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) || +          conn->pktns.crypto.tx.ckm) { +        /* If we have something to send in Handshake or 1RTT packet, +           then add PADDING in that packet. */ +        min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; +      } else { +        return 1; +      } +    } else { +      if (conn->hs_pktns->crypto.tx.ckm && +          (conn->hs_pktns->rtb.probe_pkt_left || +           !ngtcp2_strm_streamfrq_empty(&conn->hs_pktns->crypto.strm) || +           !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { +        /* If we have something to send in Handshake packet, then add +           PADDING in Handshake packet. */ +        min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; +      } else if (conn->early.ckm && write_datalen > 0) { +        /* If we have something to send in 0RTT packet, then add +           PADDING in that packet.  Take maximum in case that +           write_datalen includes DATAGRAM which cannot be split. */ +        min_payloadlen = +          ngtcp2_max_uint64(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN); +      } else { +        return 1; +      } +    } +  } else { +    assert(type == NGTCP2_PKT_HANDSHAKE); + +    if (!require_padding) { +      return 0; +    } + +    if (!conn->pktns.crypto.tx.ckm) { +      return 1; +    } + +    min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN; +  } + +  /* TODO the next packet type should be taken into account */ +  return left < +         /* TODO Assuming that pkt_num is encoded in 1 byte. */ +         NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + +           conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 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 &= (uint32_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_keep_alive_enabled returns nonzero if keep-alive is enabled. + */ +static int conn_keep_alive_enabled(ngtcp2_conn *conn) { +  return conn->keep_alive.last_ts != UINT64_MAX && +         conn->keep_alive.timeout != UINT64_MAX; +} + +/* + * conn_keep_alive_expired returns nonzero if keep-alive timer has + * expired. + */ +static int conn_keep_alive_expired(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  return ngtcp2_tstamp_elapsed(conn->keep_alive.last_ts, +                               conn->keep_alive.timeout, ts); +} + +/* + * conn_keep_alive_expiry returns the expiry time of keep-alive timer. + */ +static ngtcp2_tstamp conn_keep_alive_expiry(ngtcp2_conn *conn) { +  if ((conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) || +      !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || +      !conn_keep_alive_enabled(conn) || +      conn->keep_alive.last_ts >= UINT64_MAX - conn->keep_alive.timeout) { +    return UINT64_MAX; +  } + +  return conn->keep_alive.last_ts + conn->keep_alive.timeout; +} + +/* + * conn_cancel_expired_keep_alive_timer cancels the expired keep-alive + * timer. + */ +static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn, +                                                 ngtcp2_tstamp ts) { +  if (!(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) && +      conn_keep_alive_expired(conn, ts)) { +    conn->flags |= NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; +  } +} + +/* + * conn_update_keep_alive_last_ts updates the base time point of + * keep-alive timer. + */ +static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn, +                                           ngtcp2_tstamp ts) { +  conn->keep_alive.last_ts = ts; +  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED; +} + +void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, +                                        ngtcp2_duration timeout) { +  if (timeout == 0) { +    timeout = UINT64_MAX; +  } + +  conn->keep_alive.timeout = timeout; +} + +/* + * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event + * loop.  Packet pacing might require sub milliseconds packet spacing, + * but userspace event loop might not offer such precision. + * Typically, if delay is 0.5 microseconds, the actual delay after + * which we can send packet is well over 1 millisecond when event loop + * is involved (which includes other stuff, like reading packets etc + * in a typical single threaded use case). + */ +#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS + +static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn, +                                             ngtcp2_tstamp ts) { +  if (conn->tx.pacing.next_ts == UINT64_MAX) { +    return; +  } + +  if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) { +    return; +  } + +  conn->tx.pacing.next_ts = UINT64_MAX; +} + +static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  return conn->tx.pacing.next_ts == UINT64_MAX || +         conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD; +} + +static uint8_t conn_pkt_flags(ngtcp2_conn *conn) { +  if (conn->remote.transport_params && +      conn->remote.transport_params->grease_quic_bit && +      (conn->flags & NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT)) { +    return NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; +  } + +  return NGTCP2_PKT_FLAG_NONE; +} + +static uint8_t conn_pkt_flags_long(ngtcp2_conn *conn) { +  return NGTCP2_PKT_FLAG_LONG_FORM | conn_pkt_flags(conn); +} + +static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { +  return (uint8_t)(conn_pkt_flags(conn) | ((conn->pktns.crypto.tx.ckm->flags & +                                            NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) +                                             ? NGTCP2_PKT_FLAG_KEY_PHASE +                                             : NGTCP2_PKT_FLAG_NONE)); +} + +static size_t conn_min_pktlen(ngtcp2_conn *conn); + +/* + * conn_write_handshake_pkt writes handshake packet in the buffer + * pointed by |dest| whose length is |destlen|.  |dgram_offset| is the + * offset in UDP datagram payload where this QUIC packet is positioned + * at.  |type| specifies long packet type.  It should be either + * NGTCP2_PKT_INITIAL or NGTCP2_PKT_HANDSHAKE_PKT. + * + * |write_datalen| is the minimum length of application data ready to + * send in subsequent 0RTT packet. + * + * 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, ngtcp2_pkt_info *pi, uint8_t *dest, +                         size_t destlen, size_t dgram_offset, uint8_t type, +                         uint8_t flags, uint64_t write_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; +  uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; +  int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; +  int pkt_empty = 1; +  int min_padded = 0; +  int padded = 0; +  int hd_logged = 0; +  uint64_t crypto_offset; +  ngtcp2_ssize num_reclaimed; +  uint32_t version; + +  switch (type) { +  case NGTCP2_PKT_INITIAL: +    if (!conn->in_pktns) { +      return 0; +    } +    assert(conn->in_pktns->crypto.tx.ckm); +    pktns = conn->in_pktns; +    version = conn->negotiated_version ? conn->negotiated_version +                                       : conn->client_chosen_version; +    if (version == conn->client_chosen_version) { +      cc.ckm = pktns->crypto.tx.ckm; +      cc.hp_ctx = pktns->crypto.tx.hp_ctx; +    } else { +      assert(conn->vneg.version == version); + +      cc.ckm = conn->vneg.tx.ckm; +      cc.hp_ctx = conn->vneg.tx.hp_ctx; +    } +    break; +  case NGTCP2_PKT_HANDSHAKE: +    if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { +      return 0; +    } +    pktns = conn->hs_pktns; +    version = conn->negotiated_version; +    cc.ckm = pktns->crypto.tx.ckm; +    cc.hp_ctx = pktns->crypto.tx.hp_ctx; +    break; +  default: +    ngtcp2_unreachable(); +  } + +  cc.aead = pktns->crypto.ctx.aead; +  cc.hp = pktns->crypto.ctx.hp; +  cc.encrypt = conn->callbacks.encrypt; +  cc.hp_mask = conn->callbacks.hp_mask; + +  ngtcp2_pkt_hd_init( +    &hd, conn_pkt_flags_long(conn), type, &conn->dcid.current.cid, &conn->oscid, +    pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), version, 0); + +  if (!conn->server && type == NGTCP2_PKT_INITIAL && +      conn->local.settings.tokenlen) { +    hd.token = conn->local.settings.token; +    hd.tokenlen = conn->local.settings.tokenlen; +  } + +  ngtcp2_ppe_init(&ppe, dest, destlen, dgram_offset, &cc); + +  rv = ngtcp2_ppe_encode_hd(&ppe, &hd); +  if (rv != 0) { +    assert(NGTCP2_ERR_NOBUF == rv); +    return 0; +  } + +  if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { +    return 0; +  } + +  rv = ngtcp2_conn_create_ack_frame(conn, &ackfr, pktns, type, ts, +                                    /* ack_delay = */ 0, +                                    NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); +  if (rv != 0) { +    ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, 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; +    } +  } + +  /* Server requires at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes in +     order to send ack-eliciting Initial packet. */ +  if (!conn->server || type != NGTCP2_PKT_INITIAL || +      destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +  build_pkt: +    for (; !ngtcp2_strm_streamfrq_empty(&pktns->crypto.strm);) { +      left = ngtcp2_ppe_left(&ppe); + +      crypto_offset = ngtcp2_strm_streamfrq_unacked_offset(&pktns->crypto.strm); +      if (crypto_offset == (uint64_t)-1) { +        ngtcp2_strm_streamfrq_clear(&pktns->crypto.strm); +        break; +      } + +      left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); +      if (left == (size_t)-1) { +        break; +      } + +      rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, left); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, +                                             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) { +        ngtcp2_unreachable(); +      } + +      *pfrc = nfrc; +      pfrc = &(*pfrc)->next; + +      pkt_empty = 0; +      rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                         NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                         NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +    } + +    if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && +        pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { +      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); +      if (num_reclaimed < 0) { +        ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, +                                             conn->mem); +        return rv; +      } +      if (num_reclaimed) { +        goto build_pkt; +      } +      /* We had pktns->rtb.num_retransmittable > 0 but the contents of +         those packets have been acknowledged (i.e., retransmission in +         another packet).  For server, in this case, we don't have to +         send any probe packet.  Client needs to send probe packets +         until it knows that server has completed address validation or +         handshake has been confirmed. */ +      if (pktns->rtb.num_pto_eliciting == 0 && +          (conn->server || +           (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | +                           NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { +        pktns->rtb.probe_pkt_left = 0; +        ngtcp2_conn_set_loss_detection_timer(conn, ts); +        /* TODO If packet is empty, we should return now if cwnd is +           zero. */ +      } +    } + +    if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && +        pktns->rtb.probe_pkt_left) { +      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_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PROBE; +        pkt_empty = 0; +      } +    } + +    if (!pkt_empty) { +      if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +        if (ngtcp2_tstamp_elapsed(pktns->tx.non_ack_pkt_start_ts, +                                  conn->cstat.smoothed_rtt, ts)) { +          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_ENTRY_FLAG_ACK_ELICITING; +            pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; +            pkt_empty = 0; +          } +        } else if (pktns->tx.non_ack_pkt_start_ts == UINT64_MAX) { +          pktns->tx.non_ack_pkt_start_ts = ts; +        } +      } else { +        pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; +      } +    } +  } + +  if (pkt_empty && !require_padding) { +    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), write_datalen, +        (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0, +        require_padding)) { +    lfr.type = NGTCP2_FRAME_PADDING; +    lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe); +  } else if (pkt_empty) { +    return 0; +  } else { +    lfr.type = NGTCP2_FRAME_PADDING; +    lfr.padding.len = ngtcp2_ppe_padding_size(&ppe, conn_min_pktlen(conn)); +    min_padded = 1; +  } + +  if (lfr.padding.len) { +    if (!min_padded || +        (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +      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_objalloc_del(frq, &conn->frc_objalloc, conn->mem); +    return spktlen; +  } + +  ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen); + +  if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) { +    if (pi) { +      conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); +    } + +    rv = +      ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen, +                                    rtb_entry_flags, &conn->rtb_entry_objalloc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); +      return rv; +    } + +    rv = conn_on_pkt_sent(conn, pktns, rtbent); +    if (rv != 0) { +      ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, +                                    &conn->frc_objalloc, conn->mem); +      return rv; +    } + +    if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && +        (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { +      conn_restart_timer_on_write(conn, ts); +    } +  } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { +    conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); +  } + +  if (pktns->rtb.probe_pkt_left && +      (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +    --pktns->rtb.probe_pkt_left; +  } + +  conn_update_keep_alive_last_ts(conn, ts); + +  conn->dcid.current.bytes_sent += (uint64_t)spktlen; + +  conn->tx.pacing.pktlen += (size_t)spktlen; + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  ++pktns->tx.last_pkt_num; + +  return spktlen; +} + +/* + * conn_write_ack_pkt writes QUIC packet for type |type| which only + * includes ACK frame in the buffer pointed by |dest| whose length is + * |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, +                                       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; +  ngtcp2_ssize spktlen; + +  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_1RTT: +    pktns = &conn->pktns; +    ack_delay = conn_compute_ack_delay(conn); +    ack_delay_exponent = conn->local.transport_params.ack_delay_exponent; +    break; +  default: +    ngtcp2_unreachable(); +  } + +  if (!pktns->crypto.tx.ckm) { +    return 0; +  } + +  ackfr = NULL; +  rv = ngtcp2_conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay, +                                    ack_delay_exponent); +  if (rv != 0) { +    return rv; +  } + +  if (!ackfr) { +    return 0; +  } + +  spktlen = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE, +    &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + +  if (spktlen <= 0) { +    return spktlen; +  } + +  conn->dcid.current.bytes_sent += (uint64_t)spktlen; + +  return spktlen; +} + +static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns, +                               ngtcp2_tstamp ts) { +  ngtcp2_pktns *pktns = *ppktns; +  uint64_t bytes_in_flight; + +  bytes_in_flight = pktns->rtb.cc_bytes_in_flight; + +  assert(conn->cstat.bytes_in_flight >= bytes_in_flight); + +  conn->cstat.bytes_in_flight -= bytes_in_flight; +  conn->cstat.pto_count = 0; +  conn->cstat.last_tx_pkt_ts[pktns->id] = UINT64_MAX; +  conn->cstat.loss_time[pktns->id] = UINT64_MAX; + +  conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); +  conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); +  conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); +  conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); + +  pktns_del(pktns, conn->mem); +  *ppktns = NULL; + +  ngtcp2_conn_set_loss_detection_timer(conn, ts); +} + +void ngtcp2_conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  if (!conn->in_pktns) { +    return; +  } + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                  "discarding Initial packet number space"); + +  conn_discard_pktns(conn, &conn->in_pktns, ts); + +  conn_vneg_crypto_free(conn); + +  memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx)); +  memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx)); +} + +void ngtcp2_conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  if (!conn->hs_pktns) { +    return; +  } + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                  "discarding Handshake packet number space"); + +  conn_discard_pktns(conn, &conn->hs_pktns, ts); +} + +/* + * conn_discard_early_key discards early key. + */ +static void conn_discard_early_key(ngtcp2_conn *conn) { +  assert(conn->early.ckm); + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "discarding early key"); + +  conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx); +  conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx); +  memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx)); + +  ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); +  conn->early.ckm = NULL; +} + +/* + * 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, +                                                  ngtcp2_pkt_info *pi, +                                                  uint8_t *dest, size_t destlen, +                                                  ngtcp2_tstamp ts) { +  ngtcp2_ssize res = 0, nwrite = 0; + +  /* In the most cases, client sends ACK in conn_write_handshake_pkt. +     This function is only called when it is CWND limited or pacing +     limited.  It is not required for client to send ACK for server +     Initial.  This is because once it gets server Initial, it gets +     Handshake tx key and discards Initial key.  The only good reason +     to send ACK is give server RTT measurement early. */ +  if (conn->server && conn->in_pktns) { +    nwrite = +      conn_write_ack_pkt(conn, pi, 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, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts); +    if (nwrite < 0) { +      assert(nwrite != NGTCP2_ERR_NOBUF); +      return nwrite; +    } + +    res += nwrite; + +    if (!conn->server && nwrite) { +      ngtcp2_conn_discard_initial_state(conn, ts); +    } +  } + +  return res; +} + +/* + * conn_write_client_initial writes Initial packet in the buffer + * pointed by |dest| whose length is |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, +                                              ngtcp2_pkt_info *pi, +                                              uint8_t *dest, size_t destlen, +                                              uint64_t early_datalen, +                                              ngtcp2_tstamp ts) { +  int rv; + +  rv = conn_call_client_initial(conn); +  if (rv != 0) { +    return rv; +  } + +  return conn_write_handshake_pkt( +    conn, pi, dest, destlen, 0, NGTCP2_PKT_INITIAL, NGTCP2_WRITE_PKT_FLAG_NONE, +    early_datalen, ts); +} + +/* + * dcid_tx_left returns the maximum number of bytes that server is + * allowed to send to an unvalidated path associated to |dcid|. + */ +static uint64_t dcid_tx_left(ngtcp2_dcid *dcid) { +  if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { +    return SIZE_MAX; +  } +  /* From QUIC spec: Prior to validating the client address, servers +     MUST NOT send more than three times as many bytes as the number +     of bytes they have received. */ +  assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); + +  return dcid->bytes_recv * 3 - dcid->bytes_sent; +} + +/* + * conn_server_tx_left returns the maximum number of bytes that server + * is allowed to send to an unvalidated path. + */ +static uint64_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { +  assert(conn->server); + +  /* If pv->dcid has the current path, use conn->dcid.current.  This +     is because conn->dcid.current gets update for bytes_recv and +     bytes_sent. */ +  if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { +    return dcid_tx_left(&conn->dcid.current); +  } + +  return dcid_tx_left(dcid); +} + +/* + * 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, +                                              ngtcp2_pkt_info *pi, +                                              uint8_t *dest, size_t destlen, +                                              uint64_t write_datalen, +                                              ngtcp2_tstamp ts) { +  ngtcp2_ssize nwrite; +  ngtcp2_ssize res = 0; +  ngtcp2_rtb_entry *rtbent; +  uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_ksl_it it; + +  /* As a client, we would like to discard Initial packet number space +     when sending the first Handshake packet.  When sending Handshake +     packet, it should be one of 1) sending ACK, 2) sending PTO probe +     packet, or 3) sending CRYPTO.  If we have pending acknowledgement +     for Initial, then do not discard Initial packet number space. +     Otherwise, if either 1) or 2) is satisfied, discard Initial +     packet number space.  When sending Handshake CRYPTO, it indicates +     that client has received Handshake CRYPTO from server.  Initial +     packet number space is discarded because 1) is met.  If there is +     pending Initial ACK, Initial packet number space is discarded +     after writing the first Handshake packet. +   */ +  if (!conn->server && conn->hs_pktns->crypto.tx.ckm && conn->in_pktns && +      !ngtcp2_acktr_require_active_ack(&conn->in_pktns->acktr, +                                       /* max_ack_delay = */ 0, ts) && +      (ngtcp2_acktr_require_active_ack(&conn->hs_pktns->acktr, +                                       /* max_ack_delay = */ 0, ts) || +       conn->hs_pktns->rtb.probe_pkt_left)) { +    /* Discard Initial state here so that Handshake packet is not +       padded. */ +    ngtcp2_conn_discard_initial_state(conn, ts); +  } else if (conn->in_pktns) { +    nwrite = +      conn_write_handshake_pkt(conn, pi, dest, destlen, 0, NGTCP2_PKT_INITIAL, +                               NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); +    if (nwrite < 0) { +      assert(nwrite != NGTCP2_ERR_NOBUF); +      return nwrite; +    } + +    if (nwrite == 0) { +      if (conn->server && +          (conn->in_pktns->rtb.probe_pkt_left || +           !ngtcp2_strm_streamfrq_empty(&conn->in_pktns->crypto.strm))) { +        if (cstat->loss_detection_timer != UINT64_MAX && +            conn_server_tx_left(conn, &conn->dcid.current) < +              NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +          ngtcp2_log_info( +            &conn->log, NGTCP2_LOG_EVENT_LDC, +            "loss detection timer canceled due to amplification limit"); +          ngtcp2_conn_cancel_loss_detection_timer(conn); +        } + +        return 0; +      } +    } else { +      res += nwrite; +      dest += nwrite; +      destlen -= (size_t)nwrite; + +      /* If initial packet size is at least +         NGTCP2_MAX_UDP_PAYLOAD_SIZE, no extra padding is needed in a +         subsequent packet. */ +      if (nwrite < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +        if (conn->server) { +          it = ngtcp2_rtb_head(&conn->in_pktns->rtb); +          if (!ngtcp2_ksl_it_end(&it)) { +            rtbent = ngtcp2_ksl_it_get(&it); +            if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +              wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +            } +          } +        } else { +          wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +        } +      } +    } +  } + +  nwrite = +    conn_write_handshake_pkt(conn, pi, dest, destlen, (size_t)res, +                             NGTCP2_PKT_HANDSHAKE, wflags, write_datalen, ts); +  if (nwrite < 0) { +    assert(nwrite != NGTCP2_ERR_NOBUF); +    return nwrite; +  } + +  res += nwrite; + +  if (!conn->server && conn->hs_pktns->crypto.tx.ckm && nwrite) { +    /* We don't need to send further Initial packet if we have +       Handshake key and sent something with it.  So discard initial +       state here. */ +    ngtcp2_conn_discard_initial_state(conn, ts); +  } + +  return res; +} + +/* + * conn_initial_stream_rx_offset returns the initial maximum offset of + * data for a stream denoted by |stream_id|. + */ +static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn, +                                              int64_t stream_id) { +  int local_stream = conn_local_stream(conn, stream_id); + +  if (bidi_stream(stream_id)) { +    if (local_stream) { +      return conn->local.transport_params.initial_max_stream_data_bidi_local; +    } +    return conn->local.transport_params.initial_max_stream_data_bidi_remote; +  } + +  if (local_stream) { +    return 0; +  } +  return conn->local.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 inc = strm->rx.unsent_max_offset - strm->rx.max_offset; +  (void)conn; + +  return strm->rx.window < 2 * inc; +} + +/* + * conn_should_send_max_data returns nonzero if MAX_DATA frame should + * be sent. + */ +static int conn_should_send_max_data(ngtcp2_conn *conn) { +  uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset; + +  return conn->rx.window < 2 * inc; +} + +/* + * conn_required_num_new_connection_id returns the number of + * additional connection ID the local endpoint has to provide to the + * remote endpoint. + */ +static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { +  uint64_t n; +  size_t len = ngtcp2_ksl_len(&conn->scid.set); +  size_t lim; + +  if (len >= NGTCP2_MAX_SCID_POOL_SIZE) { +    return 0; +  } + +  assert(NGTCP2_MAX_SCID_POOL_SIZE >= conn->scid.num_in_flight); + +  lim = NGTCP2_MAX_SCID_POOL_SIZE - conn->scid.num_in_flight; +  if (lim == 0) { +    return 0; +  } + +  assert(conn->remote.transport_params); +  assert(conn->remote.transport_params->active_connection_id_limit); + +  /* len includes retired CID.  We don't provide extra CID if doing so +     exceeds NGTCP2_MAX_SCID_POOL_SIZE. */ + +  n = conn->remote.transport_params->active_connection_id_limit + +      conn->scid.num_retired; + +  n = ngtcp2_min_uint64(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; + +  return (size_t)ngtcp2_min_uint64(lim, n); +} + +/* + * conn_enqueue_new_connection_id generates additional connection IDs + * and prepares to send them to the remote endpoint. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { +  size_t i, need = conn_required_num_new_connection_id(conn); +  size_t cidlen = conn->oscid.datalen; +  ngtcp2_cid cid; +  uint64_t seq; +  int rv; +  uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +  ngtcp2_frame_chain *nfrc; +  ngtcp2_pktns *pktns = &conn->pktns; +  ngtcp2_scid *scid; +  ngtcp2_ksl_it it; + +  for (i = 0; i < need; ++i) { +    rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen); +    if (rv != 0) { +      return rv; +    } + +    if (cid.datalen != cidlen) { +      return NGTCP2_ERR_CALLBACK_FAILURE; +    } + +    /* Assert uniqueness */ +    it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid); +    if (!ngtcp2_ksl_it_end(&it) && +        ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) { +      return NGTCP2_ERR_CALLBACK_FAILURE; +    } + +    seq = ++conn->scid.last_seq; + +    scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid)); +    if (scid == NULL) { +      return NGTCP2_ERR_NOMEM; +    } + +    ngtcp2_scid_init(scid, seq, &cid); + +    rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid); +    if (rv != 0) { +      ngtcp2_mem_free(conn->mem, scid); +      return rv; +    } + +    rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +    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; + +    assert(NGTCP2_MAX_SCID_POOL_SIZE > conn->scid.num_in_flight); + +    ++conn->scid.num_in_flight; +  } + +  return 0; +} + +/* + * conn_remove_retired_connection_id removes the already retired + * connection ID.  It waits PTO before actually removing a connection + * ID after it receives RETIRE_CONNECTION_ID from peer to catch + * reordered packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static int conn_remove_retired_connection_id(ngtcp2_conn *conn, +                                             ngtcp2_duration pto, +                                             ngtcp2_tstamp ts) { +  ngtcp2_duration timeout = pto; +  ngtcp2_scid *scid; +  ngtcp2_dcid *dcid; +  int rv; + +  for (; !ngtcp2_pq_empty(&conn->scid.used);) { +    scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); + +    if (!ngtcp2_tstamp_elapsed(scid->retired_ts, timeout, ts)) { +      break; +    } + +    assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED); + +    rv = conn_call_remove_connection_id(conn, &scid->cid); +    if (rv != 0) { +      return rv; +    } + +    ngtcp2_ksl_remove(&conn->scid.set, NULL, &scid->cid); +    ngtcp2_pq_pop(&conn->scid.used); +    ngtcp2_mem_free(conn->mem, scid); + +    assert(conn->scid.num_retired); +    --conn->scid.num_retired; +  } + +  for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); +    if (dcid->retired_ts + timeout >= ts) { +      break; +    } + +    rv = conn_call_deactivate_dcid(conn, dcid); +    if (rv != 0) { +      return rv; +    } + +    ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb); +  } + +  return 0; +} + +/* + * conn_min_pktlen returns the minimum length of packet this endpoint + * sends.  It may underestimate the length because this does not take + * into account header protection sample. + */ +static size_t conn_min_pktlen(ngtcp2_conn *conn) { +  return conn->oscid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; +} + +/* + * conn_handle_unconfirmed_key_update_from_remote deals with key + * update which has not been confirmed yet and initiated by the remote + * endpoint. + * + * If key update was initiated by the remote endpoint, acknowledging a + * packet encrypted with the new key completes key update procedure. + */ +static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn, +                                                           int64_t largest_ack, +                                                           ngtcp2_tstamp ts) { +  if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || +      (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) || +      largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) { +    return; +  } + +  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; +  conn->crypto.key_update.confirmed_ts = ts; + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); +} + +static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn); + +/* + * strm_should_send_stream_data_blocked returns nonzero if + * STREAM_DATA_BLOCKED frame should be sent to |strm|. + */ +static int strm_should_send_stream_data_blocked(ngtcp2_strm *strm) { +  return strm->tx.offset == strm->tx.max_offset && +         strm->tx.last_blocked_offset != strm->tx.max_offset; +} + +/* + * conn_should_send_data_blocked returns nonzero if DATA_BLOCKED frame + * should be sent. + */ +static int conn_should_send_data_blocked(ngtcp2_conn *conn) { +  return conn->tx.offset == conn->tx.max_offset && +         conn->tx.last_blocked_offset != conn->tx.max_offset; +} + +/* + * conn_reset_ppe_pending clears NGTCP2_CONN_FLAG_PPE_PENDING flag and + * nullifies conn->pkt. + */ +static void conn_reset_ppe_pending(ngtcp2_conn *conn) { +  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + +  memset(&conn->pkt, 0, sizeof(conn->pkt)); +} + +/* + * conn_write_pkt writes a protected packet in the buffer pointed by + * |dest| whose length if |destlen|.  |dgram_offset| is the offset in + * UDP datagram payload where this QUIC packet is positioned at. + * |type| specifies the type of packet.  It can be NGTCP2_PKT_1RTT or + * NGTCP2_PKT_0RTT. + * + * This function can send new stream data.  In order to send stream + * data, specify the underlying stream and parameters to + * |vmsg|->stream.  If |vmsg|->stream.fin is set to nonzero, it + * signals that the given data is the final portion of the stream. + * |vmsg|->stream.data vector of length |vmsg|->stream.datacnt + * specifies stream data to send.  The number of bytes sent to the + * stream is assigned to *|vmsg|->stream.pdatalen.  If 0 length STREAM + * data is sent, 0 is assigned to it.  The caller should initialize + * *|vmsg|->stream.pdatalen to -1. + * + * If |require_padding| is nonzero, padding bytes are added to occupy + * the remaining packet payload. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + * NGTCP2_ERR_STREAM_DATA_BLOCKED + *     Stream data could not be written because of flow control. + */ +static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, +                                   uint8_t *dest, size_t destlen, +                                   size_t dgram_offset, ngtcp2_vmsg *vmsg, +                                   uint8_t type, uint8_t flags, +                                   ngtcp2_tstamp ts) { +  int rv = 0; +  ngtcp2_crypto_cc *cc = &conn->pkt.cc; +  ngtcp2_ppe *ppe = &conn->pkt.ppe; +  ngtcp2_pkt_hd *hd = &conn->pkt.hd; +  ngtcp2_frame *ackfr = NULL, lfr; +  ngtcp2_ssize nwrite; +  ngtcp2_frame_chain **pfrc, *nfrc, *frc; +  ngtcp2_rtb_entry *ent; +  ngtcp2_strm *strm; +  int pkt_empty = 1; +  uint64_t ndatalen = 0; +  int send_stream = 0; +  int stream_blocked = 0; +  int send_datagram = 0; +  ngtcp2_pktns *pktns = &conn->pktns; +  size_t left; +  uint64_t datalen = 0; +  ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; +  size_t datacnt; +  uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; +  int hd_logged = 0; +  ngtcp2_path_challenge_entry *pcent; +  uint8_t hd_flags = NGTCP2_PKT_FLAG_NONE; +  int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; +  int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0; +  int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; +  size_t min_pktlen = conn_min_pktlen(conn); +  int min_padded = 0; +  int padded = 0; +  ngtcp2_cc_pkt cc_pkt; +  uint64_t crypto_offset; +  uint64_t stream_offset; +  ngtcp2_ssize num_reclaimed; +  int fin; +  uint64_t target_max_data; +  ngtcp2_conn_stat *cstat = &conn->cstat; +  uint64_t delta; +  const ngtcp2_cid *scid = NULL; +  int keep_alive_expired = 0; +  uint32_t version = 0; + +  /* Return 0 if destlen is less than minimum packet length which can +     trigger Stateless Reset */ +  if (destlen < min_pktlen) { +    return 0; +  } + +  if (vmsg) { +    switch (vmsg->type) { +    case NGTCP2_VMSG_TYPE_STREAM: +      datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); +      ndatalen = conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); +      /* 0 length STREAM frame is allowed */ +      if (ndatalen || datalen == 0) { +        send_stream = 1; +      } else { +        stream_blocked = 1; +      } +      break; +    case NGTCP2_VMSG_TYPE_DATAGRAM: +      datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); +      send_datagram = 1; +      break; +    default: +      break; +    } +  } + +  if (!ppe_pending) { +    switch (type) { +    case NGTCP2_PKT_1RTT: +      hd_flags = conn_pkt_flags_short(conn); +      scid = NULL; +      cc->aead = pktns->crypto.ctx.aead; +      cc->hp = pktns->crypto.ctx.hp; +      cc->ckm = pktns->crypto.tx.ckm; +      cc->hp_ctx = pktns->crypto.tx.hp_ctx; + +      assert(conn->negotiated_version); + +      version = conn->negotiated_version; + +      /* 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.  Because server +         can use remote transport parameters sending stream data in +         0.5 RTT, it is also allowed to use remote transport +         parameters here.  */ +      if (conn->oscid.datalen && +          (conn->server || conn_is_tls_handshake_completed(conn))) { +        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 = conn_pkt_flags_long(conn); +      scid = &conn->oscid; +      cc->aead = conn->early.ctx.aead; +      cc->hp = conn->early.ctx.hp; +      cc->ckm = conn->early.ckm; +      cc->hp_ctx = conn->early.hp_ctx; +      version = conn->client_chosen_version; +      break; +    default: +      /* Unreachable */ +      ngtcp2_unreachable(); +    } + +    cc->encrypt = conn->callbacks.encrypt; +    cc->hp_mask = conn->callbacks.hp_mask; + +    if (conn_should_send_max_data(conn)) { +      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +      if (rv != 0) { +        return rv; +      } + +      if (conn->local.settings.max_window && +          conn->tx.last_max_data_ts != UINT64_MAX && +          ts - conn->tx.last_max_data_ts < +            NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt && +          conn->local.settings.max_window > conn->rx.window) { +        target_max_data = NGTCP2_FLOW_WINDOW_SCALING_FACTOR * conn->rx.window; +        if (target_max_data > conn->local.settings.max_window) { +          target_max_data = conn->local.settings.max_window; +        } + +        delta = target_max_data - conn->rx.window; +        if (conn->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) { +          delta = NGTCP2_MAX_VARINT - conn->rx.unsent_max_offset; +        } + +        conn->rx.window = target_max_data; +      } else { +        delta = 0; +      } + +      conn->tx.last_max_data_ts = ts; + +      nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; +      nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset + delta; +      nfrc->next = pktns->tx.frq; +      pktns->tx.frq = nfrc; + +      conn->rx.max_offset = conn->rx.unsent_max_offset = +        nfrc->fr.max_data.max_data; +    } + +    if (stream_blocked && conn_should_send_max_data(conn)) { +      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +      if (rv != 0) { +        return rv; +      } + +      nfrc->fr.type = NGTCP2_FRAME_DATA_BLOCKED; +      nfrc->fr.data_blocked.offset = conn->tx.max_offset; +      nfrc->next = pktns->tx.frq; +      pktns->tx.frq = nfrc; + +      conn->tx.last_blocked_offset = conn->tx.max_offset; +    } + +    if (stream_blocked && !ngtcp2_strm_is_tx_queued(vmsg->stream.strm) && +        strm_should_send_stream_data_blocked(vmsg->stream.strm)) { +      assert(vmsg); +      assert(vmsg->type == NGTCP2_VMSG_TYPE_STREAM); + +      vmsg->stream.strm->cycle = conn_tx_strmq_first_cycle(conn); +      rv = ngtcp2_conn_tx_strmq_push(conn, vmsg->stream.strm); +      if (rv != 0) { +        return rv; +      } +    } + +    ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, +                       pktns->tx.last_pkt_num + 1, +                       pktns_select_pkt_numlen(pktns), version, 0); + +    ngtcp2_ppe_init(ppe, dest, destlen, dgram_offset, 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.rb)) { +      pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); + +      /* PATH_RESPONSE is bound to the path that the corresponding +         PATH_CHALLENGE is received. */ +      if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { +        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.rb); + +          pkt_empty = 0; +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; +          require_padding = require_padding || !conn->server || +                            destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE; +          /* We don't retransmit PATH_RESPONSE. */ +        } +      } +    } + +    rv = ngtcp2_conn_create_ack_frame( +      conn, &ackfr, pktns, type, ts, conn_compute_ack_delay(conn), +      conn->local.transport_params.ack_delay_exponent); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      return rv; +    } + +    if (ackfr) { +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); +      if (rv != 0) { +        assert(NGTCP2_ERR_NOBUF == rv); +      } else { +        ngtcp2_acktr_commit_ack(&pktns->acktr); +        ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, +                             ackfr->ack.largest_ack); +        if (type == NGTCP2_PKT_1RTT) { +          conn_handle_unconfirmed_key_update_from_remote( +            conn, ackfr->ack.largest_ack, ts); +        } +        pkt_empty = 0; +      } +    } + +  build_pkt: +    for (pfrc = &pktns->tx.frq; *pfrc;) { +      if ((*pfrc)->binder && +          ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { +        frc = *pfrc; +        *pfrc = (*pfrc)->next; +        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +        continue; +      } + +      switch ((*pfrc)->fr.type) { +      case NGTCP2_FRAME_RESET_STREAM: +        strm = +          ngtcp2_conn_find_stream(conn, (*pfrc)->fr.reset_stream.stream_id); +        if (strm == NULL || +            !ngtcp2_strm_require_retransmit_reset_stream(strm)) { +          frc = *pfrc; +          *pfrc = (*pfrc)->next; +          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +          continue; +        } +        break; +      case NGTCP2_FRAME_STOP_SENDING: +        strm = +          ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); +        if (strm == NULL || +            !ngtcp2_strm_require_retransmit_stop_sending(strm)) { +          frc = *pfrc; +          *pfrc = (*pfrc)->next; +          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +          continue; +        } +        break; +      case NGTCP2_FRAME_STREAM: +        ngtcp2_unreachable(); +      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_objalloc_del(frc, &conn->frc_objalloc, 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_objalloc_del(frc, &conn->frc_objalloc, 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 || !ngtcp2_strm_require_retransmit_max_stream_data( +                              strm, &(*pfrc)->fr.max_stream_data)) { +          frc = *pfrc; +          *pfrc = (*pfrc)->next; +          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, 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_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +          continue; +        } +        break; +      case NGTCP2_FRAME_STREAM_DATA_BLOCKED: +        strm = ngtcp2_conn_find_stream( +          conn, (*pfrc)->fr.stream_data_blocked.stream_id); +        if (strm == NULL || !ngtcp2_strm_require_retransmit_stream_data_blocked( +                              strm, &(*pfrc)->fr.stream_data_blocked)) { +          frc = *pfrc; +          *pfrc = (*pfrc)->next; +          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +          continue; +        } +        break; +      case NGTCP2_FRAME_DATA_BLOCKED: +        if ((*pfrc)->fr.data_blocked.offset != conn->tx.max_offset) { +          frc = *pfrc; +          *pfrc = (*pfrc)->next; +          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +          continue; +        } +        break; +      case NGTCP2_FRAME_CRYPTO: +        ngtcp2_unreachable(); +      } + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); +      if (rv != 0) { +        assert(NGTCP2_ERR_NOBUF == rv); +        break; +      } + +      pkt_empty = 0; +      rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                         NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                         NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +      pfrc = &(*pfrc)->next; +    } + +    if (*pfrc == NULL) { +      for (; !ngtcp2_strm_streamfrq_empty(&pktns->crypto.strm);) { +        left = ngtcp2_ppe_left(ppe); + +        crypto_offset = +          ngtcp2_strm_streamfrq_unacked_offset(&pktns->crypto.strm); +        if (crypto_offset == (uint64_t)-1) { +          ngtcp2_strm_streamfrq_clear(&pktns->crypto.strm); +          break; +        } + +        left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); + +        if (left == (size_t)-1) { +          break; +        } + +        rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, 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) { +          ngtcp2_unreachable(); +        } + +        *pfrc = nfrc; +        pfrc = &(*pfrc)->next; + +        pkt_empty = 0; +        rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +      } +    } + +    if (*pfrc == NULL) { +      for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { +        strm = ngtcp2_conn_tx_strmq_top(conn); + +        if (strm->flags & NGTCP2_STRM_FLAG_SEND_RESET_STREAM) { +          rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +          if (rv != 0) { +            return rv; +          } + +          nfrc->fr.type = NGTCP2_FRAME_RESET_STREAM; +          nfrc->fr.reset_stream.stream_id = strm->stream_id; +          nfrc->fr.reset_stream.app_error_code = +            strm->tx.reset_stream_app_error_code; +          nfrc->fr.reset_stream.final_size = strm->tx.offset; +          *pfrc = nfrc; + +          strm->flags &= ~NGTCP2_STRM_FLAG_SEND_RESET_STREAM; + +          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; +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +          pfrc = &(*pfrc)->next; +        } + +        if (strm->flags & NGTCP2_STRM_FLAG_SEND_STOP_SENDING) { +          if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && +              ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) { +            strm->flags &= ~NGTCP2_STRM_FLAG_SEND_STOP_SENDING; +          } else { +            rv = conn_call_stream_stop_sending( +              conn, strm->stream_id, strm->tx.stop_sending_app_error_code, +              strm->stream_user_data); +            if (rv != 0) { +              assert(ngtcp2_err_is_fatal(rv)); + +              return rv; +            } + +            rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +            if (rv != 0) { +              return rv; +            } + +            nfrc->fr.type = NGTCP2_FRAME_STOP_SENDING; +            nfrc->fr.stop_sending.stream_id = strm->stream_id; +            nfrc->fr.stop_sending.app_error_code = +              strm->tx.stop_sending_app_error_code; +            *pfrc = nfrc; + +            strm->flags &= ~NGTCP2_STRM_FLAG_SEND_STOP_SENDING; + +            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; +            rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                               NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                               NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +            pfrc = &(*pfrc)->next; +          } +        } + +        if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && +            strm_should_send_stream_data_blocked(strm)) { +          rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +          if (rv != 0) { +            return rv; +          } + +          nfrc->fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; +          nfrc->fr.stream_data_blocked.stream_id = strm->stream_id; +          nfrc->fr.stream_data_blocked.offset = strm->tx.max_offset; +          *pfrc = nfrc; + +          strm->tx.last_blocked_offset = strm->tx.max_offset; + +          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; +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +          pfrc = &(*pfrc)->next; +        } + +        if (!(strm->flags & +              (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) && +            conn_should_send_max_stream_data(conn, strm)) { +          rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +          if (rv != 0) { +            assert(ngtcp2_err_is_fatal(rv)); +            return rv; +          } + +          if (conn->local.settings.max_stream_window && +              strm->tx.last_max_stream_data_ts != UINT64_MAX && +              ts - strm->tx.last_max_stream_data_ts < +                NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt && +              conn->local.settings.max_stream_window > strm->rx.window) { +            target_max_data = +              NGTCP2_FLOW_WINDOW_SCALING_FACTOR * strm->rx.window; +            if (target_max_data > conn->local.settings.max_stream_window) { +              target_max_data = conn->local.settings.max_stream_window; +            } + +            delta = target_max_data - strm->rx.window; +            if (strm->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) { +              delta = NGTCP2_MAX_VARINT - strm->rx.unsent_max_offset; +            } + +            strm->rx.window = target_max_data; +          } else { +            delta = 0; +          } + +          strm->tx.last_max_stream_data_ts = ts; + +          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 + delta; +          *pfrc = nfrc; + +          strm->rx.max_offset = strm->rx.unsent_max_offset = +            nfrc->fr.max_stream_data.max_stream_data; + +          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; +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                             NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +          pfrc = &(*pfrc)->next; +        } + +        if (ngtcp2_strm_streamfrq_empty(strm)) { +          ngtcp2_conn_tx_strmq_pop(conn); +          continue; +        } + +        stream_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); +        if (stream_offset == (uint64_t)-1) { +          ngtcp2_strm_streamfrq_clear(strm); +          ngtcp2_conn_tx_strmq_pop(conn); +          continue; +        } + +        left = ngtcp2_ppe_left(ppe); + +        left = ngtcp2_pkt_stream_max_datalen(strm->stream_id, stream_offset, +                                             left, left); + +        if (left == (size_t)-1) { +          break; +        } + +        rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left); +        if (rv != 0) { +          assert(ngtcp2_err_is_fatal(rv)); +          return rv; +        } + +        if (nfrc == NULL) { +          /* TODO Why? */ +          break; +        } + +        rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); +        if (rv != 0) { +          ngtcp2_unreachable(); +        } + +        *pfrc = nfrc; +        pfrc = &(*pfrc)->next; + +        pkt_empty = 0; +        rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + +        if (ngtcp2_strm_streamfrq_empty(strm)) { +          ngtcp2_conn_tx_strmq_pop(conn); +          continue; +        } + +        ngtcp2_conn_tx_strmq_pop(conn); +        ++strm->cycle; +        rv = ngtcp2_conn_tx_strmq_push(conn, strm); +        if (rv != 0) { +          assert(ngtcp2_err_is_fatal(rv)); +          return rv; +        } +      } +    } + +    /* Write MAX_STREAMS after RESET_STREAM so that we can extend +       stream ID space in one packet. */ +    if (*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_objalloc_new(&nfrc, &conn->frc_objalloc); +      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_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +        pfrc = &(*pfrc)->next; +      } +    } + +    if (*pfrc == NULL && +        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_objalloc_new(&nfrc, &conn->frc_objalloc); +      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_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; +        pfrc = &(*pfrc)->next; +      } +    } + +    if (pktns->tx.frq == NULL && !send_stream && !send_datagram && +        !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && +        pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { +      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); +      if (num_reclaimed < 0) { +        return rv; +      } +      if (num_reclaimed) { +        goto build_pkt; +      } + +      /* We had pktns->rtb.num_retransmittable > 0 but we were unable +         to reclaim any frame.  In this case, we do not have to send +         any probe packet. */ +      if (pktns->rtb.num_pto_eliciting == 0) { +        pktns->rtb.probe_pkt_left = 0; +        ngtcp2_conn_set_loss_detection_timer(conn, ts); + +        if (pkt_empty && conn_cwnd_is_zero(conn) && !require_padding) { +          return 0; +        } +      } +    } +  } 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 (*pfrc == NULL && send_stream && +      (ndatalen = ngtcp2_pkt_stream_max_datalen( +         vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, +         left)) != (size_t)-1 && +      (ndatalen || datalen == 0)) { +    datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT, +                                      vmsg->stream.data, vmsg->stream.datacnt, +                                      (size_t)ndatalen); +    ndatalen = ngtcp2_vec_len(data, datacnt); + +    assert((datacnt == 0 && datalen == 0) || (datacnt && datalen)); + +    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +      &nfrc, datacnt, &conn->frc_objalloc, conn->mem); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      return rv; +    } + +    nfrc->fr.stream.type = NGTCP2_FRAME_STREAM; +    nfrc->fr.stream.flags = 0; +    nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id; +    nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset; +    nfrc->fr.stream.datacnt = datacnt; +    ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt); + +    fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) && +          ndatalen == datalen; +    nfrc->fr.stream.fin = (uint8_t)fin; + +    rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); +    if (rv != 0) { +      ngtcp2_unreachable(); +    } + +    *pfrc = nfrc; +    pfrc = &(*pfrc)->next; + +    pkt_empty = 0; +    rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                       NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                       NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + +    vmsg->stream.strm->tx.offset += ndatalen; +    conn->tx.offset += ndatalen; +    vmsg->stream.strm->flags |= NGTCP2_STRM_FLAG_ANY_SENT; + +    if (fin) { +      ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR); +    } + +    if (vmsg->stream.pdatalen) { +      *vmsg->stream.pdatalen = (ngtcp2_ssize)ndatalen; +    } +  } else { +    send_stream = 0; +  } + +  if (vmsg && vmsg->type == NGTCP2_VMSG_TYPE_STREAM && +      ((stream_blocked && *pfrc == NULL) || +       (send_stream && +        !(vmsg->stream.strm->flags & NGTCP2_STRM_FLAG_SHUT_WR)))) { +    if (conn_should_send_data_blocked(conn)) { +      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); + +        return rv; +      } + +      nfrc->fr.type = NGTCP2_FRAME_DATA_BLOCKED; +      nfrc->fr.data_blocked.offset = conn->tx.offset; + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); +      if (rv != 0) { +        assert(NGTCP2_ERR_NOBUF == rv); + +        /* We cannot add nfrc to pktns->tx.frq here. */ +        ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); +      } else { +        *pfrc = nfrc; +        pfrc = &(*pfrc)->next; + +        pkt_empty = 0; +        rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + +        conn->tx.last_blocked_offset = conn->tx.max_offset; +      } +    } + +    strm = vmsg->stream.strm; + +    if (strm_should_send_stream_data_blocked(strm)) { +      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); + +        return rv; +      } + +      nfrc->fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; +      nfrc->fr.stream_data_blocked.stream_id = strm->stream_id; +      nfrc->fr.stream_data_blocked.offset = strm->tx.max_offset; + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); +      if (rv != 0) { +        assert(NGTCP2_ERR_NOBUF == rv); + +        /* We cannot add nfrc to pktns->tx.frq here. */ +        ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem); + +        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; +          } +        } +      } else { +        *pfrc = nfrc; +        pfrc = &(*pfrc)->next; + +        pkt_empty = 0; +        rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                           NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + +        strm->tx.last_blocked_offset = strm->tx.max_offset; +      } +    } +  } + +  if (*pfrc == NULL && send_datagram && +      left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) { +    if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) { +      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        return rv; +      } + +      nfrc->fr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; +      nfrc->fr.datagram.dgram_id = vmsg->datagram.dgram_id; +      nfrc->fr.datagram.datacnt = vmsg->datagram.datacnt; +      nfrc->fr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); +      assert(rv == 0); + +      /* Because DATAGRAM will not be retransmitted, we do not use +         data anymore.  Just nullify it.  The only reason to keep +         track a frame is keep dgram_id to pass it to +         ngtcp2_ack_datagram or ngtcp2_lost_datagram callbacks. */ +      nfrc->fr.datagram.datacnt = 0; +      nfrc->fr.datagram.data = NULL; + +      *pfrc = nfrc; +      pfrc = &(*pfrc)->next; +    } else { +      lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; +      lfr.datagram.datacnt = vmsg->datagram.datacnt; +      lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); +      assert(rv == 0); +    } + +    pkt_empty = 0; +    rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | +                       NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +                       NGTCP2_RTB_ENTRY_FLAG_DATAGRAM; + +    if (vmsg->datagram.paccepted) { +      *vmsg->datagram.paccepted = 1; +    } +  } else { +    send_datagram = 0; +  } + +  if (pkt_empty) { +    if (*pfrc == NULL && rv == 0 && stream_blocked && +        (write_more || !require_padding) && +        ngtcp2_conn_get_max_data_left(conn)) { +      if (write_more) { +        conn->pkt.pfrc = pfrc; +        conn->pkt.pkt_empty = pkt_empty; +        conn->pkt.rtb_entry_flags = rtb_entry_flags; +        conn->pkt.hd_logged = hd_logged; +        conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; +      } + +      return NGTCP2_ERR_STREAM_DATA_BLOCKED; +    } + +    keep_alive_expired = +      type == NGTCP2_PKT_1RTT && conn_keep_alive_expired(conn, ts); + +    if (conn->pktns.rtb.probe_pkt_left == 0 && !keep_alive_expired && +        !require_padding) { +      conn_reset_ppe_pending(conn); + +      return 0; +    } +  } else if (write_more) { +    conn->pkt.pfrc = pfrc; +    conn->pkt.pkt_empty = pkt_empty; +    conn->pkt.rtb_entry_flags = rtb_entry_flags; +    conn->pkt.hd_logged = hd_logged; +    conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; + +    assert(vmsg); + +    switch (vmsg->type) { +    case NGTCP2_VMSG_TYPE_STREAM: +      if (send_stream) { +        if (ngtcp2_ppe_left(ppe)) { +          return NGTCP2_ERR_WRITE_MORE; +        } +        break; +      } + +      if (*pfrc == NULL && ngtcp2_conn_get_max_data_left(conn) && +          stream_blocked) { +        return NGTCP2_ERR_STREAM_DATA_BLOCKED; +      } +      break; +    case NGTCP2_VMSG_TYPE_DATAGRAM: +      if (send_datagram && ngtcp2_ppe_left(ppe)) { +        return NGTCP2_ERR_WRITE_MORE; +      } +      /* If DATAGRAM cannot be written due to insufficient space, +         continue to create a packet with the hope that application +         calls ngtcp2_conn_writev_datagram again. */ +      break; +    default: +      ngtcp2_unreachable(); +    } +  } + +  if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +    if (ngtcp2_tstamp_elapsed(pktns->tx.non_ack_pkt_start_ts, +                              cstat->smoothed_rtt, ts) || +        keep_alive_expired || conn->pktns.rtb.probe_pkt_left) { +      lfr.type = NGTCP2_FRAME_PING; + +      rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); +      if (rv != 0) { +        assert(rv == NGTCP2_ERR_NOBUF); +        /* TODO If buffer is too small, PING cannot be written if +           packet is still empty. */ +      } else { +        rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; +        if (conn->pktns.rtb.probe_pkt_left) { +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE; +        } else { +          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING; +        } +        pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; +        pkt_empty = 0; +      } +    } else if (pktns->tx.non_ack_pkt_start_ts == UINT64_MAX) { +      pktns->tx.non_ack_pkt_start_ts = ts; +    } +  } else { +    pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; +  } + +  /* TODO Push STREAM frame back to ngtcp2_strm if there is an error +     before ngtcp2_rtb_entry is safely created and added. */ +  if (require_padding) { +    lfr.padding.len = ngtcp2_ppe_dgram_padding(ppe); +  } else { +    lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); +    min_padded = 1; +  } + +  if (lfr.padding.len) { +    if (!min_padded || +        (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +      padded = 1; +    } +    lfr.type = NGTCP2_FRAME_PADDING; +    ngtcp2_log_tx_fr(&conn->log, hd, &lfr); +    ngtcp2_qlog_write_frame(&conn->qlog, &lfr); +  } + +  nwrite = ngtcp2_ppe_final(ppe, NULL); +  if (nwrite < 0) { +    assert(ngtcp2_err_is_fatal((int)nwrite)); +    return nwrite; +  } + +  ++cc->ckm->use_count; + +  ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); + +  /* TODO ack-eliciting vs needs-tracking */ +  /* probe packet needs tracking but it does not need ACK, could be lost. */ +  if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) { +    if (pi) { +      conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts); +    } + +    rv = +      ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite, +                                    rtb_entry_flags, &conn->rtb_entry_objalloc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal((int)nwrite)); +      return rv; +    } + +    if (*pfrc != pktns->tx.frq) { +      ent->frc = pktns->tx.frq; +      pktns->tx.frq = *pfrc; +      *pfrc = NULL; +    } + +    if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && +        pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) { +      conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START, +                     ts); +    } + +    rv = conn_on_pkt_sent(conn, pktns, ent); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc, +                                    &conn->frc_objalloc, conn->mem); +      return rv; +    } + +    if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +      if (conn->cc.on_pkt_sent) { +        conn->cc.on_pkt_sent( +          &conn->cc, &conn->cstat, +          ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite, +                             NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost, +                             ent->rst.tx_in_flight, ent->rst.is_app_limited)); +      } + +      if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { +        conn_restart_timer_on_write(conn, ts); +      } +    } +  } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { +    conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts); +  } + +  conn_reset_ppe_pending(conn); + +  if (pktns->rtb.probe_pkt_left && +      (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +    --pktns->rtb.probe_pkt_left; + +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", +                    nwrite); +  } + +  conn_update_keep_alive_last_ts(conn, ts); + +  conn->dcid.current.bytes_sent += (uint64_t)nwrite; + +  conn->tx.pacing.pktlen += (size_t)nwrite; + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  ++pktns->tx.last_pkt_num; + +  return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( +  ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, +  uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, +  uint16_t rtb_entry_flags, const ngtcp2_path *path, 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 hd_flags; +  ngtcp2_rtb_entry *rtbent; +  int padded = 0; +  const ngtcp2_cid *scid; +  uint32_t version; + +  switch (type) { +  case NGTCP2_PKT_INITIAL: +    pktns = conn->in_pktns; +    hd_flags = conn_pkt_flags_long(conn); +    scid = &conn->oscid; +    version = conn->negotiated_version ? conn->negotiated_version +                                       : conn->client_chosen_version; +    if (version == conn->client_chosen_version) { +      cc.ckm = pktns->crypto.tx.ckm; +      cc.hp_ctx = pktns->crypto.tx.hp_ctx; +    } else { +      assert(version == conn->vneg.version); + +      cc.ckm = conn->vneg.tx.ckm; +      cc.hp_ctx = conn->vneg.tx.hp_ctx; +    } +    break; +  case NGTCP2_PKT_HANDSHAKE: +    pktns = conn->hs_pktns; +    hd_flags = conn_pkt_flags_long(conn); +    scid = &conn->oscid; +    version = conn->negotiated_version; +    cc.ckm = pktns->crypto.tx.ckm; +    cc.hp_ctx = pktns->crypto.tx.hp_ctx; +    break; +  case NGTCP2_PKT_1RTT: +    pktns = &conn->pktns; +    hd_flags = conn_pkt_flags_short(conn); +    scid = NULL; +    version = conn->negotiated_version; +    cc.ckm = pktns->crypto.tx.ckm; +    cc.hp_ctx = pktns->crypto.tx.hp_ctx; +    break; +  default: +    /* We don't support 0-RTT packet in this function. */ +    ngtcp2_unreachable(); +  } + +  cc.aead = pktns->crypto.ctx.aead; +  cc.hp = pktns->crypto.ctx.hp; +  cc.encrypt = conn->callbacks.encrypt; +  cc.hp_mask = conn->callbacks.hp_mask; + +  ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid, +                     pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), +                     version, 0); + +  ngtcp2_ppe_init(&ppe, dest, destlen, 0, &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); + +  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 (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL) { +    lfr.padding.len = ngtcp2_ppe_dgram_padding_size(&ppe, destlen); +  } else if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) { +    lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe); +  } else { +    switch (fr->type) { +    case NGTCP2_FRAME_PATH_CHALLENGE: +    case NGTCP2_FRAME_PATH_RESPONSE: +      if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +        lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe); +      } else { +        lfr.padding.len = 0; +      } +      break; +    default: +      lfr.padding.len = ngtcp2_ppe_padding_size(&ppe, conn_min_pktlen(conn)); +    } +  } +  if (lfr.padding.len) { +    padded = 1; +    ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); +    ngtcp2_qlog_write_frame(&conn->qlog, &lfr); +  } + +  nwrite = ngtcp2_ppe_final(&ppe, NULL); +  if (nwrite < 0) { +    return nwrite; +  } + +  if (type == NGTCP2_PKT_1RTT) { +    ++cc.ckm->use_count; +  } + +  ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); + +  /* Do this when we are sure that there is no error. */ +  switch (fr->type) { +  case NGTCP2_FRAME_ACK: +  case NGTCP2_FRAME_ACK_ECN: +    ngtcp2_acktr_commit_ack(&pktns->acktr); +    ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); +    if (type == NGTCP2_PKT_1RTT) { +      conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack, +                                                     ts); +    } + +    if (!(flags & (NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL | +                   NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING))) { +      padded = 0; +    } + +    break; +  } + +  if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && +      (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { +    if (pi && (conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE || +               !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE))) { +      conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); +    } + +    rv = +      ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, +                                    rtb_entry_flags, &conn->rtb_entry_objalloc); +    if (rv != 0) { +      return rv; +    } + +    rv = conn_on_pkt_sent(conn, pktns, rtbent); +    if (rv != 0) { +      ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, +                                    &conn->frc_objalloc, conn->mem); +      return rv; +    } + +    if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +      if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { +        conn_restart_timer_on_write(conn, ts); +      } + +      if (pktns->rtb.probe_pkt_left && path && +          ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +        --pktns->rtb.probe_pkt_left; + +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", +                        nwrite); +      } +    } +  } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { +    conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); +  } + +  if (path && ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +    conn_update_keep_alive_last_ts(conn, ts); +  } + +  if (!padded) { +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      break; +    default: +      conn->tx.pacing.pktlen += (size_t)nwrite; +    } +  } else { +    conn->tx.pacing.pktlen += (size_t)nwrite; +  } + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  ++pktns->tx.last_pkt_num; + +  return nwrite; +} + +/* + * conn_process_early_rtb makes any pending 0RTT packet 1RTT 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 1RTT packet. */ +    ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; +    ent->hd.type = NGTCP2_PKT_1RTT; +  } +} + +/* + * 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_is_tls_handshake_completed(conn) || +         (in_pktns && (in_pktns->rtb.num_pto_eliciting || +                       !ngtcp2_strm_streamfrq_empty(&in_pktns->crypto.strm))) || +         (hs_pktns && (hs_pktns->rtb.num_pto_eliciting || +                       !ngtcp2_strm_streamfrq_empty(&hs_pktns->crypto.strm))); +} + +/* + * 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. + * NGTCP2_ERR_CONNECTION_ID_LIMIT + *     The number of unacknowledged retirement exceeds the limit. + */ +static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { +  ngtcp2_pktns *pktns = &conn->pktns; +  ngtcp2_frame_chain *nfrc; +  int rv; + +  if (ngtcp2_conn_check_retired_dcid_tracked(conn, seq)) { +    return 0; +  } + +  rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +  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; + +  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.rb; +  ngtcp2_dcid *dest, *stale_dcid; +  int rv; + +  assert(dcid->cid.datalen); + +  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->retired_ts = ts; + +  return conn_retire_dcid_seq(conn, dcid->seq); +} + +/* + * conn_bind_dcid stores the DCID to |*pdcid| bound to |path|.  If + * such DCID is not found, bind the new DCID to |path| and stores it + * to |*pdcid|.  If a remote endpoint uses zero-length connection ID, + * the pointer to conn->dcid.current is assigned to |*pdcid|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + *     No unused DCID is available + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, +                          const ngtcp2_path *path, ngtcp2_tstamp ts) { +  ngtcp2_dcid *dcid, *ndcid; +  ngtcp2_cid cid; +  size_t i, len; +  int rv; + +  assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)); +  assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)); +  assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || +         !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)); + +  len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); +  for (i = 0; i < len; ++i) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + +    if (ngtcp2_path_eq(&dcid->ps.path, path)) { +      *pdcid = dcid; +      return 0; +    } +  } + +  if (conn->dcid.current.cid.datalen == 0) { +    ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); +    ngtcp2_cid_zero(&cid); +    ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); +    ngtcp2_dcid_set_path(ndcid, path); + +    *pdcid = ndcid; + +    return 0; +  } + +  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { +    return NGTCP2_ERR_CONN_ID_BLOCKED; +  } + +  if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); +    rv = conn_retire_dcid(conn, dcid, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +  ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); + +  ngtcp2_dcid_copy(ndcid, dcid); +  ndcid->bound_ts = ts; +  ngtcp2_dcid_set_path(ndcid, path); + +  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +  *pdcid = ndcid; + +  return 0; +} + +static int conn_start_pmtud(ngtcp2_conn *conn) { +  int rv; +  size_t hard_max_udp_payload_size; + +  assert(!conn->local.settings.no_pmtud); +  assert(!conn->pmtud); +  assert(conn_is_tls_handshake_completed(conn)); +  assert(conn->remote.transport_params); +  assert(conn->remote.transport_params->max_udp_payload_size >= +         NGTCP2_MAX_UDP_PAYLOAD_SIZE); + +  hard_max_udp_payload_size = (size_t)ngtcp2_min_uint64( +    conn->remote.transport_params->max_udp_payload_size, +    (uint64_t)conn->local.settings.max_tx_udp_payload_size); + +  rv = +    ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size, +                     hard_max_udp_payload_size, conn->pktns.tx.last_pkt_num + 1, +                     conn->local.settings.pmtud_probes, +                     conn->local.settings.pmtud_probeslen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  if (ngtcp2_pmtud_finished(conn->pmtud)) { +    ngtcp2_conn_stop_pmtud(conn); +  } + +  return 0; +} + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) { +  return conn_start_pmtud(conn); +} + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) { +  if (!conn->pmtud) { +    return; +  } + +  ngtcp2_pmtud_del(conn->pmtud); + +  conn->pmtud = NULL; +} + +static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn, +                                           ngtcp2_pkt_info *pi, uint8_t *dest, +                                           size_t destlen, ngtcp2_tstamp ts) { +  size_t probelen; +  ngtcp2_ssize nwrite; +  ngtcp2_frame lfr; + +  assert(conn->pmtud); +  assert(!ngtcp2_pmtud_finished(conn->pmtud)); + +  if (!ngtcp2_pmtud_require_probe(conn->pmtud)) { +    return 0; +  } + +  probelen = ngtcp2_pmtud_probelen(conn->pmtud); +  if (probelen > destlen) { +    return 0; +  } + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                  "sending PMTUD probe packet len=%zu", probelen); + +  lfr.type = NGTCP2_FRAME_PING; + +  nwrite = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, probelen, NGTCP2_PKT_1RTT, +    NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL, &conn->dcid.current.cid, &lfr, +    NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING | +      NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE, +    NULL, ts); +  if (nwrite < 0) { +    return nwrite; +  } + +  assert(nwrite); + +  ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns), +                          ts); + +  return nwrite; +} + +/* + * 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.cid.datalen && 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.cid.datalen && +      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; +} + +/* + * conn_abort_pv aborts the current path validation and frees + * resources allocated for it.  This function assumes that conn->pv is + * not NULL. + * + * 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_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_pv *pv = conn->pv; +  int rv; + +  assert(pv); + +  if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { +    rv = conn_call_path_validation(conn, pv, +                                   NGTCP2_PATH_VALIDATION_RESULT_ABORTED); +    if (rv != 0) { +      return rv; +    } +  } + +  return conn_stop_pv(conn, ts); +} + +static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, +                                     size_t payloadlen) { +  if (conn->remote.transport_params && +      conn->remote.transport_params->max_udp_payload_size) { +    assert(conn->remote.transport_params->max_udp_payload_size >= +           NGTCP2_MAX_UDP_PAYLOAD_SIZE); + +    payloadlen = (size_t)ngtcp2_min_uint64( +      (uint64_t)payloadlen, +      conn->remote.transport_params->max_udp_payload_size); +  } + +  payloadlen = +    ngtcp2_min_size(payloadlen, conn->local.settings.max_tx_udp_payload_size); + +  if (conn->local.settings.no_tx_udp_payload_size_shaping) { +    return payloadlen; +  } + +  return ngtcp2_min_size(payloadlen, dcid->max_udp_payload_size); +} + +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * 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; + +  if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { +    rv = conn_call_path_validation(conn, pv, +                                   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, ts); +  } + +  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, +                                              ngtcp2_pkt_info *pi, +                                              uint8_t *dest, size_t destlen, +                                              ngtcp2_tstamp ts) { +  ngtcp2_ssize nwrite; +  ngtcp2_tstamp expiry; +  ngtcp2_pv *pv = conn->pv; +  ngtcp2_frame lfr; +  ngtcp2_duration timeout, initial_pto; +  uint8_t flags; +  uint64_t tx_left; +  int rv; + +  if (ngtcp2_pv_validation_timed_out(pv, ts)) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, +                    "path validation was timed out"); +    rv = conn_on_path_validation_failed(conn, pv, ts); +    if (rv != 0) { +      return rv; +    } + +    /* We might set path to the one which we just failed validate. +       Set it to the current path here. */ +    if (path) { +      ngtcp2_path_copy(path, &conn->dcid.current.ps.path); +    } + +    return 0; +  } + +  ngtcp2_pv_handle_entry_expiry(pv, ts); + +  if (!ngtcp2_pv_should_send_probe(pv)) { +    return 0; +  } + +  rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data); +  if (rv != 0) { +    return rv; +  } + +  lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; + +  initial_pto = conn_compute_initial_pto(conn, &conn->pktns); +  timeout = conn_compute_pto(conn, &conn->pktns); +  timeout = ngtcp2_max_uint64(timeout, initial_pto); +  expiry = ts + timeout * (1ULL << pv->round); + +  destlen = ngtcp2_min_size(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + +  if (conn->server) { +    if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +      tx_left = conn_server_tx_left(conn, &pv->dcid); +      destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, tx_left); +      if (destlen == 0) { +        return 0; +      } +    } + +    if (destlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +      flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED; +    } else { +      flags = NGTCP2_PV_ENTRY_FLAG_NONE; +    } +  } else { +    flags = NGTCP2_PV_ENTRY_FLAG_NONE; +  } + +  ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts); + +  nwrite = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, +    &pv->dcid.cid, &lfr, +    NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING, +    &pv->dcid.ps.path, ts); +  if (nwrite <= 0) { +    return nwrite; +  } + +  if (path) { +    ngtcp2_path_copy(path, &pv->dcid.ps.path); +  } + +  if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) { +    conn->dcid.current.bytes_sent += (uint64_t)nwrite; +  } else { +    pv->dcid.bytes_sent += (uint64_t)nwrite; +  } + +  return nwrite; +} + +/* + * conn_write_path_response writes a packet which includes + * PATH_RESPONSE 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_response(ngtcp2_conn *conn, +                                             ngtcp2_path *path, +                                             ngtcp2_pkt_info *pi, uint8_t *dest, +                                             size_t destlen, ngtcp2_tstamp ts) { +  ngtcp2_pv *pv = conn->pv; +  ngtcp2_path_challenge_entry *pcent = NULL; +  ngtcp2_dcid *dcid = NULL; +  ngtcp2_frame lfr; +  ngtcp2_ssize nwrite; +  int rv; +  uint64_t tx_left; + +  for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) { +    pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0); + +    if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { +      /* Send PATH_RESPONSE from conn_write_pkt. */ +      return 0; +    } + +    if (pv) { +      if (ngtcp2_path_eq(&pv->dcid.ps.path, &pcent->ps.path)) { +        dcid = &pv->dcid; +        break; +      } +      if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && +          ngtcp2_path_eq(&pv->fallback_dcid.ps.path, &pcent->ps.path)) { +        dcid = &pv->fallback_dcid; +        break; +      } +    } + +    if (conn->server) { +      break; +    } + +    /* Client does not expect to respond to path validation against +       unknown path */ +    ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); +    pcent = NULL; +  } + +  if (pcent == NULL) { +    return 0; +  } + +  if (dcid == NULL) { +    /* client is expected to have |path| in conn->dcid.current or +       conn->pv. */ +    assert(conn->server); + +    rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path, ts); +    if (rv != 0) { +      if (ngtcp2_err_is_fatal(rv)) { +        return rv; +      } +      return 0; +    } +  } + +  destlen = ngtcp2_min_size(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE); + +  if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +    tx_left = conn_server_tx_left(conn, dcid); +    destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, tx_left); +    if (destlen == 0) { +      return 0; +    } +  } + +  lfr.type = NGTCP2_FRAME_PATH_RESPONSE; +  memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); + +  nwrite = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, +    &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts); +  if (nwrite <= 0) { +    return nwrite; +  } + +  if (path) { +    ngtcp2_path_copy(path, &pcent->ps.path); +  } + +  ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb); + +  dcid->bytes_sent += (uint64_t)nwrite; + +  return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(ngtcp2_conn *conn, +                                             ngtcp2_path *path, +                                             int pkt_info_version, +                                             ngtcp2_pkt_info *pi, uint8_t *dest, +                                             size_t destlen, ngtcp2_tstamp ts) { +  return ngtcp2_conn_writev_stream_versioned( +    conn, path, pkt_info_version, pi, dest, destlen, +    /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE, +    /* stream_id = */ -1, +    /* datav = */ NULL, /* datavcnt = */ 0, ts); +} + +/* + * conn_on_version_negotiation is called when Version Negotiation + * packet is received.  The function decodes the data in the buffer + * pointed by |payload| whose length is |payloadlen| as Version + * Negotiation packet payload.  The packet header is given in |hd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + * NGTCP2_ERR_INVALID_ARGUMENT + *     Packet payload is badly formatted. + */ +static int conn_on_version_negotiation(ngtcp2_conn *conn, +                                       const ngtcp2_pkt_hd *hd, +                                       const uint8_t *payload, +                                       size_t payloadlen) { +  uint32_t sv[16]; +  uint32_t *p; +  int rv = 0; +  size_t nsv; +  size_t i; + +  if (payloadlen % sizeof(uint32_t)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  /* Version Negotiation packet is ignored if client has reacted upon +     Version Negotiation packet. */ +  if (conn->local.settings.original_version != conn->client_chosen_version) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (payloadlen > sizeof(sv)) { +    p = ngtcp2_mem_malloc(conn->mem, payloadlen); +    if (p == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +  } else { +    p = sv; +  } + +  nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen); + +  ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); + +  ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv); + +  if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) { +    for (i = 0; i < nsv; ++i) { +      if (p[i] == conn->local.settings.original_version) { +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                        "ignore Version Negotiation because it contains the " +                        "original version"); + +        rv = NGTCP2_ERR_INVALID_ARGUMENT; +        goto fin; +      } +    } +  } + +  rv = conn_call_recv_version_negotiation(conn, hd, p, nsv); +  if (rv != 0) { +    goto fin; +  } + +fin: +  if (p != sv) { +    ngtcp2_mem_free(conn->mem, p); +  } + +  return rv; +} + +static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { +  ngtcp2_strm *strm; + +  if (ngtcp2_pq_empty(&conn->tx.strmq)) { +    return 0; +  } + +  strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); +  return strm->cycle; +} + +uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { +  ngtcp2_strm *strm; + +  if (ngtcp2_pq_empty(&conn->tx.strmq)) { +    return 0; +  } + +  strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); +  return strm->cycle; +} + +/* + * 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, +                         ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_pkt_retry retry; +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_rtb *rtb = &conn->pktns.rtb; +  ngtcp2_rtb *in_rtb; +  uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; +  uint8_t *token; + +  if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { +    return 0; +  } + +  in_rtb = &in_pktns->rtb; + +  rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen); +  if (rv != 0) { +    return rv; +  } + +  retry.odcid = conn->dcid.current.cid; + +  rv = ngtcp2_pkt_verify_retry_tag( +    conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt, +    &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx); +  if (rv != 0) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                    "unable to verify Retry packet integrity"); +    return rv; +  } + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s", +                  (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data, +                                                  retry.odcid.datalen)); + +  if (retry.tokenlen == 0) { +    return NGTCP2_ERR_PROTO; +  } + +  if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) { +    return 0; +  } + +  ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry); + +  /* DCID must be updated before invoking callback because client +     generates new initial keys there. */ +  conn->dcid.current.cid = hd->scid; +  conn->retry_scid = hd->scid; + +  conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + +  rv = conn_call_recv_retry(conn, hd); +  if (rv != 0) { +    return rv; +  } + +  conn->state = NGTCP2_CS_CLIENT_INITIAL; + +  /* Just freeing memory is dangerous because we might free twice. */ + +  rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat); +  if (rv != 0) { +    return rv; +  } + +  ngtcp2_mem_free(conn->mem, (uint8_t *)conn->local.settings.token); +  conn->local.settings.token = NULL; +  conn->local.settings.tokenlen = 0; + +  token = ngtcp2_mem_malloc(conn->mem, retry.tokenlen); +  if (token == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  ngtcp2_cpymem(token, retry.token, retry.tokenlen); + +  conn->local.settings.token = token; +  conn->local.settings.tokenlen = retry.tokenlen; + +  reset_conn_stat_recovery(&conn->cstat); +  conn_reset_congestion_state(conn, ts); +  conn_reset_ecn_validation_state(conn); + +  return 0; +} + +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                                ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { +  return ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, ts); +} + +/* + * 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_ssize num_acked; +  ngtcp2_conn_stat *cstat = &conn->cstat; + +  if (pktns->tx.last_pkt_num < fr->largest_ack) { +    return NGTCP2_ERR_PROTO; +  } + +  rv = ngtcp2_pkt_validate_ack(fr, conn->local.settings.initial_pkt_num); +  if (rv != 0) { +    return rv; +  } + +  ngtcp2_acktr_recv_ack(&pktns->acktr, fr); + +  num_acked = +    ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, pkt_ts, ts); +  if (num_acked < 0) { +    assert(ngtcp2_err_is_fatal((int)num_acked)); +    return (int)num_acked; +  } + +  if (num_acked == 0) { +    return 0; +  } + +  pktns->rtb.probe_pkt_left = 0; + +  if (cstat->pto_count && +      (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { +    /* Reset PTO count but no less than 2 to avoid frequent probe +       packet transmission. */ +    cstat->pto_count = ngtcp2_min_size(cstat->pto_count, 2); +  } + +  ngtcp2_conn_set_loss_detection_timer(conn, ts); + +  return 0; +} + +/* + * conn_assign_recved_ack_delay_unscaled assigns + * fr->ack_delay_unscaled. + */ +static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr, +                                             uint64_t ack_delay_exponent) { +  fr->ack_delay_unscaled = +    fr->ack_delay * (1ULL << 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_objalloc_strm_get(&conn->strm_objalloc); +    if (strm == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +    rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); +    if (rv != 0) { +      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); +      return rv; +    } + +    rv = conn_call_stream_open(conn, strm); +    if (rv != 0) { +      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_uint64(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 ngtcp2_pkt_info *pi, +                           const uint8_t *pkt, size_t pktlen, size_t dgramlen, +                           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, pi, pkt, pktlen, dgramlen, ts, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  *ppc = pc; + +  return 0; +} + +static int ensure_decrypt_buffer(ngtcp2_vec *vec, size_t n, size_t initial, +                                 const ngtcp2_mem *mem) { +  uint8_t *nbuf; +  size_t len; + +  if (vec->len >= n) { +    return 0; +  } + +  len = vec->len == 0 ? initial : vec->len * 2; +  for (; len < n; len *= 2) +    ; +  nbuf = ngtcp2_mem_realloc(mem, vec->base, len); +  if (nbuf == NULL) { +    return NGTCP2_ERR_NOMEM; +  } +  vec->base = nbuf; +  vec->len = len; + +  return 0; +} + +/* + * conn_ensure_decrypt_hp_buffer ensures that + * conn->crypto.decrypt_hp_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_hp_buffer(ngtcp2_conn *conn, size_t n) { +  return ensure_decrypt_buffer(&conn->crypto.decrypt_hp_buf, n, 256, conn->mem); +} + +/* + * 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) { +  return ensure_decrypt_buffer(&conn->crypto.decrypt_buf, n, 2048, conn->mem); +} + +/* + * 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 |aad| is the Additional + * Authenticated Data, and its length is |aadlen|.  |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_DECRYPT + *     Failed to decrypt a packet. + */ +static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                                const uint8_t *payload, size_t payloadlen, +                                const uint8_t *aad, size_t aadlen, +                                int64_t pkt_num, ngtcp2_crypto_km *ckm, +                                ngtcp2_decrypt decrypt) { +  /* TODO nonce is limited to 64 bytes. */ +  uint8_t nonce[64]; +  int rv; + +  assert(sizeof(nonce) >= ckm->iv.len); + +  ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); + +  rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce, +               ckm->iv.len, aad, aadlen); + +  if (rv != 0) { +    if (rv == NGTCP2_ERR_DECRYPT) { +      return rv; +    } +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  assert(payloadlen >= aead->max_overhead); + +  return (ngtcp2_ssize)(payloadlen - aead->max_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, const ngtcp2_crypto_cipher *hp, +           const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset, +           const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) { +  size_t sample_offset; +  uint8_t *p = dest; +  uint8_t mask[NGTCP2_HP_SAMPLELEN]; +  size_t i; +  int rv; + +  assert(hp_mask); + +  if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { +    return NGTCP2_ERR_PROTO; +  } + +  p = ngtcp2_cpymem(p, pkt, pkt_num_offset); + +  sample_offset = pkt_num_offset + 4; + +  rv = hp_mask(mask, hp, hp_ctx, pkt + sample_offset); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { +    dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f)); +  } else { +    dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f)); +    if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) { +      hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE; +    } +  } + +  hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); + +  for (i = 0; i < hd->pkt_numlen; ++i) { +    *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; +  } + +  hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); + +  return p - dest; +} + +/* + * conn_emit_pending_crypto_data delivers pending stream data to the + * application due to packet reordering. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + *     User callback failed + * NGTCP2_ERR_CRYPTO + *     TLS backend reported error + */ +static int +conn_emit_pending_crypto_data(ngtcp2_conn *conn, +                              ngtcp2_encryption_level encryption_level, +                              ngtcp2_strm *strm, uint64_t rx_offset) { +  size_t datalen; +  const uint8_t *data; +  int rv; +  uint64_t offset; + +  if (!strm->rx.rob) { +    return 0; +  } + +  for (;;) { +    datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); +    if (datalen == 0) { +      assert(rx_offset == ngtcp2_strm_rx_offset(strm)); +      return 0; +    } + +    offset = rx_offset; +    rx_offset += datalen; + +    rv = +      conn_call_recv_crypto_data(conn, encryption_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 int conn_recv_connection_close(ngtcp2_conn *conn, +                                      ngtcp2_connection_close *fr) { +  ngtcp2_ccerr *ccerr = &conn->rx.ccerr; + +  conn->state = NGTCP2_CS_DRAINING; +  if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { +    ccerr->type = NGTCP2_CCERR_TYPE_TRANSPORT; +  } else { +    ccerr->type = NGTCP2_CCERR_TYPE_APPLICATION; +  } +  ccerr->error_code = fr->error_code; +  ccerr->frame_type = fr->frame_type; + +  if (!fr->reasonlen) { +    ccerr->reasonlen = 0; + +    return 0; +  } + +  if (ccerr->reason == NULL) { +    ccerr->reason = ngtcp2_mem_malloc(conn->mem, NGTCP2_CCERR_MAX_REASONLEN); +    if (ccerr->reason == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +  } + +  ccerr->reasonlen = ngtcp2_min_size(fr->reasonlen, NGTCP2_CCERR_MAX_REASONLEN); +  ngtcp2_cpymem((uint8_t *)ccerr->reason, fr->reason, ccerr->reasonlen); + +  return 0; +} + +static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path, +                                     ngtcp2_path_challenge *fr) { +  ngtcp2_path_challenge_entry *ent; + +  /* client only responds to PATH_CHALLENGE from the current path or +     path which client is migrating to. */ +  if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && +      (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "discard PATH_CHALLENGE from the path which is not current " +                    "or endpoint is migrating to"); +    return; +  } + +  ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb); +  ngtcp2_path_challenge_entry_init(ent, path, fr->data); +} + +/* + * conn_reset_congestion_state resets congestion state. + */ +static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  conn_reset_conn_stat_cc(conn, &conn->cstat); + +  if (conn->cc.reset) { +    conn->cc.reset(&conn->cc, &conn->cstat, ts); +  } + +  if (conn->hs_pktns) { +    ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb, +                              conn->hs_pktns->tx.last_pkt_num + 1); +  } +  ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1); +  ngtcp2_rst_init(&conn->rst); + +  conn->tx.pacing.next_ts = UINT64_MAX; +} + +static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, +                                   ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_pv *pv = conn->pv, *npv; +  uint8_t ent_flags; + +  if (!pv) { +    return 0; +  } + +  rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data); +  if (rv != 0) { +    assert(!ngtcp2_err_is_fatal(rv)); + +    return 0; +  } + +  if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { +    if (pv->dcid.seq != conn->dcid.current.seq) { +      assert(!conn->server); +      assert(conn->dcid.current.cid.datalen); + +      rv = conn_retire_dcid(conn, &conn->dcid.current, ts); +      if (rv != 0) { +        return rv; +      } +      ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); + +      conn_reset_congestion_state(conn, ts); +      conn_reset_ecn_validation_state(conn); +    } + +    assert(ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)); + +    conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + +    if (!conn->local.settings.no_pmtud) { +      ngtcp2_conn_stop_pmtud(conn); + +      if (!(ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) { +        rv = conn_start_pmtud(conn); +        if (rv != 0) { +          return rv; +        } +      } +    } + +    if (!(ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) { +      rv = conn_call_path_validation(conn, pv, +                                     NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); +      if (rv != 0) { +        return rv; +      } +    } +  } + +  if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { +    if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) { +      assert(conn->server); + +      /* Validate path again */ +      rv = ngtcp2_pv_new(&npv, &pv->dcid, conn_compute_pv_timeout(conn), +                         NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, &conn->log, +                         conn->mem); +      if (rv != 0) { +        return rv; +      } + +      npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; +      ngtcp2_dcid_copy(&npv->fallback_dcid, &pv->fallback_dcid); +      npv->fallback_pto = pv->fallback_pto; +    } else { +      rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, +                         conn_compute_pv_timeout_pto(conn, pv->fallback_pto), +                         NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); +      if (rv != 0) { +        return rv; +      } +    } + +    /* Unset the flag bit so that conn_stop_pv does not retire +       DCID. */ +    pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; + +    rv = conn_stop_pv(conn, ts); +    if (rv != 0) { +      ngtcp2_pv_del(npv); +      return rv; +    } + +    conn->pv = npv; + +    return 0; +  } + +  return conn_stop_pv(conn, ts); +} + +/* + * 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 ack_eliciting, ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_range r; + +  rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1); +  if (rv != 0) { +    return rv; +  } + +  if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) { +    ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap); +  } + +  if (ack_eliciting) { +    if (pktns->rx.max_ack_eliciting_pkt_num != -1) { +      if (pkt_num < pktns->rx.max_ack_eliciting_pkt_num) { +        ngtcp2_acktr_immediate_ack(&pktns->acktr); +      } else if (pkt_num != pktns->rx.max_ack_eliciting_pkt_num + 1) { +        r = ngtcp2_gaptr_get_first_gap_after( +          &pktns->rx.pngap, (uint64_t)pktns->rx.max_ack_eliciting_pkt_num); + +        if (r.begin < (uint64_t)pkt_num) { +          ngtcp2_acktr_immediate_ack(&pktns->acktr); +        } +      } +    } + +    if (pktns->rx.max_ack_eliciting_pkt_num < pkt_num) { +      pktns->rx.max_ack_eliciting_pkt_num = pkt_num; +    } +  } + +  if (pktns->rx.max_pkt_num < pkt_num) { +    pktns->rx.max_pkt_num = pkt_num; +    pktns->rx.max_pkt_ts = ts; +  } + +  return 0; +} + +/* + * verify_token verifies |hd| contains |token| in its token field.  It + * returns 0 if it succeeds, or NGTCP2_ERR_PROTO. + */ +static int verify_token(const uint8_t *token, size_t tokenlen, +                        const ngtcp2_pkt_hd *hd) { +  if (tokenlen == hd->tokenlen && ngtcp2_cmemeq(token, hd->token, tokenlen)) { +    return 0; +  } +  return NGTCP2_ERR_PROTO; +} + +static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns, +                                      const ngtcp2_pkt_info *pi) { +  switch (pi->ecn & NGTCP2_ECN_MASK) { +  case NGTCP2_ECN_ECT_0: +    ++pktns->rx.ecn.ect0; +    break; +  case NGTCP2_ECN_ECT_1: +    ++pktns->rx.ecn.ect1; +    break; +  case NGTCP2_ECN_CE: +    ++pktns->rx.ecn.ce; +    break; +  } +} + +/* + * vneg_available_versions_includes returns nonzero if + * |available_versions| of length |available_versionslen| includes + * |version|.  |available_versions| is the wire image of + * available_versions field of version_information transport + * parameter, and each version is encoded in network byte order. + */ +static int vneg_available_versions_includes(const uint8_t *available_versions, +                                            size_t available_versionslen, +                                            uint32_t version) { +  size_t i; +  uint32_t v; + +  assert(!(available_versionslen & 0x3)); + +  if (available_versionslen == 0) { +    return 0; +  } + +  for (i = 0; i < available_versionslen; i += sizeof(uint32_t)) { +    available_versions = ngtcp2_get_uint32be(&v, available_versions); + +    if (version == v) { +      return 1; +    } +  } + +  return 0; +} + +/* + * conn_verify_fixed_bit verifies that fixed bit in |hd| is + * acceptable. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + *     Clearing fixed bit is not permitted. + */ +static int conn_verify_fixed_bit(ngtcp2_conn *conn, ngtcp2_pkt_hd *hd) { +  if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { +    return 0; +  } + +  if (conn->server) { +    switch (hd->type) { +    case NGTCP2_PKT_INITIAL: +    case NGTCP2_PKT_0RTT: +    case NGTCP2_PKT_HANDSHAKE: +      /* RFC 9287 requires that a token from NEW_TOKEN. */ +      if (!(conn->flags & NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED) && +          (conn->local.settings.token_type != NGTCP2_TOKEN_TYPE_NEW_TOKEN || +           !conn->local.settings.tokenlen)) { +        return NGTCP2_ERR_INVALID_ARGUMENT; +      } + +      break; +    } +  } + +  /* TODO we have no information that we enabled grease_quic_bit in +     the previous connection. */ +  if (!conn->local.transport_params.grease_quic_bit) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  return 0; +} + +static int conn_recv_crypto(ngtcp2_conn *conn, +                            ngtcp2_encryption_level encryption_level, +                            ngtcp2_strm *strm, const ngtcp2_stream *fr); + +static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, +                                  const ngtcp2_pkt_info *pi, const uint8_t *pkt, +                                  size_t pktlen, size_t dgramlen, +                                  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 ngtcp2_pkt_info *pi, const uint8_t *pkt, +                        size_t pktlen, size_t dgramlen, 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; +  ngtcp2_crypto_aead *aead; +  ngtcp2_crypto_cipher *hp; +  ngtcp2_crypto_km *ckm; +  ngtcp2_crypto_cipher_ctx *hp_ctx; +  ngtcp2_hp_mask hp_mask; +  ngtcp2_decrypt decrypt; +  ngtcp2_pktns *pktns; +  ngtcp2_strm *crypto; +  ngtcp2_encryption_level encryption_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 1RTT packet unless server's first Handshake packet has +         been transmitted. */ +      return (ngtcp2_ssize)pktlen; +    } + +    if (conn->pktns.crypto.rx.ckm) { +      return 0; +    } + +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "buffering 1RTT packet len=%zu", pktlen); + +    rv = +      conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, 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_INITIAL_PKT_PROCESSED) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (!ngtcp2_cid_eq(&conn->oscid, &hd.dcid)) { +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                      "packet was ignored because of mismatched DCID"); +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { +      /* Just discard invalid Version Negotiation packet */ +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                      "packet was ignored because of mismatched SCID"); +      return NGTCP2_ERR_DISCARD_PKT; +    } +    rv = +      conn_on_version_negotiation(conn, &hd, pkt + hdpktlen, pktlen - hdpktlen); +    if (rv != 0) { +      if (ngtcp2_err_is_fatal(rv)) { +        return rv; +      } +      return NGTCP2_ERR_DISCARD_PKT; +    } +    return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; +  case NGTCP2_PKT_RETRY: +    hdpktlen = (size_t)nread; + +    ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + +    if (conn->server) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (conn_verify_fixed_bit(conn, &hd) != 0) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    /* Receiving Retry packet after getting Initial packet from server +       is invalid. */ +    if (conn->flags & NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (conn->client_chosen_version != hd.version) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts); +    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 (!ngtcp2_is_supported_version(hd.version)) { +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  if (conn->server) { +    if (hd.version != conn->client_chosen_version && +        (!conn->negotiated_version || hd.version != conn->negotiated_version)) { +      return NGTCP2_ERR_DISCARD_PKT; +    } +  } else if (hd.version != conn->client_chosen_version && +             conn->negotiated_version && +             hd.version != conn->negotiated_version) { +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  if (conn_verify_fixed_bit(conn, &hd) != 0) { +    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_INITIAL_PKT_PROCESSED) && +      !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 (hd.version != conn->client_chosen_version) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (conn->flags & NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED) { +      if (conn->early.ckm) { +        ngtcp2_ssize nread2; +        /* TODO Avoid to parse header twice. */ +        nread2 = +          conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, 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, pi, pkt, pktlen, dgramlen, +                         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 (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +        ngtcp2_log_info( +          &conn->log, NGTCP2_LOG_EVENT_PKT, +          "Initial packet was ignored because it is included in UDP datagram " +          "less than %zu bytes: %zu bytes", +          NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen); +        return NGTCP2_ERR_DISCARD_PKT; +      } +      if (conn->local.settings.tokenlen) { +        rv = verify_token(conn->local.settings.token, +                          conn->local.settings.tokenlen, &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_INITIAL_PKT_PROCESSED) == 0) { +        /* Set rcid here so that it is available to callback.  If this +           packet is discarded later in this function and no packet is +           processed in this connection attempt so far, connection +           will be dropped. */ +        conn->rcid = hd.dcid; + +        rv = conn_call_recv_client_initial(conn, &hd.dcid); +        if (rv != 0) { +          return rv; +        } +      } +    } else { +      if (hd.tokenlen != 0) { +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                        "packet was ignored because token is not empty"); +        return NGTCP2_ERR_DISCARD_PKT; +      } + +      if (hd.version != conn->client_chosen_version && +          !conn->negotiated_version && conn->vneg.version != hd.version) { +        if (!vneg_available_versions_includes(conn->vneg.available_versions, +                                              conn->vneg.available_versionslen, +                                              hd.version)) { +          return NGTCP2_ERR_DISCARD_PKT; +        } + +        /* Install new Initial keys using QUIC version = hd.version */ +        rv = conn_call_version_negotiation( +          conn, hd.version, +          (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) ? &conn->dcid.current.cid +                                                      : &conn->rcid); +        if (rv != 0) { +          return rv; +        } + +        assert(conn->vneg.version == hd.version); +      } +    } + +    pktns = conn->in_pktns; +    crypto = &pktns->crypto.strm; +    encryption_level = NGTCP2_ENCRYPTION_LEVEL_INITIAL; + +    if (hd.version == conn->client_chosen_version) { +      ckm = pktns->crypto.rx.ckm; +      hp_ctx = &pktns->crypto.rx.hp_ctx; +    } else { +      assert(conn->vneg.version == hd.version); + +      ckm = conn->vneg.rx.ckm; +      hp_ctx = &conn->vneg.rx.hp_ctx; +    } + +    break; +  case NGTCP2_PKT_HANDSHAKE: +    if (hd.version != conn->negotiated_version) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    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, pi, pkt, pktlen, +                           dgramlen, ts); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        return rv; +      } +      return (ngtcp2_ssize)pktlen; +    } + +    pktns = conn->hs_pktns; +    crypto = &pktns->crypto.strm; +    encryption_level = NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE; +    ckm = pktns->crypto.rx.ckm; +    hp_ctx = &pktns->crypto.rx.hp_ctx; + +    break; +  default: +    ngtcp2_unreachable(); +  } + +  hp_mask = conn->callbacks.hp_mask; +  decrypt = conn->callbacks.decrypt; +  aead = &pktns->crypto.ctx.aead; +  hp = &pktns->crypto.ctx.hp; + +  assert(ckm); +  assert(hp_mask); +  assert(decrypt); + +  rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4); +  if (rv != 0) { +    return rv; +  } + +  nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, +                      (size_t)nread, hp_ctx, hp_mask); +  if (nwrite < 0) { +    if (ngtcp2_err_is_fatal((int)nwrite)) { +      return nwrite; +    } +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                    "could not decrypt packet number"); +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  hdpktlen = (size_t)nwrite; +  payload = pkt + hdpktlen; +  payloadlen = hd.len - hd.pkt_numlen; + +  hd.pkt_num = +    ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, 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(conn->crypto.decrypt_hp_buf.base[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, +                       conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num, +                       ckm, decrypt); +  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; +  } + +  if (!conn->server && hd.version != conn->client_chosen_version && +      !conn->negotiated_version) { +    conn->negotiated_version = hd.version; + +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "the negotiated version is 0x%08x", +                    conn->negotiated_version); +  } + +  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_INITIAL_PKT_PROCESSED) && +         !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: +    ngtcp2_unreachable(); +  } + +  if (payloadlen == 0) { +    /* QUIC packet must contain at least one frame */ +    if (hd.type == NGTCP2_PKT_INITIAL) { +      return NGTCP2_ERR_DISCARD_PKT; +    } +    return NGTCP2_ERR_PROTO; +  } + +  if (hd.type == NGTCP2_PKT_INITIAL && +      !(conn->flags & NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED)) { +    conn->flags |= NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED; +    if (!conn->server) { +      conn->dcid.current.cid = hd.scid; +    } +  } + +  ngtcp2_qlog_pkt_received_start(&conn->qlog); + +  for (; payloadlen;) { +    nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); +    if (nread < 0) { +      return nread; +    } + +    payload += nread; +    payloadlen -= (size_t)nread; + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      fr->ack.ack_delay = 0; +      fr->ack.ack_delay_unscaled = 0; +      break; +    } + +    ngtcp2_log_rx_fr(&conn->log, &hd, fr); + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { +        conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; +      } +      rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGTCP2_FRAME_PADDING: +      break; +    case NGTCP2_FRAME_CRYPTO: +      if (!conn->server && !conn->negotiated_version && +          ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt)) { +        conn->negotiated_version = hd.version; + +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                        "the negotiated version is 0x%08x", +                        conn->negotiated_version); +      } + +      rv = conn_recv_crypto(conn, encryption_level, crypto, &fr->stream); +      if (rv != 0) { +        return rv; +      } +      require_ack = 1; +      break; +    case NGTCP2_FRAME_CONNECTION_CLOSE: +      rv = conn_recv_connection_close(conn, &fr->connection_close); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGTCP2_FRAME_PING: +      require_ack = 1; +      break; +    default: +      return NGTCP2_ERR_PROTO; +    } + +    ngtcp2_qlog_write_frame(&conn->qlog, fr); +  } + +  if (hd.type == NGTCP2_PKT_HANDSHAKE) { +    /* Successful processing of Handshake packet from a remote +       endpoint validates its source address. */ +    conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; +  } + +  ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); + +  rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  pktns_increase_ecn_counts(pktns, pi); + +  /* Initial and Handshake are always acknowledged without delay.  No +     need to call ngtcp2_acktr_immediate_ack(). */ +  rv = +    ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  conn_restart_timer_on_read(conn, ts); + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING +                                           : (ngtcp2_ssize)pktlen; +} + +static int is_unrecoverable_error(int liberr) { +  switch (liberr) { +  case NGTCP2_ERR_CRYPTO: +  case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: +  case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: +  case NGTCP2_ERR_TRANSPORT_PARAM: +  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: +    return 1; +  } + +  return 0; +} + +/* + * conn_recv_handshake_cpkt processes compound packet during + * handshake.  The buffer pointed by |pkt| might contain multiple + * packets.  The 1RTT 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 ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, +                                             const ngtcp2_path *path, +                                             const ngtcp2_pkt_info *pi, +                                             const uint8_t *pkt, size_t pktlen, +                                             ngtcp2_tstamp ts) { +  ngtcp2_ssize nread; +  size_t dgramlen = pktlen; +  const uint8_t *origpkt = pkt; +  uint32_t version; + +  if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +    conn->dcid.current.bytes_recv += dgramlen; +  } + +  while (pktlen) { +    nread = +      conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); +    if (nread < 0) { +      if (ngtcp2_err_is_fatal((int)nread)) { +        return nread; +      } + +      if (nread == NGTCP2_ERR_DRAINING) { +        return NGTCP2_ERR_DRAINING; +      } + +      if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) { +        /* Not a Version Negotiation packet */ +        ngtcp2_get_uint32be(&version, &pkt[1]); +        if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) { +          if (conn->server) { +            if (is_unrecoverable_error((int)nread)) { +              /* If server gets crypto error from TLS stack, it is +                 unrecoverable, therefore drop connection. */ +              return nread; +            } + +            /* If server discards first Initial, then drop connection +               state.  This is because SCID in packet might be corrupted +               and the current connection state might wrongly discard +               valid packet and prevent the handshake from +               completing. */ +            if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { +              return NGTCP2_ERR_DROP_CONN; +            } + +            return (ngtcp2_ssize)dgramlen; +          } +          /* client */ +          if (is_unrecoverable_error((int)nread)) { +            /* If client gets crypto error from TLS stack, it is +               unrecoverable, therefore drop connection. */ +            return nread; +          } +          return (ngtcp2_ssize)dgramlen; +        } +      } + +      if (nread == NGTCP2_ERR_DISCARD_PKT) { +        return (ngtcp2_ssize)dgramlen; +      } + +      return nread; +    } + +    if (nread == 0) { +      assert(!(pkt[0] & NGTCP2_HEADER_FORM_BIT)); +      return pkt - origpkt; +    } + +    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 (ngtcp2_ssize)dgramlen; +} + +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); + +  assert(conn->remote.transport_params); + +  if (bidi_stream(stream_id)) { +    if (local_stream) { +      max_rx_offset = +        conn->local.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.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.transport_params.initial_max_stream_data_uni; +    max_tx_offset = 0; +  } + +  ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, +                   max_tx_offset, stream_user_data, &conn->frc_objalloc, +                   conn->mem); + +  rv = +    ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id, strm); +  if (rv != 0) { +    assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); +    goto fail; +  } + +  return 0; + +fail: +  ngtcp2_strm_free(strm); +  return rv; +} + +/* + * conn_emit_pending_stream_data passes buffered ordered stream data + * to the application.  |rx_offset| is the first offset to deliver to + * the application.  This function assumes that the data up to + * |rx_offset| has been delivered already.  This function only passes + * the ordered data without any gap.  If there is a gap, it stops + * providing the data to the application, and returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + *     User callback failed. + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, +                                         uint64_t rx_offset) { +  size_t datalen; +  const uint8_t *data; +  int rv; +  uint64_t offset; +  uint32_t sdflags; +  int handshake_completed = conn_is_tls_handshake_completed(conn); + +  if (!strm->rx.rob) { +    return 0; +  } + +  for (;;) { +    /* Stop calling callback if application has called +       ngtcp2_conn_shutdown_stream_read() inside the callback. +       Because it doubly counts connection window. */ +    if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { +      return 0; +    } + +    datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); +    if (datalen == 0) { +      assert(rx_offset == ngtcp2_strm_rx_offset(strm)); +      return 0; +    } + +    offset = rx_offset; +    rx_offset += datalen; + +    sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; +    if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && +        rx_offset == strm->rx.last_offset) { +      sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; +    } +    if (!handshake_completed) { +      sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; +    } + +    rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, datalen); +    if (rv != 0) { +      return rv; +    } + +    /* ngtcp2_conn_shutdown_stream_read from a callback will free +       strm->rx.rob. */ +    if (!strm->rx.rob) { +      return 0; +    } + +    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. + * |encryption_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_encryption_level encryption_level, +                            ngtcp2_strm *crypto, const ngtcp2_stream *fr) { +  uint64_t fr_end_offset; +  uint64_t rx_offset; +  int rv; + +  if (fr->datacnt == 0) { +    return 0; +  } + +  fr_end_offset = fr->offset + fr->data[0].len; + +  if (NGTCP2_MAX_VARINT < fr_end_offset) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  rx_offset = ngtcp2_strm_rx_offset(crypto); + +  if (fr_end_offset <= rx_offset) { +    if (conn->server && +        !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) && +        encryption_level == NGTCP2_ENCRYPTION_LEVEL_INITIAL) { +      /* https://datatracker.ietf.org/doc/html/rfc9002#section-6.2.3: +         Speeding Up Handshake Completion + +         When a server receives an Initial packet containing duplicate +         CRYPTO data, it can assume the client did not receive all of +         the server's CRYPTO data sent in Initial packets, or the +         client's estimated RTT is too small.  ...  To speed up +         handshake completion under these conditions, an endpoint MAY +         send a packet containing unacknowledged CRYPTO data earlier +         than the PTO expiry, subject to address validation limits; +         ... */ +      conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT; +      conn->in_pktns->rtb.probe_pkt_left = 1; +      conn->hs_pktns->rtb.probe_pkt_left = 1; +    } +    return 0; +  } + +  crypto->rx.last_offset = +    ngtcp2_max_uint64(crypto->rx.last_offset, fr_end_offset); + +  /* TODO Before dispatching incoming data to TLS stack, make sure +     that previous data in previous encryption level has been +     completely sent to TLS stack.  Usually, if data is left, it is an +     error because key is generated after consuming all data in the +     previous encryption level. */ +  if (fr->offset <= rx_offset) { +    size_t ncut = (size_t)(rx_offset - fr->offset); +    const uint8_t *data = fr->data[0].base + ncut; +    size_t datalen = fr->data[0].len - ncut; +    uint64_t offset = rx_offset; + +    rx_offset += datalen; +    ngtcp2_strm_update_rx_offset(crypto, rx_offset); + +    rv = +      conn_call_recv_crypto_data(conn, encryption_level, offset, data, datalen); +    if (rv != 0) { +      return rv; +    } + +    rv = +      conn_emit_pending_crypto_data(conn, encryption_level, crypto, rx_offset); +    if (rv != 0) { +      return rv; +    } + +    return 0; +  } + +  if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) { +    return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED; +  } + +  return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len, +                                     fr->offset); +} + +/* + * conn_max_data_violated returns nonzero if receiving |datalen| + * violates connection flow control on local endpoint. + */ +static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) { +  return conn->rx.max_offset - conn->rx.offset < datalen; +} + +/* + * conn_recv_stream is called when STREAM frame |fr| is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + *     STREAM frame is received for a local stream which is not + *     initiated; or STREAM frame is received for a local + *     unidirectional stream + * NGTCP2_ERR_STREAM_LIMIT + *     STREAM frame has remote stream ID which is strictly greater + *     than the allowed limit. + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + *     Flow control limit is violated; or the end offset of stream + *     data is beyond the NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + *     STREAM frame has strictly larger end offset than it is + *     permitted. + */ +static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { +  int rv; +  ngtcp2_strm *strm; +  ngtcp2_idtr *idtr; +  uint64_t rx_offset, fr_end_offset; +  int local_stream; +  int bidi; +  uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); +  uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; + +  local_stream = conn_local_stream(conn, fr->stream_id); +  bidi = bidi_stream(fr->stream_id); + +  if (bidi) { +    if (local_stream) { +      if (conn->local.bidi.next_stream_id <= fr->stream_id) { +        return NGTCP2_ERR_STREAM_STATE; +      } +    } else if (conn->remote.bidi.max_streams < +               ngtcp2_ord_stream_id(fr->stream_id)) { +      return NGTCP2_ERR_STREAM_LIMIT; +    } + +    idtr = &conn->remote.bidi.idtr; +  } else { +    if (local_stream) { +      return NGTCP2_ERR_STREAM_STATE; +    } +    if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { +      return NGTCP2_ERR_STREAM_LIMIT; +    } + +    idtr = &conn->remote.uni.idtr; +  } + +  if (NGTCP2_MAX_VARINT - datalen < fr->offset) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  strm = ngtcp2_conn_find_stream(conn, fr->stream_id); +  if (strm == NULL) { +    if (local_stream) { +      /* TODO The stream has been closed.  This should be responded +         with RESET_STREAM, or simply ignored. */ +      return 0; +    } + +    rv = ngtcp2_idtr_open(idtr, fr->stream_id); +    if (rv != 0) { +      if (ngtcp2_err_is_fatal(rv)) { +        return rv; +      } +      assert(rv == NGTCP2_ERR_STREAM_IN_USE); +      /* TODO The stream has been closed.  This should be responded +         with RESET_STREAM, or simply ignored. */ +      return 0; +    } + +    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +    if (strm == NULL) { +      return NGTCP2_ERR_NOMEM; +    } + +    rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); +    if (rv != 0) { +      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); +      return rv; +    } + +    if (!bidi) { +      ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); +      strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; +    } + +    rv = conn_call_stream_open(conn, strm); +    if (rv != 0) { +      return rv; +    } +  } + +  fr_end_offset = fr->offset + datalen; + +  if (strm->rx.max_offset < fr_end_offset) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  if (strm->rx.last_offset < fr_end_offset) { +    uint64_t len = fr_end_offset - strm->rx.last_offset; + +    if (conn_max_data_violated(conn, len)) { +      return NGTCP2_ERR_FLOW_CONTROL; +    } + +    conn->rx.offset += len; + +    if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { +      ngtcp2_conn_extend_max_offset(conn, len); +    } +  } + +  rx_offset = ngtcp2_strm_rx_offset(strm); + +  if (fr->fin) { +    if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) { +      if (strm->rx.last_offset != fr_end_offset) { +        return NGTCP2_ERR_FINAL_SIZE; +      } + +      if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { +        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); +    } +  } 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_uint64(strm->rx.last_offset, fr_end_offset); + +    if (fr_end_offset <= rx_offset) { +      return 0; +    } + +    if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { +      return 0; +    } +  } + +  if (fr->offset <= rx_offset) { +    size_t ncut = (size_t)(rx_offset - fr->offset); +    uint64_t offset = rx_offset; +    const uint8_t *data; +    int fin; + +    if (fr->datacnt) { +      data = fr->data[0].base + ncut; +      datalen -= ncut; + +      rx_offset += datalen; +      ngtcp2_strm_update_rx_offset(strm, rx_offset); +    } else { +      data = NULL; +      datalen = 0; +    } + +    if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { +      return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +    } + +    fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && +          rx_offset == strm->rx.last_offset; + +    if (fin || datalen) { +      if (fin) { +        sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; +      } +      if (!conn_is_tls_handshake_completed(conn)) { +        sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; +      } +      rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, +                                      (size_t)datalen); +      if (rv != 0) { +        return rv; +      } + +      rv = conn_emit_pending_stream_data(conn, strm, rx_offset); +      if (rv != 0) { +        return rv; +      } +    } +  } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { +    rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, +                                     fr->offset); +    if (rv != 0) { +      return rv; +    } +  } +  return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +} + +/* + * 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) { +  strm->flags |= NGTCP2_STRM_FLAG_SEND_RESET_STREAM; +  strm->tx.reset_stream_app_error_code = app_error_code; + +  if (ngtcp2_strm_is_tx_queued(strm)) { +    return 0; +  } + +  strm->cycle = conn_tx_strmq_first_cycle(conn); + +  return ngtcp2_conn_tx_strmq_push(conn, strm); +} + +/* + * 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) { +  strm->flags |= NGTCP2_STRM_FLAG_SEND_STOP_SENDING; +  strm->tx.stop_sending_app_error_code = app_error_code; + +  if (ngtcp2_strm_is_tx_queued(strm)) { +    return 0; +  } + +  strm->cycle = conn_tx_strmq_first_cycle(conn); + +  return ngtcp2_conn_tx_strmq_push(conn, strm); +} + +/* + * handle_max_remote_streams_extension extends + * |*punsent_max_remote_streams| by |n| if a condition allows it. + */ +static void +handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, +                                    size_t n) { +  if ( +#if SIZE_MAX > UINT32_MAX +    NGTCP2_MAX_STREAMS < n || +#endif /* SIZE_MAX > UINT32_MAX */ +    *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) { +    *punsent_max_remote_streams = NGTCP2_MAX_STREAMS; +  } else { +    *punsent_max_remote_streams += n; +  } +} + +/* + * conn_recv_reset_stream is called when RESET_STREAM |fr| is + * received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + *     RESET_STREAM frame is received to the local stream which is not + *     initiated. + * NGTCP2_ERR_STREAM_LIMIT + *     RESET_STREAM frame has remote stream ID which is strictly + *     greater than the allowed limit. + * NGTCP2_ERR_PROTO + *     RESET_STREAM frame is received to the local unidirectional + *     stream + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + *     Flow control limit is violated; or the final size is beyond the + *     NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + *     The final offset is strictly larger than it is permitted. + */ +static int conn_recv_reset_stream(ngtcp2_conn *conn, +                                  const ngtcp2_reset_stream *fr) { +  ngtcp2_strm *strm; +  int local_stream = conn_local_stream(conn, fr->stream_id); +  int bidi = bidi_stream(fr->stream_id); +  uint64_t datalen; +  ngtcp2_idtr *idtr; +  int rv; + +  /* TODO share this piece of code */ +  if (bidi) { +    if (local_stream) { +      if (conn->local.bidi.next_stream_id <= fr->stream_id) { +        return NGTCP2_ERR_STREAM_STATE; +      } +    } else if (conn->remote.bidi.max_streams < +               ngtcp2_ord_stream_id(fr->stream_id)) { +      return NGTCP2_ERR_STREAM_LIMIT; +    } + +    idtr = &conn->remote.bidi.idtr; +  } else { +    if (local_stream) { +      return NGTCP2_ERR_PROTO; +    } +    if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { +      return NGTCP2_ERR_STREAM_LIMIT; +    } + +    idtr = &conn->remote.uni.idtr; +  } + +  if (NGTCP2_MAX_VARINT < fr->final_size) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  strm = ngtcp2_conn_find_stream(conn, fr->stream_id); +  if (strm == NULL) { +    if (local_stream) { +      return 0; +    } + +    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; +    } + +    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; +    } + +    /* Stream is reset before we create ngtcp2_strm object. */ +    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +    if (strm == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +    rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); +    if (rv != 0) { +      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); +      return rv; +    } + +    rv = conn_call_stream_open(conn, strm); +    if (rv != 0) { +      return rv; +    } +  } + +  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; +  } + +  if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { +    return 0; +  } + +  if (strm->rx.max_offset < fr->final_size) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  datalen = fr->final_size - strm->rx.last_offset; + +  if (conn_max_data_violated(conn, datalen)) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  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_RESET_STREAM_RECVED; + +  ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + +  return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +} + +/* + * 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; +    } + +    /* STOP_SENDING frame is received before we create ngtcp2_strm +       object. */ +    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +    if (strm == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +    rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); +    if (rv != 0) { +      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); +      return rv; +    } + +    rv = conn_call_stream_open(conn, strm); +    if (rv != 0) { +      return rv; +    } +  } + +  if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING_RECVED) { +    return 0; +  } + +  ngtcp2_strm_set_app_error_code(strm, fr->app_error_code); + +  /* No RESET_STREAM is required if we have sent FIN and all data have +     been acknowledged. */ +  if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) && +      !(strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM)) { +    strm->flags |= NGTCP2_STRM_FLAG_RESET_STREAM; + +    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_STOP_SENDING_RECVED; + +  ngtcp2_strm_streamfrq_clear(strm); + +  return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +} + +/* + * 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_dcid_verify_stateless_reset_token( +           dcid, 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.rb); +    for (i = 0; i < len; ++i) { +      dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); +      if (check_stateless_reset(dcid, path, &sr)) { +        break; +      } +    } + +    if (i == len) { +      len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); +      for (i = 0; i < len; ++i) { +        dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 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); + +  ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr); + +  return conn_call_recv_stateless_reset(conn, &sr); +} + +/* + * 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_uint64(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); +      continue; +    } + +    if (i == ngtcp2_ringbuf_len(rb) - 1) { +      ngtcp2_ringbuf_pop_back(rb); +      break; +    } + +    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.bound.rb); + +  for (i = 0; i < len; ++i) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 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; +    } +  } + +  len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); + +  for (i = 0; i < len; ++i) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 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.bound.rb, +                                   fr->retire_prior_to); +    if (rv != 0) { +      return rv; +    } + +    rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb, +                                   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. */ +    return conn_retire_dcid_seq(conn, fr->seq); +  } + +  if (found) { +    return 0; +  } + +  if (ngtcp2_gaptr_is_pushed(&conn->dcid.seqgap, fr->seq, 1)) { +    return 0; +  } + +  rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, fr->seq, 1); +  if (rv != 0) { +    return rv; +  } + +  if (ngtcp2_ksl_len(&conn->dcid.seqgap.gap) > 32) { +    ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); +  } + +  len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); + +  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.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.rb); +  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.rb) == 0) { +      return 0; +    } + +    rv = conn_retire_dcid(conn, &conn->dcid.current, ts); +    if (rv != 0) { +      return rv; +    } + +    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +    if (pv) { +      if (conn->dcid.current.seq == pv->dcid.seq) { +        ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); +      } +      if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && +          conn->dcid.current.seq == pv->fallback_dcid.seq) { +        ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); +      } +    } + +    ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid); +    ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +    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.rb)) { +        rv = conn_retire_dcid(conn, &pv->dcid, ts); +        if (rv != 0) { +          return rv; +        } + +        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + +        if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && +            pv->dcid.seq == pv->fallback_dcid.seq) { +          ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); +        } + +        ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); +        ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +        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_abort_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.rb)) { +        rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); +        if (rv != 0) { +          return rv; +        } + +        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +        ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); +        ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +        rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); +        if (rv != 0) { +          return rv; +        } +      } else { +        /* Now we have no fallback dcid. */ +        return conn_abort_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_PROTO; +      } + +      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->retired_ts = ts; + +      return ngtcp2_pq_push(&conn->scid.used, &scid->pe); +    } +  } + +  return 0; +} + +/* + * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + *     Token is empty + * NGTCP2_ERR_PROTO: + *     Server received NEW_TOKEN. + */ +static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { +  if (conn->server) { +    return NGTCP2_ERR_PROTO; +  } + +  if (fr->tokenlen == 0) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  return conn_call_recv_new_token(conn, fr->token, fr->tokenlen); +} + +/* + * conn_recv_streams_blocked_bidi processes the incoming + * STREAMS_BLOCKED (0x16). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + *     Maximum Streams is larger than advertised value. + */ +static int conn_recv_streams_blocked_bidi(ngtcp2_conn *conn, +                                          ngtcp2_streams_blocked *fr) { +  if (fr->max_streams > conn->remote.bidi.max_streams) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  return 0; +} + +/* + * conn_recv_streams_blocked_uni processes the incoming + * STREAMS_BLOCKED (0x17). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + *     Maximum Streams is larger than advertised value. + */ +static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn, +                                         ngtcp2_streams_blocked *fr) { +  if (fr->max_streams > conn->remote.uni.max_streams) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  return 0; +} + +/* + * conn_recv_stream_data_blocked processes the incoming + * STREAM_DATA_BLOCKED frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + *     STREAM_DATA_BLOCKED is received for a local stream which is not + *     initiated; or it is received for a local unidirectional stream. + * NGTCP2_ERR_STREAM_LIMIT + *     STREAM_DATA_BLOCKED has remote stream ID which is strictly + *     greater than the allowed limit. + * NGTCP2_ERR_FLOW_CONTROL + *     STREAM_DATA_BLOCKED frame violates flow control limit. + * NGTCP2_ERR_FINAL_SIZE + *     The offset is strictly larger than it is permitted. + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static int conn_recv_stream_data_blocked(ngtcp2_conn *conn, +                                         ngtcp2_stream_data_blocked *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); +  uint64_t datalen; + +  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; +  } + +  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 before we create ngtcp2_strm object. */ +    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +    if (strm == NULL) { +      return NGTCP2_ERR_NOMEM; +    } +    rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); +    if (rv != 0) { +      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); +      return rv; +    } + +    if (!bidi) { +      ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); +      strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; +    } + +    rv = conn_call_stream_open(conn, strm); +    if (rv != 0) { +      return rv; +    } +  } + +  if (strm->rx.max_offset < fr->offset) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  if (fr->offset <= strm->rx.last_offset) { +    return 0; +  } + +  if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) { +    return NGTCP2_ERR_FINAL_SIZE; +  } + +  datalen = fr->offset - strm->rx.last_offset; +  if (datalen) { +    if (conn_max_data_violated(conn, datalen)) { +      return NGTCP2_ERR_FLOW_CONTROL; +    } + +    conn->rx.offset += datalen; + +    if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { +      ngtcp2_conn_extend_max_offset(conn, datalen); +    } +  } + +  strm->rx.last_offset = fr->offset; + +  return 0; +} + +/* + * conn_recv_data_blocked processes the incoming DATA_BLOCKED frame + * |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FLOW_CONTROL + *     It violates connection-level flow control limit. + */ +static int conn_recv_data_blocked(ngtcp2_conn *conn, ngtcp2_data_blocked *fr) { +  if (conn->rx.max_offset < fr->offset) { +    return NGTCP2_ERR_FLOW_CONTROL; +  } + +  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) { +  ngtcp2_path_storage ps; +  int rv; +  ngtcp2_pv *pv; +  ngtcp2_dcid *dcid; + +  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { +    return 0; +  } + +  ngtcp2_path_storage_zero(&ps); +  ngtcp2_addr_copy(&ps.path.local, &conn->dcid.current.ps.path.local); + +  rv = conn_call_select_preferred_addr(conn, &ps.path); +  if (rv != 0) { +    return rv; +  } + +  if (ps.path.remote.addrlen == 0 || +      ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) { +    return 0; +  } + +  assert(conn->pv == NULL); + +  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +  ngtcp2_dcid_set_path(dcid, &ps.path); + +  rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), +                     NGTCP2_PV_FLAG_PREFERRED_ADDR, &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.rb); +  conn->pv = pv; + +  return conn_call_activate_dcid(conn, &pv->dcid); +} + +/* + * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE + * frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + *     Server received HANDSHAKE_DONE frame. + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  int rv; + +  if (conn->server) { +    return NGTCP2_ERR_PROTO; +  } + +  if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { +    return 0; +  } + +  conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED | +                 NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + +  conn->pktns.rtb.persistent_congestion_start_ts = ts; + +  ngtcp2_conn_discard_handshake_state(conn, ts); + +  assert(conn->remote.transport_params); + +  if (conn->remote.transport_params->preferred_addr_present) { +    rv = conn_select_preferred_addr(conn); +    if (rv != 0) { +      return rv; +    } +  } + +  rv = conn_call_handshake_confirmed(conn); +  if (rv != 0) { +    return rv; +  } + +  /* Re-arm loss detection timer after handshake has been +     confirmed. */ +  ngtcp2_conn_set_loss_detection_timer(conn, ts); + +  return 0; +} + +/* + * conn_recv_datagram processes the incoming DATAGRAM frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) { +  assert(conn->local.transport_params.max_datagram_frame_size); + +  return conn_call_recv_datagram(conn, fr); +} + +/* + * 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); +} + +static int conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * conn_prepare_key_update installs new updated keys. + */ +static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; +  ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); +  ngtcp2_pktns *pktns = &conn->pktns; +  ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm; +  ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm; +  ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm; +  ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}, tx_aead_ctx = {0}; +  size_t secretlen, ivlen; + +  if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && +      tx_ckm->use_count >= pktns->crypto.ctx.max_encryption && +      conn_initiate_key_update(conn, ts) != 0) { +    return NGTCP2_ERR_AEAD_LIMIT_REACHED; +  } + +  if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || +      ngtcp2_tstamp_not_elapsed(confirmed_ts, pto, ts)) { +    return 0; +  } + +  if (conn->crypto.key_update.new_rx_ckm || +      conn->crypto.key_update.new_tx_ckm) { +    assert(conn->crypto.key_update.new_rx_ckm); +    assert(conn->crypto.key_update.new_tx_ckm); +    return 0; +  } + +  secretlen = rx_ckm->secret.len; +  ivlen = rx_ckm->iv.len; + +  rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm, +                                   secretlen, ivlen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm, +                                   secretlen, ivlen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  new_rx_ckm = conn->crypto.key_update.new_rx_ckm; +  new_tx_ckm = conn->crypto.key_update.new_tx_ckm; + +  rv = conn_call_update_key( +    conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx, +    new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base, rx_ckm->secret.base, +    tx_ckm->secret.base, secretlen); +  if (rv != 0) { +    return rv; +  } + +  new_rx_ckm->aead_ctx = rx_aead_ctx; +  new_tx_ckm->aead_ctx = tx_aead_ctx; + +  if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) { +    new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; +    new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; +  } + +  if (conn->crypto.key_update.old_rx_ckm) { +    conn_call_delete_crypto_aead_ctx( +      conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); +    ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); +    conn->crypto.key_update.old_rx_ckm = NULL; +  } + +  return 0; +} + +/* + * conn_rotate_keys rotates keys.  The current key moves to old key, + * and new key moves to the current key.  If the local endpoint + * initiated this key update, pass nonzero as |initiator|. + */ +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num, +                             int initiator) { +  ngtcp2_pktns *pktns = &conn->pktns; + +  assert(conn->crypto.key_update.new_rx_ckm); +  assert(conn->crypto.key_update.new_tx_ckm); +  assert(!conn->crypto.key_update.old_rx_ckm); +  assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + +  conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm; + +  pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm; +  conn->crypto.key_update.new_rx_ckm = NULL; +  pktns->crypto.rx.ckm->pkt_num = pkt_num; + +  assert(pktns->crypto.tx.ckm); + +  conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); +  ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + +  pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm; +  conn->crypto.key_update.new_tx_ckm = NULL; +  pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; + +  conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; +  if (initiator) { +    conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR; +  } +} + +/* + * 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, +                                                 size_t dgramlen, +                                                 int new_cid_used, +                                                 ngtcp2_tstamp ts) { +  ngtcp2_dcid dcid, *bound_dcid, *last; +  ngtcp2_pv *pv; +  int rv; +  ngtcp2_duration pto; +  int require_new_cid; +  int local_addr_eq; +  uint32_t remote_addr_cmp; +  size_t len, i; + +  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, ts); +    conn->dcid.current.bytes_recv += dgramlen; +    conn_reset_ecn_validation_state(conn); + +    rv = conn_abort_pv(conn, ts); +    if (rv != 0) { +      return rv; +    } + +    /* Run PMTUD just in case if it is prematurely aborted */ +    assert(!conn->pmtud); + +    return conn_start_pmtud(conn); +  } + +  remote_addr_cmp = +    ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote); +  local_addr_eq = +    ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); + +  /* +   * When to change DCID?  RFC 9002 section 9.5 says: +   * +   * An endpoint MUST NOT reuse a connection ID when sending from more +   * than one local address -- for example, when initiating connection +   * migration as described in Section 9.2 or when probing a new +   * network path as described in Section 9.1. +   * +   * Similarly, an endpoint MUST NOT reuse a connection ID when +   * sending to more than one destination address.  Due to network +   * changes outside the control of its peer, an endpoint might +   * receive packets from a new source address with the same +   * Destination Connection ID field value, in which case it MAY +   * continue to use the current connection ID with the new remote +   * address while still sending from the same local address. +   */ +  require_new_cid = conn->dcid.current.cid.datalen && +                    ((new_cid_used && remote_addr_cmp) || !local_addr_eq); + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                  "non-probing packet was received from new remote address"); + +  len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); + +  for (i = 0; i < len; ++i) { +    bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); +    if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) { +      ngtcp2_log_info( +        &conn->log, NGTCP2_LOG_EVENT_CON, +        "Found DCID which has already been bound to the new path"); + +      ngtcp2_dcid_copy(&dcid, bound_dcid); +      if (i == 0) { +        ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); +      } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { +        ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); +      } else { +        last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1); +        ngtcp2_dcid_copy(bound_dcid, last); +        ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); +      } +      require_new_cid = 0; + +      if (dcid.cid.datalen) { +        rv = conn_call_activate_dcid(conn, &dcid); +        if (rv != 0) { +          return rv; +        } +      } +      break; +    } +  } + +  if (i == len) { +    if (require_new_cid) { +      if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { +        return NGTCP2_ERR_CONN_ID_BLOCKED; +      } +      ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0)); +      ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +      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. */ +      ngtcp2_dcid_copy(&dcid, &conn->dcid.current); +      dcid.bytes_sent = 0; +      dcid.bytes_recv = 0; +      dcid.flags &= (uint8_t)~NGTCP2_DCID_FLAG_PATH_VALIDATED; +    } + +    ngtcp2_dcid_set_path(&dcid, path); +  } + +  dcid.bytes_recv += dgramlen; + +  pto = conn_compute_pto(conn, &conn->pktns); + +  rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), +                     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); +    pv->fallback_pto = conn->pv->fallback_pto; +    /* Unset the flag bit so that conn_stop_pv does not retire +       DCID. */ +    conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; +  } else { +    ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); +    pv->fallback_pto = pto; +  } + +  ngtcp2_dcid_copy(&conn->dcid.current, &dcid); + +  if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR | +                                            NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { +    conn_reset_congestion_state(conn, ts); +  } + +  conn_reset_ecn_validation_state(conn); + +  ngtcp2_conn_stop_pmtud(conn); + +  if (conn->pv) { +    ngtcp2_log_info( +      &conn->log, NGTCP2_LOG_EVENT_PTV, +      "path migration is aborted because new migration has started"); +    rv = conn_abort_pv(conn, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  conn->pv = pv; + +  return 0; +} + +/* + * conn_recv_pkt_from_new_path is called when a 1RTT packet is + * received from new path (not current path).  This packet would be a + * packet which only contains probing frame, or reordered packet, or a + * path is being validated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + *     No unused DCID is available + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, +                                       const ngtcp2_path *path, size_t dgramlen, +                                       int path_challenge_recved, +                                       ngtcp2_tstamp ts) { +  ngtcp2_pv *pv = conn->pv; +  ngtcp2_dcid *bound_dcid; +  int rv; + +  if (pv) { +    if (ngtcp2_path_eq(&pv->dcid.ps.path, path)) { +      pv->dcid.bytes_recv += dgramlen; +      return 0; +    } + +    if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && +        ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)) { +      pv->fallback_dcid.bytes_recv += dgramlen; +      return 0; +    } +  } + +  if (!path_challenge_recved) { +    return 0; +  } + +  rv = conn_bind_dcid(conn, &bound_dcid, path, ts); +  if (rv != 0) { +    return rv; +  } + +  ngtcp2_dcid_set_path(bound_dcid, path); +  bound_dcid->bytes_recv += dgramlen; + +  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_info *pi, +                                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_PROTO; +  } + +  ngtcp2_qlog_pkt_received_start(&conn->qlog); + +  for (; payloadlen;) { +    nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); +    if (nread < 0) { +      return (int)nread; +    } + +    payload += nread; +    payloadlen -= (size_t)nread; + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      fr->ack.ack_delay = 0; +      fr->ack.ack_delay_unscaled = 0; +      break; +    } + +    ngtcp2_log_rx_fr(&conn->log, hd, fr); + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      if (!conn->server) { +        conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; +      } +      rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGTCP2_FRAME_PADDING: +      break; +    case NGTCP2_FRAME_CONNECTION_CLOSE: +      rv = conn_recv_connection_close(conn, &fr->connection_close); +      if (rv != 0) { +        return rv; +      } +      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, require_ack, pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  pktns_increase_ecn_counts(pktns, pi); + +  /* Initial and Handshake are always acknowledged without delay.  No +     need to call ngtcp2_acktr_immediate_ack(). */ +  rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack, +                             pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  conn_restart_timer_on_read(conn, ts); + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  return 0; +} + +/* + * conn_allow_path_change_under_disable_active_migration returns + * nonzero if a packet from |path| is acceptable under + * disable_active_migration is on. + */ +static int +conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn, +                                                      const ngtcp2_path *path) { +  uint32_t remote_addr_cmp; +  const ngtcp2_preferred_addr *paddr; +  ngtcp2_addr addr; + +  assert(conn->server); +  assert(conn->local.transport_params.disable_active_migration); + +  /* If local address does not change, it must be passive migration +     (NAT rebinding). */ +  if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) { +    remote_addr_cmp = +      ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote); + +    return (remote_addr_cmp | NGTCP2_ADDR_COMPARE_FLAG_PORT) == +           NGTCP2_ADDR_COMPARE_FLAG_PORT; +  } + +  /* If local address changes, it must be one of the preferred +     addresses. */ + +  if (!conn->local.transport_params.preferred_addr_present) { +    return 0; +  } + +  paddr = &conn->local.transport_params.preferred_addr; + +  if (paddr->ipv4_present) { +    ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv4, +                     sizeof(paddr->ipv4)); + +    if (ngtcp2_addr_eq(&addr, &path->local)) { +      return 1; +    } +  } + +  if (paddr->ipv6_present) { +    ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv6, +                     sizeof(paddr->ipv6)); + +    if (ngtcp2_addr_eq(&addr, &path->local)) { +      return 1; +    } +  } + +  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 ngtcp2_pkt_info *pi, const uint8_t *pkt, +                                  size_t pktlen, size_t dgramlen, +                                  ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { +  ngtcp2_pkt_hd hd; +  int rv = 0; +  size_t hdpktlen; +  const uint8_t *payload; +  size_t payloadlen; +  ngtcp2_ssize nread, nwrite; +  ngtcp2_max_frame mfr; +  ngtcp2_frame *fr = &mfr.fr; +  int require_ack = 0; +  ngtcp2_crypto_aead *aead; +  ngtcp2_crypto_cipher *hp; +  ngtcp2_crypto_km *ckm; +  ngtcp2_crypto_cipher_ctx *hp_ctx; +  ngtcp2_hp_mask hp_mask; +  ngtcp2_decrypt decrypt; +  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; +  int path_challenge_recved = 0; + +  if (conn->server && conn->local.transport_params.disable_active_migration && +      !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && +      !conn_allow_path_change_under_disable_active_migration(conn, path)) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                    "packet is discarded because active migration is disabled"); + +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { +    nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); +    if (nread < 0) { +      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) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    assert(conn->negotiated_version); + +    if (hd.version != conn->client_chosen_version && +        hd.version != conn->negotiated_version) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    if (conn_verify_fixed_bit(conn, &hd) != 0) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    pktlen = (size_t)nread + hd.len; + +    /* Quoted from spec: if subsequent packets of those types include +       a different Source Connection ID, they MUST be discarded. */ +    if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { +      ngtcp2_log_rx_pkt_hd(&conn->log, &hd); +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                      "packet was ignored because of mismatched SCID"); +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    switch (hd.type) { +    case NGTCP2_PKT_INITIAL: +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                      "delayed Initial packet was discarded"); +      return (ngtcp2_ssize)pktlen; +    case NGTCP2_PKT_HANDSHAKE: +      if (hd.version != conn->negotiated_version) { +        return NGTCP2_ERR_DISCARD_PKT; +      } + +      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; +      aead = &pktns->crypto.ctx.aead; +      hp = &pktns->crypto.ctx.hp; +      ckm = pktns->crypto.rx.ckm; +      hp_ctx = &pktns->crypto.rx.hp_ctx; +      hp_mask = conn->callbacks.hp_mask; +      decrypt = conn->callbacks.decrypt; +      break; +    case NGTCP2_PKT_0RTT: +      if (!conn->server || hd.version != conn->client_chosen_version) { +        return NGTCP2_ERR_DISCARD_PKT; +      } + +      if (!conn->early.ckm) { +        return (ngtcp2_ssize)pktlen; +      } + +      pktns = &conn->pktns; +      aead = &conn->early.ctx.aead; +      hp = &conn->early.ctx.hp; +      ckm = conn->early.ckm; +      hp_ctx = &conn->early.hp_ctx; +      hp_mask = conn->callbacks.hp_mask; +      decrypt = conn->callbacks.decrypt; +      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; +    } + +    if (conn_verify_fixed_bit(conn, &hd) != 0) { +      return NGTCP2_ERR_DISCARD_PKT; +    } + +    pktns = &conn->pktns; +    aead = &pktns->crypto.ctx.aead; +    hp = &pktns->crypto.ctx.hp; +    ckm = pktns->crypto.rx.ckm; +    hp_ctx = &pktns->crypto.rx.hp_ctx; +    hp_mask = conn->callbacks.hp_mask; +    decrypt = conn->callbacks.decrypt; +  } + +  rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4); +  if (rv != 0) { +    return rv; +  } + +  nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, +                      (size_t)nread, hp_ctx, hp_mask); +  if (nwrite < 0) { +    if (ngtcp2_err_is_fatal((int)nwrite)) { +      return nwrite; +    } +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                    "could not decrypt packet number"); +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  hdpktlen = (size_t)nwrite; +  payload = pkt + hdpktlen; +  payloadlen = pktlen - hdpktlen; + +  hd.pkt_num = +    ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, hd.pkt_numlen); +  if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                    "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); +    return NGTCP2_ERR_DISCARD_PKT; +  } + +  ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + +  if (hd.type == NGTCP2_PKT_1RTT) { +    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_1RTT); + +    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, +                       conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num, +                       ckm, decrypt); + +  if (force_decrypt_failure) { +    nwrite = NGTCP2_ERR_DECRYPT; +  } + +  if (nwrite < 0) { +    if (ngtcp2_err_is_fatal((int)nwrite)) { +      return nwrite; +    } + +    assert(NGTCP2_ERR_DECRYPT == nwrite); + +    if (hd.type == NGTCP2_PKT_1RTT && +        ++conn->crypto.decryption_failure_count >= +          pktns->crypto.ctx.max_decryption_failure) { +      return NGTCP2_ERR_AEAD_LIMIT_REACHED; +    } + +    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(conn->crypto.decrypt_hp_buf.base[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_PROTO; +  } + +  if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { +    switch (hd.type) { +    case NGTCP2_PKT_HANDSHAKE: +      rv = conn_verify_dcid(conn, NULL, &hd); +      if (rv != 0) { +        if (ngtcp2_err_is_fatal(rv)) { +          return rv; +        } +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                        "packet was ignored because of mismatched DCID"); +        return NGTCP2_ERR_DISCARD_PKT; +      } + +      rv = conn_recv_delayed_handshake_pkt(conn, pi, &hd, pktlen, payload, +                                           payloadlen, pkt_ts, ts); +      if (rv < 0) { +        return (ngtcp2_ssize)rv; +      } + +      return (ngtcp2_ssize)pktlen; +    case NGTCP2_PKT_0RTT: +      if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) { +        rv = conn_verify_dcid(conn, NULL, &hd); +        if (rv != 0) { +          if (ngtcp2_err_is_fatal(rv)) { +            return rv; +          } +          ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, +                          "packet was ignored because of mismatched DCID"); +          return NGTCP2_ERR_DISCARD_PKT; +        } +      } +      break; +    default: +      /* Unreachable */ +      ngtcp2_unreachable(); +    } +  } 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; +    } +  } + +  ngtcp2_qlog_pkt_received_start(&conn->qlog); + +  for (; payloadlen;) { +    nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); +    if (nread < 0) { +      return nread; +    } + +    payload += nread; +    payloadlen -= (size_t)nread; + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) && +          hd.type == NGTCP2_PKT_0RTT) { +        return NGTCP2_ERR_PROTO; +      } +      assert(conn->remote.transport_params); +      assign_recved_ack_delay_unscaled( +        &fr->ack, conn->remote.transport_params->ack_delay_exponent); +      break; +    } + +    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_PATH_CHALLENGE: +      case NGTCP2_FRAME_CONNECTION_CLOSE: +      case NGTCP2_FRAME_CONNECTION_CLOSE_APP: +      case NGTCP2_FRAME_DATAGRAM: +      case NGTCP2_FRAME_DATAGRAM_LEN: +        break; +      default: +        return NGTCP2_ERR_PROTO; +      } +    } + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +    case NGTCP2_FRAME_PADDING: +    case NGTCP2_FRAME_CONNECTION_CLOSE: +    case NGTCP2_FRAME_CONNECTION_CLOSE_APP: +      break; +    default: +      require_ack = 1; +    } + +    switch (fr->type) { +    case NGTCP2_FRAME_ACK: +    case NGTCP2_FRAME_ACK_ECN: +      if (!conn->server) { +        conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; +      } +      rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_STREAM: +      rv = conn_recv_stream(conn, &fr->stream); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_CRYPTO: +      rv = conn_recv_crypto(conn, NGTCP2_ENCRYPTION_LEVEL_1RTT, +                            &pktns->crypto.strm, &fr->stream); +      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: +      rv = conn_recv_connection_close(conn, &fr->connection_close); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGTCP2_FRAME_PING: +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_PATH_CHALLENGE: +      conn_recv_path_challenge(conn, path, &fr->path_challenge); +      path_challenge_recved = 1; +      break; +    case NGTCP2_FRAME_PATH_RESPONSE: +      rv = conn_recv_path_response(conn, &fr->path_response, ts); +      if (rv != 0) { +        return rv; +      } +      break; +    case NGTCP2_FRAME_NEW_CONNECTION_ID: +      rv = conn_recv_new_connection_id(conn, &fr->new_connection_id); +      if (rv != 0) { +        return rv; +      } +      recv_ncid = 1; +      break; +    case NGTCP2_FRAME_RETIRE_CONNECTION_ID: +      rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id, +                                          ts); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_NEW_TOKEN: +      rv = conn_recv_new_token(conn, &fr->new_token); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_HANDSHAKE_DONE: +      rv = conn_recv_handshake_done(conn, ts); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: +      rv = conn_recv_streams_blocked_bidi(conn, &fr->streams_blocked); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: +      rv = conn_recv_streams_blocked_uni(conn, &fr->streams_blocked); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_STREAM_DATA_BLOCKED: +      rv = conn_recv_stream_data_blocked(conn, &fr->stream_data_blocked); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_DATA_BLOCKED: +      rv = conn_recv_data_blocked(conn, &fr->data_blocked); +      if (rv != 0) { +        return rv; +      } +      non_probing_pkt = 1; +      break; +    case NGTCP2_FRAME_DATAGRAM: +    case NGTCP2_FRAME_DATAGRAM_LEN: +      if ((uint64_t)nread > +          conn->local.transport_params.max_datagram_frame_size) { +        return NGTCP2_ERR_PROTO; +      } +      rv = conn_recv_datagram(conn, &fr->datagram); +      if (rv != 0) { +        return rv; +      } +      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_1RTT && +      !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +    if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num && +        !conn_path_validation_in_progress(conn, path)) { +      rv = conn_recv_non_probing_pkt_on_new_path(conn, path, dgramlen, +                                                 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); +      } +    } else { +      rv = conn_recv_pkt_from_new_path(conn, path, dgramlen, +                                       path_challenge_recved, 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_1RTT) { +    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, /* initiator = */ 0); +    } else if (ckm->pkt_num > hd.pkt_num) { +      ckm->pkt_num = hd.pkt_num; +    } + +    if (conn->server && conn->early.ckm && +        conn->early.discard_started_ts == UINT64_MAX) { +      conn->early.discard_started_ts = ts; +    } + +    if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +      conn_update_keep_alive_last_ts(conn, ts); +    } +  } + +  rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  pktns_increase_ecn_counts(pktns, pi); + +  if (require_ack && +      (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh || +       (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) { +    ngtcp2_acktr_immediate_ack(&pktns->acktr); +  } + +  rv = +    ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, pkt_ts); +  if (rv != 0) { +    return rv; +  } + +  conn_restart_timer_on_read(conn, ts); + +  ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + +  return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING +                                           : (ngtcp2_ssize)pktlen; +} + +/* + * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT + * 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)->pi, (*ppc)->pkt, +                          (*ppc)->pktlen, (*ppc)->dgramlen, (*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)->pi, +                                    (*ppc)->pkt, (*ppc)->pktlen, +                                    (*ppc)->dgramlen, (*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; + +  assert(params); + +  conn->local.bidi.max_streams = params->initial_max_streams_bidi; +  conn->local.uni.max_streams = params->initial_max_streams_uni; +} + +static int strm_set_max_offset(void *data, void *ptr) { +  ngtcp2_conn *conn = ptr; +  ngtcp2_transport_params *params = conn->remote.transport_params; +  ngtcp2_strm *strm = data; +  uint64_t max_offset; +  int rv; + +  assert(params); + +  if (!conn_local_stream(conn, strm->stream_id)) { +    return 0; +  } + +  if (bidi_stream(strm->stream_id)) { +    max_offset = params->initial_max_stream_data_bidi_remote; +  } else { +    max_offset = params->initial_max_stream_data_uni; +  } + +  if (strm->tx.max_offset < max_offset) { +    strm->tx.max_offset = max_offset; + +    /* 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, strm->stream_id, +                                          strm->tx.max_offset); +    if (rv != 0) { +      return rv; +    } +  } + +  return 0; +} + +static int conn_sync_stream_data_limit(ngtcp2_conn *conn) { +  return ngtcp2_map_each(&conn->strms, strm_set_max_offset, conn); +} + +/* + * 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; + +  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 1RTT + * 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 ngtcp2_pkt_info *pi, const uint8_t *pkt, +                          size_t pktlen, ngtcp2_tstamp ts) { +  ngtcp2_ssize nread; +  int rv; +  const uint8_t *origpkt = pkt; +  size_t dgramlen = pktlen; + +  if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +    conn->dcid.current.bytes_recv += dgramlen; +  } + +  while (pktlen) { +    nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, 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, dgramlen); +        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.rb); +  ngtcp2_dcid *dcid; + +  for (i = 0; i < len; ++i) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); +    if (ngtcp2_path_eq(&dcid->ps.path, path)) { +      return 1; +    } +  } + +  return 0; +} + +/* + * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for + * transmission. + */ +static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { +  ngtcp2_pktns *pktns = &conn->pktns; +  ngtcp2_frame_chain *nfrc; +  int rv; + +  assert(conn->server); + +  rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); +  if (rv != 0) { +    return rv; +  } + +  nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; +  nfrc->next = pktns->tx.frq; +  pktns->tx.frq = nfrc; + +  return 0; +} + +/** + * @function + * + * `conn_read_handshake` performs QUIC cryptographic handshake by + * reading given data.  |pkt| points to the buffer to read and + * |pktlen| is the length of the buffer.  |path| is the network path. + * + * This function returns the number of bytes processed.  Unless the + * last packet is 1RTT packet and an application decryption key has + * been installed, it returns |pktlen| if it succeeds.  If it finds + * 1RTT packet and an application decryption key has been installed, + * it returns the number of bytes just before 1RTT packet begins. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: (TBD). + */ +static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, +                                        const ngtcp2_path *path, +                                        const ngtcp2_pkt_info *pi, +                                        const uint8_t *pkt, size_t pktlen, +                                        ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_ssize nread; + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +    /* TODO Better to log something when we ignore input */ +    return (ngtcp2_ssize)pktlen; +  case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: +    nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); +    if (nread < 0) { +      return nread; +    } + +    if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { +      /* Retry packet was received */ +      return (ngtcp2_ssize)pktlen; +    } + +    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; +      } +    } + +    if (conn_is_tls_handshake_completed(conn) && +        !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { +      rv = conn_handshake_completed(conn); +      if (rv != 0) { +        return rv; +      } + +      rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); +      if (rv != 0) { +        return rv; +      } +    } + +    return nread; +  case NGTCP2_CS_SERVER_INITIAL: +    nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); +    if (nread < 0) { +      return nread; +    } + +    /* +     * Client Hello might not fit into single Initial packet (e.g., +     * resuming session with client authentication).  If we get Client +     * Initial which does not increase offset or it is 0RTT packet +     * buffered, perform address validation in order to buffer +     * validated data only. +     */ +    if (ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0) { +      if (conn->in_pktns->crypto.strm.rx.rob && +          ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) { +        /* Address has been validated with token */ +        if (conn->local.settings.tokenlen) { +          return nread; +        } +        return NGTCP2_ERR_RETRY; +      } +      /* If CRYPTO frame is not processed, just drop connection. */ +      return NGTCP2_ERR_DROP_CONN; +    } + +    /* 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 nread; +  case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: +    nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); +    if (nread < 0) { +      return nread; +    } + +    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) { +      ngtcp2_conn_discard_initial_state(conn, ts); +    } + +    if (!conn_is_tls_handshake_completed(conn)) { +      /* If server hits amplification limit, it cancels loss detection +         timer.  If server receives a packet from client, the limit is +         increased and server can send more.  If server has +         ack-eliciting Initial or Handshake packets, it should resend +         it if timer fired but timer is not armed in this case.  So +         instead of resending Initial/Handshake packets, if server has +         1RTT data to send, it might send them and then might hit +         amplification limit again until it hits stream data limit. +         Initial/Handshake data is not resent.  In order to avoid this +         situation, try to arm loss detection and check the expiry +         here so that on next write call, we can resend +         Initial/Handshake first. */ +      if (conn->cstat.loss_detection_timer == UINT64_MAX) { +        ngtcp2_conn_set_loss_detection_timer(conn, ts); +        if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { +          rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); +          if (rv != 0) { +            return rv; +          } +        } +      } + +      if ((size_t)nread < pktlen) { +        /* We have 1RTT packet and application rx key, but the +           handshake has not completed yet. */ +        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                        "buffering 1RTT packet len=%zu", +                        pktlen - (size_t)nread); + +        rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread, +                             pktlen - (size_t)nread, pktlen, ts); +        if (rv != 0) { +          assert(ngtcp2_err_is_fatal(rv)); +          return rv; +        } + +        return (ngtcp2_ssize)pktlen; +      } + +      return nread; +    } + +    if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { +      return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; +    } + +    rv = conn_handshake_completed(conn); +    if (rv != 0) { +      return rv; +    } +    conn->state = NGTCP2_CS_POST_HANDSHAKE; + +    rv = conn_call_activate_dcid(conn, &conn->dcid.current); +    if (rv != 0) { +      return rv; +    } + +    rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); +    if (rv != 0) { +      return rv; +    } + +    ngtcp2_conn_discard_handshake_state(conn, ts); + +    rv = conn_enqueue_handshake_done(conn); +    if (rv != 0) { +      return rv; +    } + +    if (!conn->local.settings.no_pmtud) { +      rv = conn_start_pmtud(conn); +      if (rv != 0) { +        return rv; +      } +    } + +    conn->pktns.rtb.persistent_congestion_start_ts = ts; + +    /* Re-arm loss detection timer here after handshake has been +       confirmed. */ +    ngtcp2_conn_set_loss_detection_timer(conn, ts); + +    return nread; +  case NGTCP2_CS_CLOSING: +    return NGTCP2_ERR_CLOSING; +  case NGTCP2_CS_DRAINING: +    return NGTCP2_ERR_DRAINING; +  default: +    return (ngtcp2_ssize)pktlen; +  } +} + +int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, +                                   int pkt_info_version, +                                   const ngtcp2_pkt_info *pi, +                                   const uint8_t *pkt, size_t pktlen, +                                   ngtcp2_tstamp ts) { +  int rv = 0; +  ngtcp2_ssize nread = 0; +  const ngtcp2_pkt_info zero_pi = {0}; +  (void)pkt_info_version; + +  assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + +  conn_update_timestamp(conn, ts); + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu", +                  pktlen); + +  if (pktlen == 0) { +    return 0; +  } + +  /* 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; +  } + +  if (!pi) { +    pi = &zero_pi; +  } + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +  case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: +    nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); +    if (nread < 0) { +      return (int)nread; +    } + +    if ((size_t)nread == pktlen) { +      return 0; +    } + +    assert(conn->pktns.crypto.rx.ckm); + +    pkt += nread; +    pktlen -= (size_t)nread; + +    break; +  case NGTCP2_CS_SERVER_INITIAL: +  case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: +    if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                      "ignore packet from unknown path during handshake"); + +      if (conn->state == NGTCP2_CS_SERVER_INITIAL && +          ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0 && +          (!conn->in_pktns->crypto.strm.rx.rob || +           !ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob))) { +        return NGTCP2_ERR_DROP_CONN; +      } + +      return 0; +    } + +    nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts); +    if (nread < 0) { +      return (int)nread; +    } + +    if ((size_t)nread == pktlen) { +      return 0; +    } + +    assert(conn->pktns.crypto.rx.ckm); + +    pkt += nread; +    pktlen -= (size_t)nread; + +    break; +  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; +    } +    break; +  default: +    ngtcp2_unreachable(); +  } + +  return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); +} + +/* + * 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_retransmit_retry_early retransmits 0RTT packet after Retry is + * received from server. + */ +static ngtcp2_ssize +conn_retransmit_retry_early(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, +                            uint8_t *dest, size_t destlen, size_t dgram_offset, +                            uint8_t flags, ngtcp2_tstamp ts) { +  return conn_write_pkt(conn, pi, dest, destlen, dgram_offset, NULL, +                        NGTCP2_PKT_0RTT, flags, ts); +} + +/* + * conn_handshake_probe_left returns nonzero if there are probe + * packets to be sent for Initial or Handshake packet number space + * left. + */ +static int conn_handshake_probe_left(ngtcp2_conn *conn) { +  return (conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || +         conn->hs_pktns->rtb.probe_pkt_left; +} + +/* + * conn_validate_early_transport_params_limits validates that the + * limits in transport parameters remembered by client for early data + * are not reduced.  This function is only used by client and should + * only be called when early data is accepted by server. + */ +static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { +  const ngtcp2_transport_params *params = conn->remote.transport_params; + +  assert(!conn->server); +  assert(params); + +  if (conn->early.transport_params.active_connection_id_limit > +        params->active_connection_id_limit || +      conn->early.transport_params.initial_max_data > +        params->initial_max_data || +      conn->early.transport_params.initial_max_stream_data_bidi_local > +        params->initial_max_stream_data_bidi_local || +      conn->early.transport_params.initial_max_stream_data_bidi_remote > +        params->initial_max_stream_data_bidi_remote || +      conn->early.transport_params.initial_max_stream_data_uni > +        params->initial_max_stream_data_uni || +      conn->early.transport_params.initial_max_streams_bidi > +        params->initial_max_streams_bidi || +      conn->early.transport_params.initial_max_streams_uni > +        params->initial_max_streams_uni || +      conn->early.transport_params.max_datagram_frame_size > +        params->max_datagram_frame_size) { +    return NGTCP2_ERR_PROTO; +  } + +  return 0; +} + +/* + * conn_write_handshake writes QUIC handshake packets to the buffer + * pointed by |dest| of length |destlen|.  |write_datalen| specifies + * the expected length of 0RTT packet payload.  Specify 0 to + * |write_datalen| if there is no such 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, ngtcp2_pkt_info *pi, +                                         uint8_t *dest, size_t destlen, +                                         uint64_t write_datalen, +                                         ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; +  size_t origlen = destlen; +  uint64_t pending_early_datalen; +  ngtcp2_dcid *dcid; +  ngtcp2_preferred_addr *paddr; + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +    pending_early_datalen = conn_retry_early_payloadlen(conn); +    if (pending_early_datalen) { +      write_datalen = pending_early_datalen; +    } + +    if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { +      nwrite = +        conn_write_client_initial(conn, pi, dest, destlen, write_datalen, ts); +      if (nwrite <= 0) { +        return nwrite; +      } +    } else { +      nwrite = +        conn_write_handshake_pkt(conn, pi, dest, destlen, 0, NGTCP2_PKT_INITIAL, +                                 NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts); +      if (nwrite < 0) { +        return nwrite; +      } +    } + +    if (pending_early_datalen) { +      early_spktlen = conn_retransmit_retry_early( +        conn, pi, dest + nwrite, destlen - (size_t)nwrite, (size_t)nwrite, +        nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING +               : NGTCP2_WRITE_PKT_FLAG_NONE, +        ts); + +      if (early_spktlen < 0) { +        assert(ngtcp2_err_is_fatal((int)early_spktlen)); +        return early_spktlen; +      } +    } + +    conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE; + +    res = nwrite + early_spktlen; + +    return res; +  case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: +    pending_early_datalen = 0; + +    if (!conn_handshake_probe_left(conn) && conn_cwnd_is_zero(conn)) { +      destlen = 0; +    } else { +      if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { +        pending_early_datalen = conn_retry_early_payloadlen(conn); +        if (pending_early_datalen) { +          write_datalen = pending_early_datalen; +        } +      } + +      nwrite = +        conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      res += nwrite; +      dest += nwrite; +      destlen -= (size_t)nwrite; +    } + +    if (!conn_is_tls_handshake_completed(conn)) { +      if (pending_early_datalen && +          !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { +        nwrite = conn_retransmit_retry_early( +          conn, pi, dest, destlen, (size_t)res, NGTCP2_WRITE_PKT_FLAG_NONE, ts); +        if (nwrite < 0) { +          return nwrite; +        } + +        res += nwrite; +      } + +      if (res == 0) { +        nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); +        if (nwrite < 0) { +          return nwrite; +        } +        res = nwrite; +      } + +      return res; +    } + +    if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { +      return res; +    } + +    if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { +      return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; +    } + +    if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED) && +        !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { +      rv = conn_validate_early_transport_params_limits(conn); +      if (rv != 0) { +        return rv; +      } +    } + +    /* Server might increase stream data limits.  Extend it if we have +       streams created for early data. */ +    rv = conn_sync_stream_data_limit(conn); +    if (rv != 0) { +      return rv; +    } + +    conn->state = NGTCP2_CS_POST_HANDSHAKE; + +    assert(conn->remote.transport_params); + +    if (conn->remote.transport_params->preferred_addr_present) { +      assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb)); + +      paddr = &conn->remote.transport_params->preferred_addr; +      dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); +      ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); + +      rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); +      if (rv != 0) { +        return (ngtcp2_ssize)rv; +      } +    } + +    if (conn->remote.transport_params->stateless_reset_token_present) { +      assert(conn->dcid.current.seq == 0); +      assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT)); +      ngtcp2_dcid_set_token( +        &conn->dcid.current, +        conn->remote.transport_params->stateless_reset_token); +    } + +    rv = conn_call_activate_dcid(conn, &conn->dcid.current); +    if (rv != 0) { +      return rv; +    } + +    conn_process_early_rtb(conn); + +    if (!conn->local.settings.no_pmtud) { +      rv = conn_start_pmtud(conn); +      if (rv != 0) { +        return rv; +      } +    } + +    return res; +  case NGTCP2_CS_SERVER_INITIAL: +    nwrite = +      conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); +    if (nwrite < 0) { +      return nwrite; +    } + +    if (nwrite) { +      conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE; +    } + +    return nwrite; +  case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: +    if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { +      nwrite = +        conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      res += nwrite; +      dest += nwrite; +      destlen -= (size_t)nwrite; +    } + +    if (res == 0) { +      nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      res += nwrite; +      dest += nwrite; +      origlen -= (size_t)nwrite; +    } + +    return res; +  case NGTCP2_CS_CLOSING: +    return NGTCP2_ERR_CLOSING; +  case NGTCP2_CS_DRAINING: +    return NGTCP2_ERR_DRAINING; +  default: +    return 0; +  } +} + +/** + * @function + * + * `conn_client_write_handshake` writes client side handshake data and + * 0RTT packet. + * + * In order to send STREAM data in 0RTT packet, specify + * |vmsg|->stream.  |vmsg|->stream.strm, |vmsg|->stream.fin, + * |vmsg|->stream.data, and |vmsg|->stream.datacnt are stream to which + * 0-RTT data is sent, whether it is a last data chunk in this stream, + * a vector of 0-RTT data, and its number of elements respectively. + * The amount of 0RTT data sent is assigned to + * *|vmsg|->stream.pdatalen.  If no data is sent, -1 is assigned. + * Note that 0 length STREAM frame is allowed in QUIC, so 0 might be + * assigned to *|vmsg|->stream.pdatalen. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited.  Application should + * keep reading and wait for congestion window to grow. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, +                                                ngtcp2_pkt_info *pi, +                                                uint8_t *dest, size_t destlen, +                                                ngtcp2_vmsg *vmsg, +                                                ngtcp2_tstamp ts) { +  int send_stream = 0; +  int send_datagram = 0; +  ngtcp2_ssize spktlen, early_spktlen; +  uint64_t datalen; +  uint64_t write_datalen = 0; +  uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; +  int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; +  uint32_t version; + +  assert(!conn->server); + +  /* conn->early.ckm might be created in the first call of +     conn_handshake().  Check it later. */ +  if (vmsg) { +    switch (vmsg->type) { +    case NGTCP2_VMSG_TYPE_STREAM: +      datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); +      send_stream = conn_retry_early_payloadlen(conn) == 0; +      if (send_stream) { +        write_datalen = ngtcp2_min_uint64(datalen + NGTCP2_STREAM_OVERHEAD, +                                          NGTCP2_MIN_COALESCED_PAYLOADLEN); + +        if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { +          wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; +        } +      } else { +        vmsg = NULL; +      } +      break; +    case NGTCP2_VMSG_TYPE_DATAGRAM: +      datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); +      send_datagram = conn_retry_early_payloadlen(conn) == 0; +      if (send_datagram) { +        write_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; + +        if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { +          wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; +        } +      } else { +        vmsg = NULL; +      } +      break; +    } +  } + +  if (!ppe_pending) { +    spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); + +    if (spktlen < 0) { +      return spktlen; +    } + +    if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) || +        !conn->early.ckm || (!send_stream && !send_datagram)) { +      return spktlen; +    } + +    /* If spktlen > 0, we are making a compound packet.  If Initial +       packet is written, we have to pad bytes to 0-RTT packet. */ +    version = conn->negotiated_version ? conn->negotiated_version +                                       : conn->client_chosen_version; +    if (spktlen > 0 && +        ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { +      wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +      conn->pkt.require_padding = 1; +    } +  } else { +    assert(!conn->pktns.crypto.rx.ckm); +    assert(!conn->pktns.crypto.tx.ckm); +    assert(conn->early.ckm); + +    if (conn->pkt.require_padding) { +      wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +    } +    spktlen = conn->pkt.hs_spktlen; +  } + +  dest += spktlen; +  destlen -= (size_t)spktlen; + +  if (conn_cwnd_is_zero(conn)) { +    return spktlen; +  } + +  early_spktlen = conn_write_pkt(conn, pi, dest, destlen, (size_t)spktlen, vmsg, +                                 NGTCP2_PKT_0RTT, wflags, ts); +  if (early_spktlen < 0) { +    switch (early_spktlen) { +    case NGTCP2_ERR_STREAM_DATA_BLOCKED: +      if (!(wflags & NGTCP2_WRITE_PKT_FLAG_MORE)) { +        if (spktlen) { +          return spktlen; +        } + +        break; +      } +      /* fall through */ +    case NGTCP2_ERR_WRITE_MORE: +      conn->pkt.hs_spktlen = spktlen; +      break; +    } +    return early_spktlen; +  } + +  return spktlen + early_spktlen; +} + +void ngtcp2_conn_tls_handshake_completed(ngtcp2_conn *conn) { +  conn->flags |= NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED; +  if (conn->server) { +    conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; +  } +} + +int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { +  return conn_is_tls_handshake_completed(conn) && +         (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED); +} + +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 NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); +  if (nread < 0) { +    return (int)nread; +  } + +  switch (p->type) { +  case NGTCP2_PKT_INITIAL: +    break; +  case NGTCP2_PKT_0RTT: +    /* 0-RTT packet may arrive before Initial packet due to +       re-ordering.  ngtcp2 does not buffer 0RTT packet unless the +       very first Initial packet is received or token is received. +       Previously, we returned NGTCP2_ERR_RETRY here, so that client +       can resend 0RTT data.  But it incurs 1RTT already and +       diminishes the value of 0RTT.  Therefore, we just discard the +       packet here for now. */ +  default: +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (pktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE || +      (p->tokenlen == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  return 0; +} + +int ngtcp2_conn_install_initial_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, +  const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, +  const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, +  const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { +  ngtcp2_pktns *pktns = conn->in_pktns; +  int rv; + +  assert(ivlen >= 8); +  assert(pktns); + +  conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); +  pktns->crypto.rx.hp_ctx.native_handle = NULL; + +  if (pktns->crypto.rx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); +    ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); +    pktns->crypto.rx.ckm = NULL; +  } + +  conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); +  pktns->crypto.tx.hp_ctx.native_handle = NULL; + +  if (pktns->crypto.tx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); +    ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); +    pktns->crypto.tx.ckm = NULL; +  } + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  /* Take owner ship after we are sure that no failure occurs, so that +     caller can delete these contexts on failure. */ +  pktns->crypto.rx.ckm->aead_ctx = *rx_aead_ctx; +  pktns->crypto.rx.hp_ctx = *rx_hp_ctx; +  pktns->crypto.tx.ckm->aead_ctx = *tx_aead_ctx; +  pktns->crypto.tx.hp_ctx = *tx_hp_ctx; + +  return 0; +} + +int ngtcp2_conn_install_vneg_initial_key( +  ngtcp2_conn *conn, uint32_t version, +  const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv, +  const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, +  const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, +  const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { +  int rv; + +  assert(ivlen >= 8); + +  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx); +  conn->vneg.rx.hp_ctx.native_handle = NULL; + +  if (conn->vneg.rx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx); +    ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem); +    conn->vneg.rx.ckm = NULL; +  } + +  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx); +  conn->vneg.tx.hp_ctx.native_handle = NULL; + +  if (conn->vneg.tx.ckm) { +    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx); +    ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem); +    conn->vneg.tx.ckm = NULL; +  } + +  rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  /* Take owner ship after we are sure that no failure occurs, so that +     caller can delete these contexts on failure. */ +  conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx; +  conn->vneg.rx.hp_ctx = *rx_hp_ctx; +  conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx; +  conn->vneg.tx.hp_ctx = *tx_hp_ctx; +  conn->vneg.version = version; + +  return 0; +} + +int ngtcp2_conn_install_rx_handshake_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  ngtcp2_pktns *pktns = conn->hs_pktns; +  int rv; + +  assert(ivlen >= 8); +  assert(pktns); +  assert(!pktns->crypto.rx.hp_ctx.native_handle); +  assert(!pktns->crypto.rx.ckm); + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, aead_ctx, iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  pktns->crypto.rx.hp_ctx = *hp_ctx; + +  rv = conn_call_recv_rx_key(conn, NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE); +  if (rv != 0) { +    ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); +    pktns->crypto.rx.ckm = NULL; + +    memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + +    return rv; +  } + +  return 0; +} + +int ngtcp2_conn_install_tx_handshake_key( +  ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  ngtcp2_pktns *pktns = conn->hs_pktns; +  int rv; + +  assert(ivlen >= 8); +  assert(pktns); +  assert(!pktns->crypto.tx.hp_ctx.native_handle); +  assert(!pktns->crypto.tx.ckm); + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, aead_ctx, iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  pktns->crypto.tx.hp_ctx = *hp_ctx; + +  if (conn->server) { +    rv = ngtcp2_conn_commit_local_transport_params(conn); +    if (rv != 0) { +      return rv; +    } +  } + +  rv = conn_call_recv_tx_key(conn, NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE); +  if (rv != 0) { +    ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); +    pktns->crypto.tx.ckm = NULL; + +    memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + +    return rv; +  } + +  return 0; +} + +int ngtcp2_conn_install_0rtt_key(ngtcp2_conn *conn, +                                 const ngtcp2_crypto_aead_ctx *aead_ctx, +                                 const uint8_t *iv, size_t ivlen, +                                 const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  int rv; + +  assert(ivlen >= 8); +  assert(!conn->early.hp_ctx.native_handle); +  assert(!conn->early.ckm); + +  rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, aead_ctx, iv, ivlen, +                            conn->mem); +  if (rv != 0) { +    return rv; +  } + +  conn->early.hp_ctx = *hp_ctx; + +  conn->flags |= NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED; + +  if (conn->server) { +    rv = conn_call_recv_rx_key(conn, NGTCP2_ENCRYPTION_LEVEL_0RTT); +  } else { +    rv = conn_call_recv_tx_key(conn, NGTCP2_ENCRYPTION_LEVEL_0RTT); +  } +  if (rv != 0) { +    ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); +    conn->early.ckm = NULL; + +    memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx)); + +    return rv; +  } + +  return 0; +} + +int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, +                               size_t secretlen, +                               const ngtcp2_crypto_aead_ctx *aead_ctx, +                               const uint8_t *iv, size_t ivlen, +                               const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  ngtcp2_pktns *pktns = &conn->pktns; +  int rv; + +  assert(ivlen >= 8); +  assert(!pktns->crypto.rx.hp_ctx.native_handle); +  assert(!pktns->crypto.rx.ckm); + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, secret, secretlen, aead_ctx, +                            iv, ivlen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  pktns->crypto.rx.hp_ctx = *hp_ctx; + +  if (!conn->server) { +    if (conn->remote.pending_transport_params) { +      ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + +      conn->remote.transport_params = conn->remote.pending_transport_params; +      conn->remote.pending_transport_params = NULL; +      conn_sync_stream_id_limit(conn); +      conn->tx.max_offset = conn->remote.transport_params->initial_max_data; +    } + +    if (conn->early.ckm) { +      conn_discard_early_key(conn); +    } +  } + +  rv = conn_call_recv_rx_key(conn, NGTCP2_ENCRYPTION_LEVEL_1RTT); +  if (rv != 0) { +    ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); +    pktns->crypto.rx.ckm = NULL; + +    memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx)); + +    return rv; +  } + +  return 0; +} + +int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, +                               size_t secretlen, +                               const ngtcp2_crypto_aead_ctx *aead_ctx, +                               const uint8_t *iv, size_t ivlen, +                               const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  ngtcp2_pktns *pktns = &conn->pktns; +  int rv; + +  assert(ivlen >= 8); +  assert(!pktns->crypto.tx.hp_ctx.native_handle); +  assert(!pktns->crypto.tx.ckm); + +  rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, secret, secretlen, aead_ctx, +                            iv, ivlen, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  pktns->crypto.tx.hp_ctx = *hp_ctx; + +  if (conn->server) { +    if (conn->remote.pending_transport_params) { +      ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); + +      conn->remote.transport_params = conn->remote.pending_transport_params; +      conn->remote.pending_transport_params = NULL; +      conn_sync_stream_id_limit(conn); +      conn->tx.max_offset = conn->remote.transport_params->initial_max_data; +    } +  } else if (conn->early.ckm) { +    conn_discard_early_key(conn); +  } + +  rv = conn_call_recv_tx_key(conn, NGTCP2_ENCRYPTION_LEVEL_1RTT); +  if (rv != 0) { +    ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); +    pktns->crypto.tx.ckm = NULL; + +    memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx)); + +    return rv; +  } + +  return 0; +} + +static int conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; +  ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + +  assert(conn->state == NGTCP2_CS_POST_HANDSHAKE); + +  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || +      (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || +      !conn->crypto.key_update.new_tx_ckm || +      !conn->crypto.key_update.new_rx_ckm || +      ngtcp2_tstamp_not_elapsed(confirmed_ts, 3 * pto, ts)) { +    return NGTCP2_ERR_INVALID_STATE; +  } + +  conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1); + +  return 0; +} + +int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  conn_update_timestamp(conn, ts); + +  return conn_initiate_key_update(conn, ts); +} + +/* + * conn_retire_stale_bound_dcid retires stale destination connection + * ID in conn->dcid.bound to keep some unused destination connection + * IDs available. + * + * 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_stale_bound_dcid(ngtcp2_conn *conn, +                                        ngtcp2_duration timeout, +                                        ngtcp2_tstamp ts) { +  size_t i; +  ngtcp2_dcid *dcid, *last; +  int rv; + +  for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + +    assert(dcid->cid.datalen); + +    if (ngtcp2_tstamp_not_elapsed(dcid->bound_ts, timeout, ts)) { +      ++i; +      continue; +    } + +    rv = conn_retire_dcid_seq(conn, dcid->seq); +    if (rv != 0) { +      return rv; +    } + +    if (i == 0) { +      ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); +      continue; +    } + +    if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { +      ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); +      break; +    } + +    last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, +                              ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1); +    ngtcp2_dcid_copy(dcid, last); +    ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); +  } + +  return 0; +} + +ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) { +  return conn->cstat.loss_detection_timer; +} + +ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { +  ngtcp2_tstamp res = UINT64_MAX; +  ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); +  ngtcp2_scid *scid; +  ngtcp2_dcid *dcid; +  size_t i, len; + +  if (conn->pv) { +    res = ngtcp2_pv_next_expiry(conn->pv); +  } + +  if (conn->pmtud) { +    res = ngtcp2_min_uint64(res, conn->pmtud->expiry); +  } + +  if (!ngtcp2_pq_empty(&conn->scid.used)) { +    scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); +    if (scid->retired_ts != UINT64_MAX) { +      res = ngtcp2_min_uint64(res, scid->retired_ts + pto); +    } +  } + +  if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); +    res = ngtcp2_min_uint64(res, dcid->retired_ts + pto); +  } + +  if (conn->dcid.current.cid.datalen) { +    len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); +    for (i = 0; i < len; ++i) { +      dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); + +      assert(dcid->cid.datalen); +      assert(dcid->bound_ts != UINT64_MAX); + +      res = ngtcp2_min_uint64(res, dcid->bound_ts + 3 * pto); +    } +  } + +  if (conn->server && conn->early.ckm && +      conn->early.discard_started_ts != UINT64_MAX) { +    res = ngtcp2_min_uint64(res, conn->early.discard_started_ts + 3 * 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; +} + +static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) { +  if (conn_is_tls_handshake_completed(conn) || +      conn->local.settings.handshake_timeout == UINT64_MAX || +      conn->local.settings.initial_ts >= +        UINT64_MAX - conn->local.settings.handshake_timeout) { +    return UINT64_MAX; +  } + +  return conn->local.settings.initial_ts + +         conn->local.settings.handshake_timeout; +} + +ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { +  ngtcp2_tstamp res = ngtcp2_min_uint64(ngtcp2_conn_loss_detection_expiry(conn), +                                        ngtcp2_conn_ack_delay_expiry(conn)); +  res = ngtcp2_min_uint64(res, ngtcp2_conn_internal_expiry(conn)); +  res = ngtcp2_min_uint64(res, ngtcp2_conn_lost_pkt_expiry(conn)); +  res = ngtcp2_min_uint64(res, conn_keep_alive_expiry(conn)); +  res = ngtcp2_min_uint64(res, conn_handshake_expiry(conn)); +  res = ngtcp2_min_uint64(res, ngtcp2_conn_get_idle_expiry(conn)); +  return ngtcp2_min_uint64(res, conn->tx.pacing.next_ts); +} + +int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_duration pto; + +  conn_update_timestamp(conn, ts); + +  pto = conn_compute_pto(conn, &conn->pktns); + +  assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + +  if (ngtcp2_conn_get_idle_expiry(conn) <= ts) { +    return NGTCP2_ERR_IDLE_CLOSE; +  } + +  ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + +  conn_cancel_expired_keep_alive_timer(conn, ts); + +  conn_cancel_expired_pkt_tx_timer(conn, ts); + +  ngtcp2_conn_remove_lost_pkt(conn, ts); + +  if (conn->pv) { +    ngtcp2_pv_cancel_expired_timer(conn->pv, ts); +  } + +  if (conn->pmtud) { +    ngtcp2_pmtud_handle_expiry(conn->pmtud, ts); +    if (ngtcp2_pmtud_finished(conn->pmtud)) { +      ngtcp2_conn_stop_pmtud(conn); +    } +  } + +  if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { +    rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  if (conn->dcid.current.cid.datalen) { +    rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  rv = conn_remove_retired_connection_id(conn, pto, ts); +  if (rv != 0) { +    return rv; +  } + +  if (conn->server && conn->early.ckm && +      ngtcp2_tstamp_elapsed(conn->early.discard_started_ts, 3 * pto, ts)) { +    conn_discard_early_key(conn); +  } + +  if (!conn_is_tls_handshake_completed(conn) && +      ngtcp2_tstamp_elapsed(conn->local.settings.initial_ts, +                            conn->local.settings.handshake_timeout, ts)) { +    return NGTCP2_ERR_HANDSHAKE_TIMEOUT; +  } + +  return 0; +} + +static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, +                                                 ngtcp2_duration max_ack_delay, +                                                 ngtcp2_tstamp ts) { +  if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && +      ngtcp2_tstamp_elapsed(acktr->first_unacked_ts, max_ack_delay, ts)) { +    acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; +  } +} + +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, +                                                ngtcp2_tstamp ts) { +  ngtcp2_duration ack_delay = conn_compute_ack_delay(conn); + +  if (conn->in_pktns) { +    acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, 0, ts); +  } +  if (conn->hs_pktns) { +    acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, 0, ts); +  } +  acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ack_delay, ts); +} + +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { +  ngtcp2_tstamp res = UINT64_MAX, ts; + +  if (conn->in_pktns) { +    ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb); +    if (ts != UINT64_MAX) { +      ts += conn_compute_pto(conn, conn->in_pktns); +      res = ngtcp2_min_uint64(res, ts); +    } +  } + +  if (conn->hs_pktns) { +    ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb); +    if (ts != UINT64_MAX) { +      ts += conn_compute_pto(conn, conn->hs_pktns); +      res = ngtcp2_min_uint64(res, ts); +    } +  } + +  ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); +  if (ts != UINT64_MAX) { +    ts += conn_compute_pto(conn, &conn->pktns); +    res = ngtcp2_min_uint64(res, ts); +  } + +  return res; +} + +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_duration pto; + +  if (conn->in_pktns) { +    pto = conn_compute_pto(conn, conn->in_pktns); +    ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts); +  } +  if (conn->hs_pktns) { +    pto = conn_compute_pto(conn, conn->hs_pktns); +    ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts); +  } +  pto = conn_compute_pto(conn, &conn->pktns); +  ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); +} + +/* + * select_preferred_version selects the most preferred version. + * |fallback_version| is chosen if no preference is made, or + * |preferred_versions| does not include any of |chosen_version| or + * |available_versions|.  |chosen_version| is treated as an extra + * other version. + */ +static uint32_t select_preferred_version(const uint32_t *preferred_versions, +                                         size_t preferred_versionslen, +                                         uint32_t chosen_version, +                                         const uint8_t *available_versions, +                                         size_t available_versionslen, +                                         uint32_t fallback_version) { +  size_t i, j; +  const uint8_t *p; +  uint32_t v; + +  if (!preferred_versionslen || +      (!available_versionslen && chosen_version == fallback_version)) { +    return fallback_version; +  } + +  for (i = 0; i < preferred_versionslen; ++i) { +    if (preferred_versions[i] == chosen_version) { +      return chosen_version; +    } +    for (j = 0, p = available_versions; j < available_versionslen; +         j += sizeof(uint32_t)) { +      p = ngtcp2_get_uint32be(&v, p); + +      if (preferred_versions[i] == v) { +        return v; +      } +    } +  } + +  return fallback_version; +} + +/* + * 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 + *     params contains preferred address but server chose zero-length + *     connection ID. + * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE + *     Validation against version negotiation parameters failed. + */ +static int +conn_client_validate_transport_params(ngtcp2_conn *conn, +                                      const ngtcp2_transport_params *params) { +  if (!params->original_dcid_present) { +    return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; +  } + +  if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_dcid)) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { +    if (!params->retry_scid_present) { +      return NGTCP2_ERR_TRANSPORT_PARAM; +    } +    if (!ngtcp2_cid_eq(&conn->retry_scid, ¶ms->retry_scid)) { +      return NGTCP2_ERR_TRANSPORT_PARAM; +    } +  } else if (params->retry_scid_present) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  if (params->preferred_addr_present && conn->dcid.current.cid.datalen == 0) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  if (params->version_info_present) { +    if (conn->negotiated_version != params->version_info.chosen_version) { +      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +    } + +    assert(vneg_available_versions_includes(conn->vneg.available_versions, +                                            conn->vneg.available_versionslen, +                                            conn->negotiated_version)); +  } else if (conn->client_chosen_version != conn->negotiated_version) { +    return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +  } + +  /* When client reacted upon Version Negotiation */ +  if (conn->local.settings.original_version != conn->client_chosen_version) { +    if (!params->version_info_present) { +      assert(conn->client_chosen_version == conn->negotiated_version); + +      /* QUIC v1 is treated specially.  If version_info is missing, no +         further validation is necessary.  See +         https://datatracker.ietf.org/doc/html/rfc9368#section-8 +       */ +      if (conn->client_chosen_version == NGTCP2_PROTO_VER_V1) { +        return 0; +      } + +      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +    } + +    /* Server choose original version after Version Negotiation.  RFC +       9368 does not say this particular case, but this smells like +       misbehaved server because server should accept original_version +       in the original connection. */ +    if (conn->local.settings.original_version == +        params->version_info.chosen_version) { +      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +    } + +    /* Check version downgrade on incompatible version negotiation. */ +    if (params->version_info.available_versionslen == 0) { +      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +    } + +    if (conn->client_chosen_version != +        select_preferred_version(conn->vneg.preferred_versions, +                                 conn->vneg.preferred_versionslen, +                                 params->version_info.chosen_version, +                                 params->version_info.available_versions, +                                 params->version_info.available_versionslen, +                                 /* fallback_version = */ 0)) { +      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +    } +  } + +  return 0; +} + +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, +                                     const ngtcp2_version_info *version_info) { +  assert(conn->server); +  assert(conn->client_chosen_version == version_info->chosen_version); + +  return select_preferred_version( +    conn->vneg.preferred_versions, conn->vneg.preferred_versionslen, +    version_info->chosen_version, version_info->available_versions, +    version_info->available_versionslen, version_info->chosen_version); +} + +int ngtcp2_conn_set_remote_transport_params( +  ngtcp2_conn *conn, const ngtcp2_transport_params *params) { +  int rv; + +  /* We expect this function is called once per QUIC connection, but +     GnuTLS server seems to call TLS extension callback twice if it +     sends HelloRetryRequest.  In practice, same QUIC transport +     parameters are sent in the 2nd client flight, just returning 0 +     would cause no harm. */ +  if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) { +    return 0; +  } + +  if (!params->initial_scid_present) { +    return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; +  } + +  /* Assume that ngtcp2_transport_params_decode sets default value if +     active_connection_id_limit is omitted. */ +  if (params->active_connection_id_limit < +      NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  /* We assume that conn->dcid.current.cid is still the initial one. +     This requires that transport parameter must be fed into +     ngtcp2_conn as early as possible. */ +  if (!ngtcp2_cid_eq(&conn->dcid.current.cid, ¶ms->initial_scid)) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  if (params->max_udp_payload_size < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +    return NGTCP2_ERR_TRANSPORT_PARAM; +  } + +  if (conn->server) { +    if (params->original_dcid_present || +        params->stateless_reset_token_present || +        params->preferred_addr_present || params->retry_scid_present) { +      return NGTCP2_ERR_TRANSPORT_PARAM; +    } + +    if (params->version_info_present) { +      if (!vneg_available_versions_includes( +            params->version_info.available_versions, +            params->version_info.available_versionslen, +            params->version_info.chosen_version)) { +        return NGTCP2_ERR_TRANSPORT_PARAM; +      } + +      if (params->version_info.chosen_version != conn->client_chosen_version) { +        return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE; +      } + +      conn->negotiated_version = +        ngtcp2_conn_server_negotiate_version(conn, ¶ms->version_info); +      if (conn->negotiated_version != conn->client_chosen_version) { +        rv = conn_call_version_negotiation(conn, conn->negotiated_version, +                                           &conn->rcid); +        if (rv != 0) { +          return rv; +        } +      } +    } else { +      conn->negotiated_version = conn->client_chosen_version; +    } + +    conn->local.transport_params.version_info.chosen_version = +      conn->negotiated_version; + +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "the negotiated version is 0x%08x", +                    conn->negotiated_version); +  } else { +    rv = conn_client_validate_transport_params(conn, params); +    if (rv != 0) { +      return rv; +    } +  } + +  ngtcp2_log_remote_tp(&conn->log, params); + +  ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, +                                              NGTCP2_QLOG_SIDE_REMOTE); + +  if ((conn->server && conn->pktns.crypto.tx.ckm) || +      (!conn->server && conn->pktns.crypto.rx.ckm)) { +    ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem); +    conn->remote.transport_params = NULL; + +    rv = ngtcp2_transport_params_copy_new(&conn->remote.transport_params, +                                          params, conn->mem); +    if (rv != 0) { +      return rv; +    } +    conn_sync_stream_id_limit(conn); +    conn->tx.max_offset = conn->remote.transport_params->initial_max_data; +  } else { +    assert(!conn->remote.pending_transport_params); + +    rv = ngtcp2_transport_params_copy_new( +      &conn->remote.pending_transport_params, params, conn->mem); +    if (rv != 0) { +      return rv; +    } +  } + +  conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; + +  return 0; +} + +int ngtcp2_conn_decode_and_set_remote_transport_params(ngtcp2_conn *conn, +                                                       const uint8_t *data, +                                                       size_t datalen) { +  ngtcp2_transport_params params; +  int rv; + +  rv = ngtcp2_transport_params_decode(¶ms, data, datalen); +  if (rv != 0) { +    return rv; +  } + +  return ngtcp2_conn_set_remote_transport_params(conn, ¶ms); +} + +const ngtcp2_transport_params * +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn) { +  if (conn->remote.pending_transport_params) { +    return conn->remote.pending_transport_params; +  } + +  return conn->remote.transport_params; +} + +ngtcp2_ssize ngtcp2_conn_encode_0rtt_transport_params(ngtcp2_conn *conn, +                                                      uint8_t *dest, +                                                      size_t destlen) { +  ngtcp2_transport_params params, *src; + +  if (conn->server) { +    src = &conn->local.transport_params; +  } else { +    assert(conn->remote.transport_params); + +    src = conn->remote.transport_params; +  } + +  ngtcp2_transport_params_default(¶ms); + +  params.initial_max_streams_bidi = src->initial_max_streams_bidi; +  params.initial_max_streams_uni = src->initial_max_streams_uni; +  params.initial_max_stream_data_bidi_local = +    src->initial_max_stream_data_bidi_local; +  params.initial_max_stream_data_bidi_remote = +    src->initial_max_stream_data_bidi_remote; +  params.initial_max_stream_data_uni = src->initial_max_stream_data_uni; +  params.initial_max_data = src->initial_max_data; +  params.active_connection_id_limit = src->active_connection_id_limit; +  params.max_datagram_frame_size = src->max_datagram_frame_size; + +  if (conn->server) { +    params.max_idle_timeout = src->max_idle_timeout; +    params.max_udp_payload_size = src->max_udp_payload_size; +    params.disable_active_migration = src->disable_active_migration; +  } + +  return ngtcp2_transport_params_encode(dest, destlen, ¶ms); +} + +int ngtcp2_conn_decode_and_set_0rtt_transport_params(ngtcp2_conn *conn, +                                                     const uint8_t *data, +                                                     size_t datalen) { +  ngtcp2_transport_params params; +  int rv; + +  rv = ngtcp2_transport_params_decode(¶ms, data, datalen); +  if (rv != 0) { +    return rv; +  } + +  return ngtcp2_conn_set_0rtt_remote_transport_params(conn, ¶ms); +} + +int ngtcp2_conn_set_0rtt_remote_transport_params( +  ngtcp2_conn *conn, const ngtcp2_transport_params *params) { +  ngtcp2_transport_params *p; + +  assert(!conn->server); +  assert(!conn->remote.transport_params); + +  /* Assume that all pointer fields in p are NULL */ +  p = ngtcp2_mem_calloc(conn->mem, 1, sizeof(*p)); +  if (p == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  conn->remote.transport_params = p; + +  ngtcp2_transport_params_default(conn->remote.transport_params); + +  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; +  /* we might hit garbage, then set the sane default. */ +  p->active_connection_id_limit = +    ngtcp2_max_uint64(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, +                      params->active_connection_id_limit); +  p->max_datagram_frame_size = params->max_datagram_frame_size; + +  /* we might hit garbage, then set the sane default. */ +  if (params->max_udp_payload_size) { +    p->max_udp_payload_size = ngtcp2_max_uint64(NGTCP2_MAX_UDP_PAYLOAD_SIZE, +                                                params->max_udp_payload_size); +  } + +  /* These parameters are treated specially.  If server accepts early +     data, it must not set values for these parameters that are +     smaller than these remembered values. */ +  conn->early.transport_params.initial_max_streams_bidi = +    params->initial_max_streams_bidi; +  conn->early.transport_params.initial_max_streams_uni = +    params->initial_max_streams_uni; +  conn->early.transport_params.initial_max_stream_data_bidi_local = +    params->initial_max_stream_data_bidi_local; +  conn->early.transport_params.initial_max_stream_data_bidi_remote = +    params->initial_max_stream_data_bidi_remote; +  conn->early.transport_params.initial_max_stream_data_uni = +    params->initial_max_stream_data_uni; +  conn->early.transport_params.initial_max_data = params->initial_max_data; +  conn->early.transport_params.active_connection_id_limit = +    params->active_connection_id_limit; +  conn->early.transport_params.max_datagram_frame_size = +    params->max_datagram_frame_size; + +  conn_sync_stream_id_limit(conn); + +  conn->tx.max_offset = p->initial_max_data; + +  ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, conn->server, +                                              NGTCP2_QLOG_SIDE_REMOTE); + +  return 0; +} + +int ngtcp2_conn_set_local_transport_params_versioned( +  ngtcp2_conn *conn, int transport_params_version, +  const ngtcp2_transport_params *params) { +  ngtcp2_transport_params paramsbuf; + +  params = ngtcp2_transport_params_convert_to_latest( +    ¶msbuf, transport_params_version, params); + +  assert(conn->server); +  assert(params->active_connection_id_limit >= +         NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT); +  assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + +  if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) { +    return NGTCP2_ERR_INVALID_STATE; +  } + +  conn_set_local_transport_params(conn, params); + +  return 0; +} + +int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { +  const ngtcp2_mem *mem = conn->mem; +  ngtcp2_transport_params *params = &conn->local.transport_params; +  ngtcp2_scid *scident; +  int rv; + +  assert(1 == ngtcp2_ksl_len(&conn->scid.set)); + +  params->initial_scid = conn->oscid; +  params->initial_scid_present = 1; + +  if (conn->oscid.datalen == 0) { +    params->preferred_addr_present = 0; +  } + +  if (conn->server && params->preferred_addr_present) { +    scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); +    if (scident == NULL) { +      return NGTCP2_ERR_NOMEM; +    } + +    ngtcp2_scid_init(scident, 1, ¶ms->preferred_addr.cid); + +    rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); +    if (rv != 0) { +      ngtcp2_mem_free(mem, scident); +      return rv; +    } + +    conn->scid.last_seq = 1; +  } + +  conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset = +    params->initial_max_data; +  conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi; +  conn->remote.bidi.max_streams = params->initial_max_streams_bidi; +  conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni; +  conn->remote.uni.max_streams = params->initial_max_streams_uni; + +  conn->flags |= NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED; + +  ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, +                                              NGTCP2_QLOG_SIDE_LOCAL); + +  return 0; +} + +const ngtcp2_transport_params * +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn) { +  return &conn->local.transport_params; +} + +ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(ngtcp2_conn *conn, +                                                       uint8_t *dest, +                                                       size_t destlen) { +  return ngtcp2_transport_params_encode(dest, destlen, +                                        &conn->local.transport_params); +} + +int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, +                                 void *stream_user_data) { +  int rv; +  ngtcp2_strm *strm; + +  if (ngtcp2_conn_get_streams_bidi_left(conn) == 0) { +    return NGTCP2_ERR_STREAM_ID_BLOCKED; +  } + +  strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +  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_objalloc_strm_release(&conn->strm_objalloc, strm); +    return rv; +  } + +  *pstream_id = conn->local.bidi.next_stream_id; +  conn->local.bidi.next_stream_id += 4; + +  return 0; +} + +int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, +                                void *stream_user_data) { +  int rv; +  ngtcp2_strm *strm; + +  if (ngtcp2_conn_get_streams_uni_left(conn) == 0) { +    return NGTCP2_ERR_STREAM_ID_BLOCKED; +  } + +  strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); +  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_objalloc_strm_release(&conn->strm_objalloc, 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) { +  return ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); +} + +ngtcp2_ssize ngtcp2_conn_write_stream_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, +  uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, +  ngtcp2_tstamp ts) { +  ngtcp2_vec datav; + +  datav.len = datalen; +  datav.base = (uint8_t *)data; + +  return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi, +                                             dest, destlen, pdatalen, flags, +                                             stream_id, &datav, 1, ts); +} + +static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, +                                            ngtcp2_path *path, +                                            int pkt_info_version, +                                            ngtcp2_pkt_info *pi, uint8_t *dest, +                                            size_t destlen, ngtcp2_vmsg *vmsg, +                                            ngtcp2_tstamp ts) { +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_ssize nwrite; + +  nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, +                                  destlen, vmsg, ts); +  if (nwrite < 0) { +    return nwrite; +  } + +  if (cstat->bytes_in_flight >= cstat->cwnd) { +    conn->rst.is_cwnd_limited = 1; +  } else if (nwrite == 0 && conn_pacing_pkt_tx_allowed(conn, ts)) { +    conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; + +    if (conn->rst.app_limited == 0) { +      conn->rst.app_limited = cstat->max_tx_udp_payload_size; +    } +  } + +  return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, +  uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt, +  ngtcp2_tstamp ts) { +  ngtcp2_vmsg vmsg, *pvmsg; +  ngtcp2_strm *strm; +  int64_t datalen; + +  if (pdatalen) { +    *pdatalen = -1; +  } + +  if (stream_id != -1) { +    strm = ngtcp2_conn_find_stream(conn, stream_id); +    if (strm == NULL) { +      return NGTCP2_ERR_STREAM_NOT_FOUND; +    } + +    if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { +      return NGTCP2_ERR_STREAM_SHUT_WR; +    } + +    datalen = ngtcp2_vec_len_varint(datav, datavcnt); +    if (datalen == -1) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    if (datalen == 0 && !(flags & NGTCP2_WRITE_STREAM_FLAG_FIN) && +        (strm->flags & NGTCP2_STRM_FLAG_ANY_SENT)) { +      pvmsg = NULL; +    } else { +      if ((uint64_t)datalen > NGTCP2_MAX_VARINT - strm->tx.offset || +          (uint64_t)datalen > NGTCP2_MAX_VARINT - conn->tx.offset) { +        return NGTCP2_ERR_INVALID_ARGUMENT; +      } + +      vmsg.type = NGTCP2_VMSG_TYPE_STREAM; +      vmsg.stream.strm = strm; +      vmsg.stream.flags = flags; +      vmsg.stream.data = datav; +      vmsg.stream.datacnt = datavcnt; +      vmsg.stream.pdatalen = pdatalen; + +      pvmsg = &vmsg; +    } +  } else { +    pvmsg = NULL; +  } + +  return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, +                                 destlen, pvmsg, ts); +} + +ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, +  uint32_t flags, uint64_t dgram_id, const uint8_t *data, size_t datalen, +  ngtcp2_tstamp ts) { +  ngtcp2_vec datav; + +  datav.len = datalen; +  datav.base = (uint8_t *)data; + +  return ngtcp2_conn_writev_datagram_versioned(conn, path, pkt_info_version, pi, +                                               dest, destlen, paccepted, flags, +                                               dgram_id, &datav, 1, ts); +} + +ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, +  uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt, +  ngtcp2_tstamp ts) { +  ngtcp2_vmsg vmsg; +  int64_t datalen; + +  if (paccepted) { +    *paccepted = 0; +  } + +  if (conn->remote.transport_params == NULL || +      conn->remote.transport_params->max_datagram_frame_size == 0) { +    return NGTCP2_ERR_INVALID_STATE; +  } + +  datalen = ngtcp2_vec_len_varint(datav, datavcnt); +  if (datalen == -1 +#if UINT64_MAX > SIZE_MAX +      || (uint64_t)datalen > SIZE_MAX +#endif /* UINT64_MAX > SIZE_MAX */ +  ) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (conn->remote.transport_params->max_datagram_frame_size < +      ngtcp2_pkt_datagram_framelen((size_t)datalen)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM; +  vmsg.datagram.dgram_id = dgram_id; +  vmsg.datagram.flags = flags; +  vmsg.datagram.data = datav; +  vmsg.datagram.datacnt = datavcnt; +  vmsg.datagram.paccepted = paccepted; + +  return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, +                                 destlen, &vmsg, ts); +} + +ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, +                                    int pkt_info_version, ngtcp2_pkt_info *pi, +                                    uint8_t *dest, size_t destlen, +                                    ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { +  ngtcp2_ssize nwrite; +  size_t origlen; +  size_t origdestlen = destlen; +  int rv; +  uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; +  int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_ssize res = 0; +  uint64_t server_tx_left; +  int64_t prev_in_pkt_num = -1; +  ngtcp2_ksl_it it; +  ngtcp2_rtb_entry *rtbent; +  (void)pkt_info_version; + +  conn_update_timestamp(conn, ts); + +  if (path) { +    ngtcp2_path_copy(path, &conn->dcid.current.ps.path); +  } + +  origlen = destlen = +    conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + +  if (!ppe_pending && pi) { +    pi->ecn = NGTCP2_ECN_NOT_ECT; +  } + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +  case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: +    if (!conn_pacing_pkt_tx_allowed(conn, ts)) { +      assert(!ppe_pending); + +      return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); +    } + +    nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); +    /* We might be unable to write a packet because of depletion of +       congestion window budget, perhaps due to packet loss that +       shrinks the window drastically. */ +    if (nwrite <= 0) { +      return nwrite; +    } +    if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { +      return nwrite; +    } + +    assert(nwrite); +    assert(dest[0] & NGTCP2_HEADER_FORM_BIT); +    assert(conn->negotiated_version); + +    if (nwrite < NGTCP2_MAX_UDP_PAYLOAD_SIZE && +        ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) == +          NGTCP2_PKT_INITIAL) { +      wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +    } + +    res = nwrite; +    dest += nwrite; +    destlen -= (size_t)nwrite; +    /* Break here so that we can coalesces 1RTT packet. */ +    break; +  case NGTCP2_CS_SERVER_INITIAL: +  case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: +    if (!conn_pacing_pkt_tx_allowed(conn, ts)) { +      assert(!ppe_pending); + +      if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +        server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +        if (server_tx_left == 0) { +          return 0; +        } + +        origlen = (size_t)ngtcp2_min_uint64((uint64_t)origlen, server_tx_left); +      } + +      return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); +    } + +    if (!ppe_pending) { +      if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +        server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +        if (server_tx_left == 0) { +          if (cstat->loss_detection_timer != UINT64_MAX) { +            ngtcp2_log_info( +              &conn->log, NGTCP2_LOG_EVENT_LDC, +              "loss detection timer canceled due to amplification limit"); +            ngtcp2_conn_cancel_loss_detection_timer(conn); +          } + +          return 0; +        } + +        destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, server_tx_left); +      } + +      if (conn->in_pktns) { +        it = ngtcp2_rtb_head(&conn->in_pktns->rtb); +        if (!ngtcp2_ksl_it_end(&it)) { +          rtbent = ngtcp2_ksl_it_get(&it); +          prev_in_pkt_num = rtbent->hd.pkt_num; +        } +      } + +      nwrite = conn_write_handshake(conn, pi, dest, destlen, +                                    /* write_datalen = */ 0, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      res = nwrite; +      dest += nwrite; +      destlen -= (size_t)nwrite; + +      if (res < NGTCP2_MAX_UDP_PAYLOAD_SIZE && conn->in_pktns && nwrite > 0) { +        it = ngtcp2_rtb_head(&conn->in_pktns->rtb); +        if (!ngtcp2_ksl_it_end(&it)) { +          rtbent = ngtcp2_ksl_it_get(&it); +          if (rtbent->hd.pkt_num != prev_in_pkt_num && +              (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +            wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +          } +        } +      } +    } +    if (conn->pktns.crypto.tx.ckm == NULL) { +      return res; +    } +    break; +  case NGTCP2_CS_POST_HANDSHAKE: +    if (!conn_pacing_pkt_tx_allowed(conn, ts)) { +      assert(!ppe_pending); + +      if (conn->server && +          !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +        server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +        if (server_tx_left == 0) { +          return 0; +        } + +        origlen = (size_t)ngtcp2_min_uint64((uint64_t)origlen, server_tx_left); +      } + +      return conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts); +    } + +    break; +  case NGTCP2_CS_CLOSING: +    return NGTCP2_ERR_CLOSING; +  case NGTCP2_CS_DRAINING: +    return NGTCP2_ERR_DRAINING; +  default: +    return 0; +  } + +  assert(conn->pktns.crypto.tx.ckm); + +  if (conn_check_pkt_num_exhausted(conn)) { +    return NGTCP2_ERR_PKT_NUM_EXHAUSTED; +  } + +  if (vmsg) { +    switch (vmsg->type) { +    case NGTCP2_VMSG_TYPE_STREAM: +      if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { +        wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; +      } +      break; +    case NGTCP2_VMSG_TYPE_DATAGRAM: +      if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { +        wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; +      } +      break; +    default: +      break; +    } +  } + +  if (ppe_pending) { +    res = conn->pkt.hs_spktlen; +    if (conn->pkt.require_padding) { +      wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +    } +    /* dest and destlen have already been adjusted in ppe in the first +       run.  They are adjusted for probe packet later. */ +    nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg, +                            NGTCP2_PKT_1RTT, wflags, ts); +    goto fin; +  } else { +    conn->pkt.require_padding = +      (wflags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING); + +    if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { +      rv = conn_prepare_key_update(conn, ts); +      if (rv != 0) { +        return rv; +      } +    } + +    if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { +      destlen = 0; +    } else { +      if (res == 0) { +        nwrite = +          conn_write_path_response(conn, path, pi, dest, origdestlen, ts); +        if (nwrite) { +          goto fin; +        } + +        if (conn->pv) { +          nwrite = +            conn_write_path_challenge(conn, path, pi, dest, origdestlen, ts); +          if (nwrite) { +            goto fin; +          } +        } + +        if (conn->pmtud && +            (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) && +            (!conn->hs_pktns || +             ngtcp2_strm_streamfrq_empty(&conn->hs_pktns->crypto.strm))) { +          nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts); +          if (nwrite) { +            goto fin; +          } +        } +      } +    } + +    if (conn->server && +        !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { +      server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +      origlen = (size_t)ngtcp2_min_uint64((uint64_t)origlen, server_tx_left); +      destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, server_tx_left); + +      if (server_tx_left == 0 && +          conn->cstat.loss_detection_timer != UINT64_MAX) { +        ngtcp2_log_info( +          &conn->log, NGTCP2_LOG_EVENT_LDC, +          "loss detection timer canceled due to amplification limit"); +        ngtcp2_conn_cancel_loss_detection_timer(conn); +      } +    } +  } + +  if (res == 0) { +    if (conn_handshake_remnants_left(conn)) { +      if (conn_handshake_probe_left(conn) || +          /* Allow exceeding CWND if an Handshake packet needs to be +             sent in order to avoid dead lock.  In some situation, +             typically for client, 1 RTT packets may occupy in-flight +             bytes (e.g., some large requests and PMTUD), and +             Handshake packet loss shrinks CWND, and we may get in the +             situation that we are unable to send Handshake packet. */ +          (conn->hs_pktns->rtb.num_pto_eliciting == 0 && +           !ngtcp2_strm_streamfrq_empty(&conn->hs_pktns->crypto.strm))) { +        destlen = origlen; +      } +      nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, +                                         /* write_datalen = */ 0, ts); +      if (nwrite < 0) { +        return nwrite; +      } +      if (nwrite > 0) { +        res = nwrite; +        dest += nwrite; +        destlen -= (size_t)nwrite; +      } else if (destlen == 0) { +        res = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); +        if (res) { +          return res; +        } +      } +    } +  } + +  if (conn->pktns.rtb.probe_pkt_left) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "transmit probe pkt left=%zu", +                    conn->pktns.rtb.probe_pkt_left); + +    nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg, +                            NGTCP2_PKT_1RTT, wflags, ts); + +    goto fin; +  } + +  nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg, +                          NGTCP2_PKT_1RTT, wflags, ts); +  if (nwrite) { +    assert(nwrite != NGTCP2_ERR_NOBUF); +    goto fin; +  } + +  if (res == 0) { +    nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts); +  } + +fin: +  if (nwrite >= 0) { +    res += nwrite; +    return res; +  } + +  switch (nwrite) { +  case NGTCP2_ERR_STREAM_DATA_BLOCKED: +    if (!(wflags & NGTCP2_WRITE_PKT_FLAG_MORE)) { +      if (res) { +        return res; +      } + +      break; +    } +    /* fall through */ +  case NGTCP2_ERR_WRITE_MORE: +    conn->pkt.hs_spktlen = res; +    break; +  } + +  return nwrite; +} + +static ngtcp2_ssize +conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, +                            uint8_t *dest, size_t destlen, uint8_t pkt_type, +                            uint64_t error_code, const uint8_t *reason, +                            size_t reasonlen, 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 flags = NGTCP2_WRITE_PKT_FLAG_NONE; + +  fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; +  fr.connection_close.error_code = error_code; +  fr.connection_close.frame_type = 0; +  fr.connection_close.reasonlen = reasonlen; +  fr.connection_close.reason = (uint8_t *)reason; + +  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && +      pkt_type != NGTCP2_PKT_INITIAL) { +    if (in_pktns && conn->server) { +      nwrite = ngtcp2_conn_write_single_frame_pkt( +        conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, NGTCP2_WRITE_PKT_FLAG_NONE, +        &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      dest += nwrite; +      destlen -= (size_t)nwrite; +      res += nwrite; +    } + +    if (pkt_type != NGTCP2_PKT_HANDSHAKE && hs_pktns && +        hs_pktns->crypto.tx.ckm) { +      nwrite = ngtcp2_conn_write_single_frame_pkt( +        conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, +        NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr, +        NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); +      if (nwrite < 0) { +        return nwrite; +      } + +      dest += nwrite; +      destlen -= (size_t)nwrite; +      res += nwrite; +    } +  } + +  if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) { +    flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; +  } + +  nwrite = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr, +    NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + +  if (nwrite < 0) { +    return nwrite; +  } + +  res += nwrite; + +  if (res == 0) { +    return NGTCP2_ERR_NOBUF; +  } + +  return res; +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( +  ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, +  size_t destlen, uint64_t error_code, const uint8_t *reason, size_t reasonlen, +  ngtcp2_tstamp ts) { +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_pktns *hs_pktns = conn->hs_pktns; +  uint8_t pkt_type; +  ngtcp2_ssize nwrite; +  uint64_t server_tx_left; + +  if (conn_check_pkt_num_exhausted(conn)) { +    return NGTCP2_ERR_PKT_NUM_EXHAUSTED; +  } + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +    return NGTCP2_ERR_INVALID_STATE; +  case NGTCP2_CS_CLOSING: +  case NGTCP2_CS_DRAINING: +    return 0; +  default: +    break; +  } + +  if (path) { +    ngtcp2_path_copy(path, &conn->dcid.current.ps.path); +  } + +  destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + +  if (pi) { +    pi->ecn = NGTCP2_ECN_NOT_ECT; +  } + +  if (conn->server) { +    server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +    destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, server_tx_left); +  } + +  if (conn->state == NGTCP2_CS_POST_HANDSHAKE || +      (conn->server && conn->pktns.crypto.tx.ckm)) { +    pkt_type = NGTCP2_PKT_1RTT; +  } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { +    pkt_type = NGTCP2_PKT_HANDSHAKE; +  } else if (in_pktns && in_pktns->crypto.tx.ckm) { +    pkt_type = NGTCP2_PKT_INITIAL; +  } else { +    /* This branch is taken if server has not read any Initial packet +       from client. */ +    return NGTCP2_ERR_INVALID_STATE; +  } + +  nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type, +                                       error_code, reason, reasonlen, ts); +  if (nwrite < 0) { +    return nwrite; +  } + +  conn->state = NGTCP2_CS_CLOSING; + +  return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( +  ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, +  size_t destlen, uint64_t app_error_code, const uint8_t *reason, +  size_t reasonlen, ngtcp2_tstamp ts) { +  ngtcp2_ssize nwrite; +  ngtcp2_ssize res = 0; +  ngtcp2_frame fr; +  uint64_t server_tx_left; + +  if (conn_check_pkt_num_exhausted(conn)) { +    return NGTCP2_ERR_PKT_NUM_EXHAUSTED; +  } + +  switch (conn->state) { +  case NGTCP2_CS_CLIENT_INITIAL: +    return NGTCP2_ERR_INVALID_STATE; +  case NGTCP2_CS_CLOSING: +  case NGTCP2_CS_DRAINING: +    return 0; +  default: +    break; +  } + +  if (path) { +    ngtcp2_path_copy(path, &conn->dcid.current.ps.path); +  } + +  destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen); + +  if (pi) { +    pi->ecn = NGTCP2_ECN_NOT_ECT; +  } + +  if (conn->server) { +    server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); +    destlen = (size_t)ngtcp2_min_uint64((uint64_t)destlen, server_tx_left); +  } + +  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { +    nwrite = conn_write_connection_close( +      conn, pi, dest, destlen, +      conn->hs_pktns->crypto.tx.ckm ? NGTCP2_PKT_HANDSHAKE : NGTCP2_PKT_INITIAL, +      NGTCP2_APPLICATION_ERROR, NULL, 0, ts); +    if (nwrite < 0) { +      return nwrite; +    } +    res = nwrite; +    dest += nwrite; +    destlen -= (size_t)nwrite; +  } + +  if (conn->state != NGTCP2_CS_POST_HANDSHAKE && +      (!conn->server || !conn->pktns.crypto.tx.ckm)) { +    return res; +  } + +  assert(conn->pktns.crypto.tx.ckm); + +  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 = reasonlen; +  fr.connection_close.reason = (uint8_t *)reason; + +  nwrite = ngtcp2_conn_write_single_frame_pkt( +    conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE, +    &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + +  if (nwrite < 0) { +    return nwrite; +  } + +  res += nwrite; + +  if (res == 0) { +    return NGTCP2_ERR_NOBUF; +  } + +  conn->state = NGTCP2_CS_CLOSING; + +  return res; +} + +static void ccerr_init(ngtcp2_ccerr *ccerr, ngtcp2_ccerr_type type, +                       uint64_t error_code, const uint8_t *reason, +                       size_t reasonlen) { +  ccerr->type = type; +  ccerr->error_code = error_code; +  ccerr->frame_type = 0; +  ccerr->reason = (uint8_t *)reason; +  ccerr->reasonlen = reasonlen; +} + +void ngtcp2_ccerr_default(ngtcp2_ccerr *ccerr) { +  ccerr_init(ccerr, NGTCP2_CCERR_TYPE_TRANSPORT, NGTCP2_NO_ERROR, NULL, 0); +} + +void ngtcp2_ccerr_set_transport_error(ngtcp2_ccerr *ccerr, uint64_t error_code, +                                      const uint8_t *reason, size_t reasonlen) { +  ccerr_init(ccerr, NGTCP2_CCERR_TYPE_TRANSPORT, error_code, reason, reasonlen); +} + +void ngtcp2_ccerr_set_liberr(ngtcp2_ccerr *ccerr, int liberr, +                             const uint8_t *reason, size_t reasonlen) { +  switch (liberr) { +  case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: +    ccerr_init(ccerr, NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION, NGTCP2_NO_ERROR, +               reason, reasonlen); + +    return; +  case NGTCP2_ERR_IDLE_CLOSE: +    ccerr_init(ccerr, NGTCP2_CCERR_TYPE_IDLE_CLOSE, NGTCP2_NO_ERROR, reason, +               reasonlen); + +    return; +  case NGTCP2_ERR_DROP_CONN: +    ccerr_init(ccerr, NGTCP2_CCERR_TYPE_DROP_CONN, NGTCP2_NO_ERROR, reason, +               reasonlen); + +    return; +  case NGTCP2_ERR_RETRY: +    ccerr_init(ccerr, NGTCP2_CCERR_TYPE_RETRY, NGTCP2_NO_ERROR, reason, +               reasonlen); + +    return; +  }; + +  ngtcp2_ccerr_set_transport_error( +    ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason, +    reasonlen); +} + +void ngtcp2_ccerr_set_tls_alert(ngtcp2_ccerr *ccerr, uint8_t tls_alert, +                                const uint8_t *reason, size_t reasonlen) { +  ngtcp2_ccerr_set_transport_error(ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, +                                   reason, reasonlen); +} + +void ngtcp2_ccerr_set_application_error(ngtcp2_ccerr *ccerr, +                                        uint64_t error_code, +                                        const uint8_t *reason, +                                        size_t reasonlen) { +  ccerr_init(ccerr, NGTCP2_CCERR_TYPE_APPLICATION, error_code, reason, +             reasonlen); +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned( +  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, +  ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, const ngtcp2_ccerr *ccerr, +  ngtcp2_tstamp ts) { +  (void)pkt_info_version; + +  conn_update_timestamp(conn, ts); + +  switch (ccerr->type) { +  case NGTCP2_CCERR_TYPE_TRANSPORT: +    return ngtcp2_conn_write_connection_close_pkt( +      conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, +      ccerr->reasonlen, ts); +  case NGTCP2_CCERR_TYPE_APPLICATION: +    return ngtcp2_conn_write_application_close_pkt( +      conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason, +      ccerr->reasonlen, ts); +  default: +    return 0; +  } +} + +int ngtcp2_conn_in_closing_period(ngtcp2_conn *conn) { +  return conn->state == NGTCP2_CS_CLOSING; +} + +int ngtcp2_conn_in_draining_period(ngtcp2_conn *conn) { +  return conn->state == NGTCP2_CS_DRAINING; +} + +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) { +  int rv; + +  rv = conn_call_stream_close(conn, strm); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_map_remove(&conn->strms, (ngtcp2_map_key_type)strm->stream_id); +  if (rv != 0) { +    assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); +    return rv; +  } + +  if (ngtcp2_strm_is_tx_queued(strm)) { +    ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); +  } + +  ngtcp2_strm_free(strm); +  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm); + +  return 0; +} + +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, +                                          ngtcp2_strm *strm) { +  if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == +        NGTCP2_STRM_FLAG_SHUT_RDWR && +      ((strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) || +       ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) && +      (((strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM) && +        (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_ACKED)) || +       ngtcp2_strm_is_all_tx_data_fin_acked(strm))) { +    return ngtcp2_conn_close_stream(conn, strm); +  } +  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) { +  ngtcp2_strm_set_app_error_code(strm, app_error_code); + +  if ((strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM) || +      ngtcp2_strm_is_all_tx_data_fin_acked(strm)) { +    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_RESET_STREAM; + +  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) { +  ngtcp2_strm_set_app_error_code(strm, app_error_code); + +  if (strm->flags & +      (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RESET_STREAM_RECVED)) { +    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_RESET_STREAM_RECVED)) { +    ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - +                                          ngtcp2_strm_rx_offset(strm)); +  } + +  strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; + +  ngtcp2_strm_discard_reordered_data(strm); + +  return conn_stop_sending(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, uint32_t flags, +                                int64_t stream_id, uint64_t app_error_code) { +  int rv; +  ngtcp2_strm *strm; +  (void)flags; + +  strm = ngtcp2_conn_find_stream(conn, stream_id); +  if (strm == NULL) { +    return 0; +  } + +  if (bidi_stream(stream_id) || !conn_local_stream(conn, stream_id)) { +    rv = conn_shutdown_stream_read(conn, strm, app_error_code); +    if (rv != 0) { +      return rv; +    } +  } + +  if (bidi_stream(stream_id) || conn_local_stream(conn, stream_id)) { +    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, uint32_t flags, +                                      int64_t stream_id, +                                      uint64_t app_error_code) { +  ngtcp2_strm *strm; +  (void)flags; + +  if (!bidi_stream(stream_id) && !conn_local_stream(conn, stream_id)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  strm = ngtcp2_conn_find_stream(conn, stream_id); +  if (strm == NULL) { +    return 0; +  } + +  return conn_shutdown_stream_write(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, uint32_t flags, +                                     int64_t stream_id, +                                     uint64_t app_error_code) { +  ngtcp2_strm *strm; +  (void)flags; + +  if (!bidi_stream(stream_id) && conn_local_stream(conn, stream_id)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  strm = ngtcp2_conn_find_stream(conn, stream_id); +  if (strm == NULL) { +    return 0; +  } + +  return conn_shutdown_stream_read(conn, strm, app_error_code); +} + +/* + * conn_extend_max_stream_offset extends stream level flow control + * window by |datalen| of the stream denoted by |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, +                                         uint64_t datalen) { +  ngtcp2_strm *top; + +  if (datalen > NGTCP2_MAX_VARINT || +      strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { +    strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT; +  } else { +    strm->rx.unsent_max_offset += datalen; +  } + +  if (!(strm->flags & +        (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) && +      !ngtcp2_strm_is_tx_queued(strm) && +      conn_should_send_max_stream_data(conn, strm)) { +    if (!ngtcp2_pq_empty(&conn->tx.strmq)) { +      top = ngtcp2_conn_tx_strmq_top(conn); +      strm->cycle = top->cycle; +    } +    strm->cycle = conn_tx_strmq_first_cycle(conn); +    return ngtcp2_conn_tx_strmq_push(conn, strm); +  } + +  return 0; +} + +int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, +                                         uint64_t datalen) { +  ngtcp2_strm *strm; + +  if (!bidi_stream(stream_id) && conn_local_stream(conn, stream_id)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  strm = ngtcp2_conn_find_stream(conn, stream_id); +  if (strm == NULL) { +    return 0; +  } + +  return conn_extend_max_stream_offset(conn, strm, datalen); +} + +void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) { +  if (NGTCP2_MAX_VARINT < datalen || +      conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { +    conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT; +    return; +  } + +  conn->rx.unsent_max_offset += datalen; +} + +void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { +  handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); +} + +void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { +  handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); +} + +const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { +  return &conn->dcid.current.cid; +} + +const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) { +  return &conn->rcid; +} + +uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) { +  return conn->client_chosen_version; +} + +uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { +  return conn->negotiated_version; +} + +static int delete_strms_pq_each(void *data, void *ptr) { +  ngtcp2_conn *conn = ptr; +  ngtcp2_strm *s = data; + +  if (ngtcp2_strm_is_tx_queued(s)) { +    ngtcp2_pq_remove(&conn->tx.strmq, &s->pe); +  } + +  ngtcp2_strm_free(s); +  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s); + +  return 0; +} + +/* + * conn_discard_early_data_state discards any connection states which + * are altered by any operations during early data transfer. + */ +static void conn_discard_early_data_state(ngtcp2_conn *conn) { +  ngtcp2_frame_chain **pfrc, *frc; + +  ngtcp2_rtb_remove_early_data(&conn->pktns.rtb, &conn->cstat); + +  ngtcp2_map_each(&conn->strms, delete_strms_pq_each, conn); +  ngtcp2_map_clear(&conn->strms); + +  conn->tx.offset = 0; +  conn->tx.last_blocked_offset = UINT64_MAX; + +  conn->rx.unsent_max_offset = conn->rx.max_offset = +    conn->local.transport_params.initial_max_data; + +  conn->remote.bidi.unsent_max_streams = conn->remote.bidi.max_streams = +    conn->local.transport_params.initial_max_streams_bidi; + +  conn->remote.uni.unsent_max_streams = conn->remote.uni.max_streams = +    conn->local.transport_params.initial_max_streams_uni; + +  if (conn->server) { +    conn->local.bidi.next_stream_id = 1; +    conn->local.uni.next_stream_id = 3; +  } else { +    conn->local.bidi.next_stream_id = 0; +    conn->local.uni.next_stream_id = 2; +  } + +  for (pfrc = &conn->pktns.tx.frq; *pfrc;) { +    frc = *pfrc; +    *pfrc = (*pfrc)->next; +    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +  } +} + +int ngtcp2_conn_tls_early_data_rejected(ngtcp2_conn *conn) { +  if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { +    return 0; +  } + +  conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + +  conn_discard_early_data_state(conn); + +  if (conn->callbacks.tls_early_data_rejected) { +    return conn->callbacks.tls_early_data_rejected(conn, conn->user_data); +  } + +  if (conn->early.ckm) { +    conn_discard_early_key(conn); +  } + +  return 0; +} + +int ngtcp2_conn_get_tls_early_data_rejected(ngtcp2_conn *conn) { +  return (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) != 0; +} + +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, +                           ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { +  ngtcp2_conn_stat *cstat = &conn->cstat; + +  if (cstat->min_rtt == UINT64_MAX) { +    cstat->latest_rtt = rtt; +    cstat->min_rtt = rtt; +    cstat->smoothed_rtt = rtt; +    cstat->rttvar = rtt / 2; +    cstat->first_rtt_sample_ts = ts; +  } else { +    if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { +      assert(conn->remote.transport_params); + +      ack_delay = ngtcp2_min_uint64( +        ack_delay, conn->remote.transport_params->max_ack_delay); +    } else if (ack_delay > 0 && rtt >= cstat->min_rtt && +               rtt < cstat->min_rtt + ack_delay) { +      /* Ignore RTT sample if adjusting ack_delay causes the sample +         less than min_rtt before handshake confirmation. */ +      ngtcp2_log_info( +        &conn->log, NGTCP2_LOG_EVENT_LDC, +        "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64 +        " min_rtt=%" PRIu64 " ack_delay=%" PRIu64, +        rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS, +        ack_delay / NGTCP2_MILLISECONDS); +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    cstat->latest_rtt = rtt; +    cstat->min_rtt = ngtcp2_min_uint64(cstat->min_rtt, rtt); + +    if (rtt >= cstat->min_rtt + ack_delay) { +      rtt -= ack_delay; +    } + +    cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt +                                            ? rtt - cstat->smoothed_rtt +                                            : cstat->smoothed_rtt - rtt)) / +                    4; +    cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8; +  } + +  ngtcp2_log_info( +    &conn->log, NGTCP2_LOG_EVENT_LDC, +    "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64 +    " rttvar=%" PRIu64 " ack_delay=%" PRIu64, +    cstat->latest_rtt / NGTCP2_MILLISECONDS, +    cstat->min_rtt / NGTCP2_MILLISECONDS, +    cstat->smoothed_rtt / NGTCP2_MILLISECONDS, +    cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS); + +  return 0; +} + +void ngtcp2_conn_get_conn_info_versioned(ngtcp2_conn *conn, +                                         int conn_info_version, +                                         ngtcp2_conn_info *cinfo) { +  const ngtcp2_conn_stat *cstat = &conn->cstat; +  (void)conn_info_version; + +  cinfo->latest_rtt = cstat->latest_rtt; +  cinfo->min_rtt = cstat->min_rtt; +  cinfo->smoothed_rtt = cstat->smoothed_rtt; +  cinfo->rttvar = cstat->rttvar; +  cinfo->cwnd = cstat->cwnd; +  cinfo->ssthresh = cstat->ssthresh; +  cinfo->bytes_in_flight = cstat->bytes_in_flight; +} + +static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn, +                                         ngtcp2_tstamp *ploss_time, +                                         ngtcp2_pktns **ppktns) { +  ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns}; +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_duration *loss_time = cstat->loss_time + 1; +  ngtcp2_tstamp earliest_loss_time = cstat->loss_time[NGTCP2_PKTNS_ID_INITIAL]; +  ngtcp2_pktns *pktns = conn->in_pktns; +  size_t i; + +  for (i = 0; i < ngtcp2_arraylen(ns); ++i) { +    if (ns[i] == NULL || loss_time[i] >= earliest_loss_time) { +      continue; +    } + +    earliest_loss_time = loss_time[i]; +    pktns = ns[i]; +  } + +  if (ploss_time) { +    *ploss_time = earliest_loss_time; +  } +  if (ppktns) { +    *ppktns = pktns; +  } +} + +static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn, +                                                  ngtcp2_tstamp ts) { +  ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; +  size_t i; +  ngtcp2_tstamp earliest_ts = UINT64_MAX, t; +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_tstamp *times = cstat->last_tx_pkt_ts; +  ngtcp2_duration duration = +    compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) * +    (1ULL << cstat->pto_count); + +  for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) { +    if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 || +        (times[i] == UINT64_MAX || +         (i == NGTCP2_PKTNS_ID_APPLICATION && +          !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { +      continue; +    } + +    t = times[i] + duration; + +    if (i == NGTCP2_PKTNS_ID_APPLICATION) { +      assert(conn->remote.transport_params); +      t += conn->remote.transport_params->max_ack_delay * +           (1ULL << cstat->pto_count); +    } + +    if (t < earliest_ts) { +      earliest_ts = t; +    } +  } + +  if (earliest_ts == UINT64_MAX) { +    return ts + duration; +  } + +  return earliest_ts; +} + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_conn_stat *cstat = &conn->cstat; +  ngtcp2_duration timeout; +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_pktns *hs_pktns = conn->hs_pktns; +  ngtcp2_pktns *pktns = &conn->pktns; +  ngtcp2_tstamp earliest_loss_time; + +  conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL); + +  if (earliest_loss_time != UINT64_MAX) { +    cstat->loss_detection_timer = earliest_loss_time; + +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, +                    "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", +                    cstat->loss_detection_timer); +    return; +  } + +  if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) && +      (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) && +      (pktns->rtb.num_pto_eliciting == 0 || +       !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) && +      (conn->server || +       (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | +                       NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { +    if (cstat->loss_detection_timer != UINT64_MAX) { +      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, +                      "loss detection timer canceled"); +      ngtcp2_conn_cancel_loss_detection_timer(conn); +    } +    return; +  } + +  cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts); + +  timeout = +    cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0; + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, +                  "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64, +                  cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS); +} + +void ngtcp2_conn_cancel_loss_detection_timer(ngtcp2_conn *conn) { +  ngtcp2_conn_stat *cstat = &conn->cstat; + +  cstat->loss_detection_timer = UINT64_MAX; +  cstat->pto_count = 0; +} + +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_conn_stat *cstat = &conn->cstat; +  int rv; +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_pktns *hs_pktns = conn->hs_pktns; +  ngtcp2_tstamp earliest_loss_time; +  ngtcp2_pktns *loss_pktns = NULL; + +  switch (conn->state) { +  case NGTCP2_CS_CLOSING: +  case NGTCP2_CS_DRAINING: +    ngtcp2_conn_cancel_loss_detection_timer(conn); +    return 0; +  default: +    break; +  } + +  if (cstat->loss_detection_timer == UINT64_MAX) { +    return 0; +  } + +  conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns); + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, +                  "loss detection timer fired"); + +  if (earliest_loss_time != UINT64_MAX) { +    assert(loss_pktns); + +    rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts); +    if (rv != 0) { +      return rv; +    } +    ngtcp2_conn_set_loss_detection_timer(conn, ts); +    return 0; +  } + +  if (!conn->server && !conn_is_tls_handshake_completed(conn)) { +    if (hs_pktns->crypto.tx.ckm) { +      hs_pktns->rtb.probe_pkt_left = 1; +    } else { +      in_pktns->rtb.probe_pkt_left = 1; +    } +  } else { +    if (in_pktns && in_pktns->rtb.num_pto_eliciting) { +      in_pktns->rtb.probe_pkt_left = 1; + +      assert(hs_pktns); + +      if (conn->server && hs_pktns->rtb.num_pto_eliciting) { +        /* let server coalesce packets */ +        hs_pktns->rtb.probe_pkt_left = 1; +      } +    } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) { +      hs_pktns->rtb.probe_pkt_left = 2; +    } else { +      conn->pktns.rtb.probe_pkt_left = 2; +    } +  } + +  ++cstat->pto_count; + +  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, "pto_count=%zu", +                  cstat->pto_count); + +  ngtcp2_conn_set_loss_detection_timer(conn, ts); + +  return 0; +} + +static int conn_buffer_crypto_data(ngtcp2_conn *conn, const uint8_t **pdata, +                                   ngtcp2_pktns *pktns, const uint8_t *data, +                                   size_t datalen) { +  int rv; +  ngtcp2_buf_chain **pbufchain = &pktns->crypto.tx.data; + +  if (*pbufchain) { +    for (; (*pbufchain)->next; pbufchain = &(*pbufchain)->next) +      ; + +    if (ngtcp2_buf_left(&(*pbufchain)->buf) < datalen) { +      pbufchain = &(*pbufchain)->next; +    } +  } + +  if (!*pbufchain) { +    rv = ngtcp2_buf_chain_new(pbufchain, ngtcp2_max_size(1024, datalen), +                              conn->mem); +    if (rv != 0) { +      return rv; +    } +  } + +  *pdata = (*pbufchain)->buf.last; +  (*pbufchain)->buf.last = ngtcp2_cpymem((*pbufchain)->buf.last, data, datalen); + +  return 0; +} + +int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, +                                   ngtcp2_encryption_level encryption_level, +                                   const uint8_t *data, const size_t datalen) { +  ngtcp2_pktns *pktns; +  ngtcp2_frame_chain *frc; +  ngtcp2_stream *fr; +  int rv; + +  if (datalen == 0) { +    return 0; +  } + +  switch (encryption_level) { +  case NGTCP2_ENCRYPTION_LEVEL_INITIAL: +    assert(conn->in_pktns); +    pktns = conn->in_pktns; +    break; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    assert(conn->hs_pktns); +    pktns = conn->hs_pktns; +    break; +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    pktns = &conn->pktns; +    break; +  default: +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  rv = conn_buffer_crypto_data(conn, &data, pktns, data, datalen); +  if (rv != 0) { +    return rv; +  } + +  rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc); +  if (rv != 0) { +    return rv; +  } + +  fr = &frc->fr.stream; + +  fr->type = NGTCP2_FRAME_CRYPTO; +  fr->flags = 0; +  fr->fin = 0; +  fr->stream_id = 0; +  fr->offset = pktns->crypto.tx.offset; +  fr->datacnt = 1; +  fr->data[0].len = datalen; +  fr->data[0].base = (uint8_t *)data; + +  rv = ngtcp2_strm_streamfrq_push(&pktns->crypto.strm, frc); +  if (rv != 0) { +    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem); +    return rv; +  } + +  pktns->crypto.strm.tx.offset += datalen; +  pktns->crypto.tx.offset += datalen; + +  return 0; +} + +int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, +                                 size_t tokenlen) { +  int rv; +  ngtcp2_frame_chain *nfrc; + +  assert(conn->server); +  assert(token); +  assert(tokenlen); + +  rv = ngtcp2_frame_chain_new_token_objalloc_new( +    &nfrc, token, tokenlen, &conn->frc_objalloc, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  nfrc->next = conn->pktns.tx.frq; +  conn->pktns.tx.frq = nfrc; + +  return 0; +} + +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) { +  assert(!ngtcp2_pq_empty(&conn->tx.strmq)); +  return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); +} + +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) { +  ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn); +  assert(strm); +  ngtcp2_pq_pop(&conn->tx.strmq); +  strm->pe.index = NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { +  return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); +} + +static int conn_has_uncommitted_preferred_addr_cid(ngtcp2_conn *conn) { +  return conn->server && +         !(conn->flags & NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED) && +         conn->oscid.datalen && +         conn->local.transport_params.preferred_addr_present; +} + +size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { +  ngtcp2_cid *origdest = dest; +  ngtcp2_ksl_it it; +  ngtcp2_scid *scid; + +  if (dest == NULL) { +    return ngtcp2_ksl_len(&conn->scid.set) + +           (size_t)conn_has_uncommitted_preferred_addr_cid(conn); +  } + +  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; +  } + +  if (conn_has_uncommitted_preferred_addr_cid(conn)) { +    *dest++ = conn->local.transport_params.preferred_addr.cid; +  } + +  return (size_t)(dest - origdest); +} + +static size_t conn_get_num_active_dcid(ngtcp2_conn *conn) { +  size_t n = 1; /* for conn->dcid.current */ +  ngtcp2_pv *pv = conn->pv; + +  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.rb); + +  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); +  if ((dest->token_present = +         (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) { +    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)) { +    return 0; +  } + +  if (dest == NULL) { +    return conn_get_num_active_dcid(conn); +  } + +  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.rb); +  for (i = 0; i < len; ++i) { +    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 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 <= +         (ngtcp2_socklen)sizeof(conn->dcid.current.ps.local_addrbuf)); +  ngtcp2_addr_copy(dest, addr); +} + +void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) { +  conn->dcid.current.ps.path.user_data = path_user_data; +} + +const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { +  return &conn->dcid.current.ps.path; +} + +size_t ngtcp2_conn_get_max_tx_udp_payload_size(ngtcp2_conn *conn) { +  return conn->local.settings.max_tx_udp_payload_size; +} + +size_t ngtcp2_conn_get_path_max_tx_udp_payload_size(ngtcp2_conn *conn) { +  if (conn->local.settings.no_tx_udp_payload_size_shaping) { +    return ngtcp2_conn_get_max_tx_udp_payload_size(conn); +  } + +  return conn->dcid.current.max_udp_payload_size; +} + +static int conn_initiate_migration_precheck(ngtcp2_conn *conn, +                                            const ngtcp2_addr *local_addr) { +  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || +      conn->remote.transport_params->disable_active_migration || +      conn->dcid.current.cid.datalen == 0 || +      (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR))) { +    return NGTCP2_ERR_INVALID_STATE; +  } + +  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { +    return NGTCP2_ERR_CONN_ID_BLOCKED; +  } + +  if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, local_addr)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  return 0; +} + +int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, +                                             const ngtcp2_path *path, +                                             ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_dcid *dcid; +  ngtcp2_pv *pv; + +  assert(!conn->server); + +  conn_update_timestamp(conn, ts); + +  rv = conn_initiate_migration_precheck(conn, &path->local); +  if (rv != 0) { +    return rv; +  } + +  ngtcp2_conn_stop_pmtud(conn); + +  if (conn->pv) { +    rv = conn_abort_pv(conn, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  rv = conn_retire_dcid(conn, &conn->dcid.current, ts); +  if (rv != 0) { +    return rv; +  } + +  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +  ngtcp2_dcid_set_path(dcid, path); + +  ngtcp2_dcid_copy(&conn->dcid.current, dcid); +  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + +  conn_reset_congestion_state(conn, ts); +  conn_reset_ecn_validation_state(conn); + +  /* TODO It might be better to add a new flag which indicates that a +     connection should be closed if this path validation failed.  The +     current design allows an application to continue, by migrating +     into yet another path. */ +  rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), +                     NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  conn->pv = pv; + +  return conn_call_activate_dcid(conn, &conn->dcid.current); +} + +int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, +                                   ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_dcid *dcid; +  ngtcp2_pv *pv; + +  assert(!conn->server); + +  conn_update_timestamp(conn, ts); + +  rv = conn_initiate_migration_precheck(conn, &path->local); +  if (rv != 0) { +    return rv; +  } + +  if (conn->pv) { +    rv = conn_abort_pv(conn, ts); +    if (rv != 0) { +      return rv; +    } +  } + +  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); +  ngtcp2_dcid_set_path(dcid, path); + +  rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), +                     NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); +  if (rv != 0) { +    return rv; +  } + +  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); +  conn->pv = pv; + +  return conn_call_activate_dcid(conn, &pv->dcid); +} + +uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { +  return conn->tx.max_offset - conn->tx.offset; +} + +uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn, +                                              int64_t stream_id) { +  ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + +  if (strm == NULL) { +    return 0; +  } + +  return strm->tx.max_offset - strm->tx.offset; +} + +uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) { +  uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id); + +  return n > conn->local.bidi.max_streams +           ? 0 +           : conn->local.bidi.max_streams - n + 1; +} + +uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { +  uint64_t n = ngtcp2_ord_stream_id(conn->local.uni.next_stream_id); + +  return n > conn->local.uni.max_streams ? 0 +                                         : conn->local.uni.max_streams - n + 1; +} + +uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) { +  uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; +  uint64_t cwnd = conn_get_cwnd(conn); + +  if (cwnd > bytes_in_flight) { +    return cwnd - bytes_in_flight; +  } + +  return 0; +} + +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_is_tls_handshake_completed(conn) || +      conn->remote.transport_params->max_idle_timeout == 0 || +      (conn->local.transport_params.max_idle_timeout && +       conn->local.transport_params.max_idle_timeout < +         conn->remote.transport_params->max_idle_timeout)) { +    idle_timeout = conn->local.transport_params.max_idle_timeout; +  } else { +    idle_timeout = conn->remote.transport_params->max_idle_timeout; +  } + +  if (idle_timeout == 0) { +    return UINT64_MAX; +  } + +  trpto = 3 * conn_compute_pto(conn, conn_is_tls_handshake_completed(conn) +                                       ? &conn->pktns +                                       : conn->hs_pktns); + +  idle_timeout = ngtcp2_max_uint64(idle_timeout, trpto); + +  if (conn->idle_ts >= UINT64_MAX - idle_timeout) { +    return UINT64_MAX; +  } + +  return conn->idle_ts + idle_timeout; +} + +ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { +  return conn_compute_pto(conn, conn_is_tls_handshake_completed(conn) +                                  ? &conn->pktns +                                  : conn->hs_pktns); +} + +void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, +                                        const ngtcp2_crypto_ctx *ctx) { +  assert(conn->in_pktns); +  conn->in_pktns->crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) { +  assert(conn->in_pktns); +  return &conn->in_pktns->crypto.ctx; +} + +void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, +                                const ngtcp2_crypto_aead *aead, +                                const ngtcp2_crypto_aead_ctx *aead_ctx) { +  assert(!conn->crypto.retry_aead_ctx.native_handle); + +  conn->crypto.retry_aead = *aead; +  conn->crypto.retry_aead_ctx = *aead_ctx; +} + +void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, +                                const ngtcp2_crypto_ctx *ctx) { +  assert(conn->hs_pktns); +  conn->hs_pktns->crypto.ctx = *ctx; +  conn->pktns.crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) { +  return &conn->pktns.crypto.ctx; +} + +void ngtcp2_conn_set_0rtt_crypto_ctx(ngtcp2_conn *conn, +                                     const ngtcp2_crypto_ctx *ctx) { +  conn->early.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_0rtt_crypto_ctx(ngtcp2_conn *conn) { +  return &conn->early.ctx; +} + +void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn) { +  return conn->crypto.tls_native_handle; +} + +void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, +                                       void *tls_native_handle) { +  conn->crypto.tls_native_handle = tls_native_handle; +} + +const ngtcp2_ccerr *ngtcp2_conn_get_ccerr(ngtcp2_conn *conn) { +  return &conn->rx.ccerr; +} + +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; +} + +void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert) { +  conn->crypto.tls_alert = alert; +} + +uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn) { +  return conn->crypto.tls_alert; +} + +int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { +  return conn_local_stream(conn, stream_id); +} + +int ngtcp2_conn_is_server(ngtcp2_conn *conn) { return conn->server; } + +int ngtcp2_conn_after_retry(ngtcp2_conn *conn) { +  return (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) != 0; +} + +int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, +                                     void *stream_user_data) { +  ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + +  if (strm == NULL) { +    return NGTCP2_ERR_STREAM_NOT_FOUND; +  } + +  strm->stream_user_data = stream_user_data; + +  return 0; +} + +void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { +  ngtcp2_duration pacing_interval; +  ngtcp2_duration wait; + +  conn_update_timestamp(conn, ts); + +  if (conn->tx.pacing.pktlen == 0) { +    return; +  } + +  if (conn->cstat.pacing_interval) { +    pacing_interval = conn->cstat.pacing_interval; +  } else { +    /* 1.25 is the under-utilization avoidance factor described in +       https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */ +    pacing_interval = (conn->cstat.first_rtt_sample_ts == UINT64_MAX +                         ? NGTCP2_MILLISECONDS +                         : conn->cstat.smoothed_rtt) * +                      100 / 125 / conn->cstat.cwnd; +  } + +  wait = (ngtcp2_duration)(conn->tx.pacing.pktlen * pacing_interval); + +  conn->tx.pacing.next_ts = ts + wait; +  conn->tx.pacing.pktlen = 0; +} + +size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) { +  return conn->cstat.send_quantum; +} + +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { +  if (conn->dcid.retire_unacked.len >= +      ngtcp2_arraylen(conn->dcid.retire_unacked.seqs)) { +    return NGTCP2_ERR_CONNECTION_ID_LIMIT; +  } + +  conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq; + +  return 0; +} + +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { +  size_t i; + +  for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { +    if (conn->dcid.retire_unacked.seqs[i] != seq) { +      continue; +    } + +    if (i != conn->dcid.retire_unacked.len - 1) { +      conn->dcid.retire_unacked.seqs[i] = +        conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1]; +    } + +    --conn->dcid.retire_unacked.len; + +    return; +  } +} + +int ngtcp2_conn_check_retired_dcid_tracked(ngtcp2_conn *conn, uint64_t seq) { +  size_t i; + +  for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { +    if (conn->dcid.retire_unacked.seqs[i] == seq) { +      return 1; +    } +  } + +  return 0; +} + +size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { +  ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + +  if (strm == NULL) { +    return 0; +  } + +  return strm->tx.loss_count; +} + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, +                                      const ngtcp2_path *path, +                                      const uint8_t *data) { +  ngtcp2_path_storage_init2(&pcent->ps, path); +  memcpy(pcent->data, data, sizeof(pcent->data)); +} + +/* 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, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, +  size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, +  const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, +  ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, +  const ngtcp2_crypto_cipher_ctx *hp_ctx) { +  ngtcp2_pkt_hd hd; +  ngtcp2_crypto_km ckm; +  ngtcp2_crypto_cc cc; +  ngtcp2_ppe ppe; +  ngtcp2_frame fr = {0}; +  int rv; + +  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, +                     scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version, +                     /* len = */ 0); + +  ngtcp2_vec_init(&ckm.secret, NULL, 0); +  ngtcp2_vec_init(&ckm.iv, iv, 12); +  ckm.aead_ctx = *aead_ctx; +  ckm.pkt_num = 0; +  ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + +  cc.aead = *aead; +  cc.hp = *hp; +  cc.ckm = &ckm; +  cc.hp_ctx = *hp_ctx; +  cc.encrypt = encrypt; +  cc.hp_mask = hp_mask; + +  ngtcp2_ppe_init(&ppe, dest, destlen, 0, &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; +  fr.connection_close.reasonlen = reasonlen; +  fr.connection_close.reason = (uint8_t *)reason; + +  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); } + +uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, +                               size_t preferred_versionslen, +                               const uint32_t *offered_versions, +                               size_t offered_versionslen) { +  size_t i, j; + +  if (!preferred_versionslen || !offered_versionslen) { +    return 0; +  } + +  for (i = 0; i < preferred_versionslen; ++i) { +    assert(ngtcp2_is_supported_version(preferred_versions[i])); + +    for (j = 0; j < offered_versionslen; ++j) { +      if (preferred_versions[i] == offered_versions[j]) { +        return preferred_versions[i]; +      } +    } +  } + +  return 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conn.h b/contrib/libs/ngtcp2/lib/ngtcp2_conn.h new file mode 100644 index 00000000000..562628d089c --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_conn.h @@ -0,0 +1,1174 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONN_H +#define NGTCP2_CONN_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_crypto.h" +#include "ngtcp2_acktr.h" +#include "ngtcp2_rtb.h" +#include "ngtcp2_strm.h" +#include "ngtcp2_idtr.h" +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_log.h" +#include "ngtcp2_pq.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_bbr.h" +#include "ngtcp2_pv.h" +#include "ngtcp2_pmtud.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_ppe.h" +#include "ngtcp2_qlog.h" +#include "ngtcp2_rst.h" +#include "ngtcp2_conn_stat.h" + +typedef enum { +  /* Client specific handshake states */ +  NGTCP2_CS_CLIENT_INITIAL, +  NGTCP2_CS_CLIENT_WAIT_HANDSHAKE, +  /* Server specific handshake states */ +  NGTCP2_CS_SERVER_INITIAL, +  NGTCP2_CS_SERVER_WAIT_HANDSHAKE, +  /* Shared by both client and server */ +  NGTCP2_CS_POST_HANDSHAKE, +  NGTCP2_CS_CLOSING, +  NGTCP2_CS_DRAINING, +} ngtcp2_conn_state; + +/* 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_RETRIES is the number of Retry packet which client can +   accept. */ +#define NGTCP2_MAX_RETRIES 3 + +/* NGTCP2_MAX_BOUND_DCID_POOL_SIZE is the maximum number of +   destination connection ID which have been bound to a particular +   path, but not yet used as primary path and path validation is not +   performed from the local endpoint. */ +#define NGTCP2_MAX_BOUND_DCID_POOL_SIZE 4 +/* 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_ECN_MAX_NUM_VALIDATION_PKTS is the maximum number of ECN marked +   packets sent in NGTCP2_ECN_STATE_TESTING period. */ +#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10 + +/* NGTCP2_CCERR_MAX_REASONLEN is the maximum length of reason phrase +   to remember.  If the received reason phrase is longer than this +   value, it is truncated. */ +#define NGTCP2_CCERR_MAX_REASONLEN 1024 + +/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other +   than Initial packet should be padded so that UDP datagram payload +   is at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes.  Initial packet +   might be padded based on QUIC requirement regardless of this +   flag. */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u +/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come +   and it should be encoded into the current packet. */ +#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL is just like +   NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, but it requests to add +   padding to the full UDP datagram payload size. */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04u + +/* + * ngtcp2_max_frame is defined so that it covers the largest ACK + * frame. + */ +typedef union ngtcp2_max_frame { +  ngtcp2_frame fr; +  struct { +    ngtcp2_ack ack; +    /* ack includes 1 ngtcp2_ack_range. */ +    ngtcp2_ack_range ranges[NGTCP2_MAX_ACK_RANGES - 1]; +  } ackfr; +} ngtcp2_max_frame; + +typedef struct ngtcp2_path_challenge_entry { +  ngtcp2_path_storage ps; +  uint8_t data[8]; +} ngtcp2_path_challenge_entry; + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, +                                      const ngtcp2_path *path, +                                      const uint8_t *data); + +/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_CONN_FLAG_NONE 0x00u +/* NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED is set when TLS stack +   declares that TLS handshake has completed.  The condition of this +   declaration varies between TLS implementations and this flag does +   not indicate the completion of QUIC handshake.  Some +   implementations declare TLS handshake completion as server when +   they write off Server Finished and before deriving application rx +   secret. */ +#define NGTCP2_CONN_FLAG_TLS_HANDSHAKE_COMPLETED 0x01u +/* NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED is set when the first +   Initial packet has successfully been processed. */ +#define NGTCP2_CONN_FLAG_INITIAL_PKT_PROCESSED 0x02u +/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport +   parameters are received. */ +#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u +/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a +   local transport parameters are applied. */ +#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u +/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry +   packet. */ +#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u +/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is +   rejected by a peer. */ +#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u +/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired +   keep-alive timer has been cancelled. */ +#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u +/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint +   confirmed completion of handshake. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when the library +   transitions its state to "post handshake". */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x0100u +/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early +   handshake retransmission has done when server receives overlapping +   Initial crypto data. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u +/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint +   sends a QUIC packet without Fixed Bit set if a remote endpoint +   supports Greasing QUIC Bit extension. */ +#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u +/* 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. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u +/* 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. */ +#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u +/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer +   should be restarted on next write. */ +#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u +/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer +   verified client address.  This flag is only used by client. */ +#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u +/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is +   installed.  conn->early.ckm cannot be used for this purpose because +   it might be discarded when a certain condition is met. */ +#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u +/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local +   endpoint has initiated key update. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u + +typedef struct ngtcp2_pktns { +  struct { +    /* last_pkt_num is the packet number which the local endpoint sent +       last time.*/ +    int64_t last_pkt_num; +    ngtcp2_frame_chain *frq; +    /* non_ack_pkt_start_ts is the timestamp since the local endpoint +       starts sending continuous non ACK-eliciting packets. */ +    ngtcp2_tstamp non_ack_pkt_start_ts; + +    struct { +      /* ect0 is the number of QUIC packets, not UDP datagram, which +         are sent in UDP datagram with ECT0 marking. */ +      size_t ect0; +      /* start_pkt_num is the lowest packet number that are sent +         during ECN validation period. */ +      int64_t start_pkt_num; +      /* validation_pkt_sent is the number of QUIC packets sent during +         validation period. */ +      size_t validation_pkt_sent; +      /* validation_pkt_lost is the number of QUIC packets lost during +         validation period. */ +      size_t validation_pkt_lost; +    } ecn; +  } tx; + +  struct { +    /* pngap tracks received packet number in order to suppress +       duplicated packet number. */ +    ngtcp2_gaptr pngap; +    /* max_pkt_num is the largest packet number received so far. */ +    int64_t max_pkt_num; +    /* max_pkt_ts is the timestamp when max_pkt_num packet is +       received. */ +    ngtcp2_tstamp max_pkt_ts; +    /* max_ack_eliciting_pkt_num is the largest ack-eliciting packet +       number received so far. */ +    int64_t max_ack_eliciting_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; + +    struct { +      /* ect0, ect1, and ce are the number of QUIC packets received +         with those markings. */ +      size_t ect0; +      size_t ect1; +      size_t ce; +      struct { +        /* ect0, ect1, ce are the ECN counts received in the latest +           ACK frame. */ +        uint64_t ect0; +        uint64_t ect1; +        uint64_t ce; +      } ack; +    } ecn; +  } rx; + +  struct { +    struct { +      /* offset is the offset of crypto stream in this packet number +         space. */ +      uint64_t offset; +      /* ckm is a cryptographic key, and iv to encrypt outgoing +         packets. */ +      ngtcp2_crypto_km *ckm; +      /* hp_ctx is cipher context for packet header protection. */ +      ngtcp2_crypto_cipher_ctx hp_ctx; +      /* data is the submitted crypto data. */ +      ngtcp2_buf_chain *data; +    } tx; + +    struct { +      /* ckm is a cryptographic key, and iv to decrypt incoming +         packets. */ +      ngtcp2_crypto_km *ckm; +      /* hp_ctx is cipher context for packet header protection. */ +      ngtcp2_crypto_cipher_ctx hp_ctx; +    } rx; + +    ngtcp2_strm strm; +    ngtcp2_crypto_ctx ctx; +  } crypto; + +  ngtcp2_acktr acktr; +  ngtcp2_rtb rtb; +  ngtcp2_pktns_id id; +} ngtcp2_pktns; + +typedef enum ngtcp2_ecn_state { +  NGTCP2_ECN_STATE_TESTING, +  NGTCP2_ECN_STATE_UNKNOWN, +  NGTCP2_ECN_STATE_FAILED, +  NGTCP2_ECN_STATE_CAPABLE, +} ngtcp2_ecn_state; + +/* ngtcp2_early_transport_params is the values remembered by client +   from the previous session. */ +typedef struct ngtcp2_early_transport_params { +  uint64_t initial_max_streams_bidi; +  uint64_t initial_max_streams_uni; +  uint64_t initial_max_stream_data_bidi_local; +  uint64_t initial_max_stream_data_bidi_remote; +  uint64_t initial_max_stream_data_uni; +  uint64_t initial_max_data; +  uint64_t active_connection_id_limit; +  uint64_t max_datagram_frame_size; +} ngtcp2_early_transport_params; + +ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE, +                          sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE, +                          sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE, +                          sizeof(ngtcp2_dcid)); +ngtcp2_static_ringbuf_def(path_challenge, 4, +                          sizeof(ngtcp2_path_challenge_entry)); + +ngtcp2_objalloc_decl(strm, ngtcp2_strm, oplent); + +struct ngtcp2_conn { +  ngtcp2_objalloc frc_objalloc; +  ngtcp2_objalloc rtb_entry_objalloc; +  ngtcp2_objalloc strm_objalloc; +  ngtcp2_conn_state state; +  ngtcp2_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_destination_connection_id transport parameter. */ +  ngtcp2_cid rcid; +  /* oscid is the source connection ID initially used by the local +     endpoint. */ +  ngtcp2_cid oscid; +  /* retry_scid is the source connection ID from Retry packet.  Client +     records it in order to verify retry_source_connection_id +     transport parameter.  Server does not use this field. */ +  ngtcp2_cid retry_scid; +  ngtcp2_pktns *in_pktns; +  ngtcp2_pktns *hs_pktns; +  ngtcp2_pktns pktns; + +  struct { +    /* current is the current destination connection ID. */ +    ngtcp2_dcid current; +    /* bound is a set of destination connection IDs which are bound to +       particular paths.  These paths are not validated yet. */ +    ngtcp2_static_ringbuf_dcid_bound bound; +    /* unused is a set of unused CID received from peer. */ +    ngtcp2_static_ringbuf_dcid_unused 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_static_ringbuf_dcid_retired retired; +    /* seqgap tracks received sequence numbers in order to ignore +       retransmitted duplicated NEW_CONNECTION_ID frame. */ +    ngtcp2_gaptr seqgap; +    /* retire_prior_to is the largest retire_prior_to received so +       far. */ +    uint64_t retire_prior_to; +    struct { +      /* seqs contains sequence number of Connection ID whose +         retirement is not acknowledged by the remote endpoint yet. */ +      uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2]; +      /* len is the number of sequence numbers that seq contains. */ +      size_t len; +    } retire_unacked; +    /* zerolen_seq is a pseudo sequence number of zero-length +       Destination Connection ID in order to distinguish between +       them. */ +    uint64_t zerolen_seq; +  } 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; +    /* num_in_flight is the number of NEW_CONNECTION_ID frames that +       are in-flight and not acknowledged yet. */ +    size_t num_in_flight; +  } scid; + +  struct { +    /* strmq contains ngtcp2_strm which has frames to send. */ +    ngtcp2_pq strmq; +    /* ack is ACK frame.  The underlying buffer is reused. */ +    ngtcp2_frame *ack; +    /* max_ack_ranges is the number of additional ngtcp2_ack_range +       which ack can contain. */ +    size_t max_ack_ranges; +    /* 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; +    /* last_blocked_offset is the largest offset where the +       transmission of stream data is blocked. */ +    uint64_t last_blocked_offset; +    /* last_max_data_ts is the timestamp when last MAX_DATA frame is +       sent. */ +    ngtcp2_tstamp last_max_data_ts; + +    struct { +      /* state is the state of ECN validation */ +      ngtcp2_ecn_state state; +      /* validation_start_ts is the timestamp when ECN validation is +         started.  It is UINT64_MAX if it has not started yet. */ +      ngtcp2_tstamp validation_start_ts; +      /* dgram_sent is the number of UDP datagram sent during ECN +         validation period. */ +      size_t dgram_sent; +    } ecn; + +    struct { +      /* pktlen is the number of bytes written before calling +         ngtcp2_conn_update_pkt_tx_time which resets this field to +         0. */ +      size_t pktlen; +      /* next_ts is the time to send next packet.  It is UINT64_MAX if +         packet pacing is disabled or expired.*/ +      ngtcp2_tstamp next_ts; +    } pacing; +  } 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; +    /* window is the connection-level flow control window size. */ +    uint64_t window; +    /* path_challenge stores received PATH_CHALLENGE data. */ +    ngtcp2_static_ringbuf_path_challenge path_challenge; +    /* ccerr is the received connection close error. */ +    ngtcp2_ccerr ccerr; +  } rx; + +  struct { +    ngtcp2_crypto_km *ckm; +    ngtcp2_crypto_cipher_ctx hp_ctx; +    ngtcp2_crypto_ctx ctx; +    /* discard_started_ts is the timestamp when the timer to discard +       early key has started.  Used by server only. */ +    ngtcp2_tstamp discard_started_ts; +    /* transport_params is the values remembered by client from the +       previous session.  These are set by +       ngtcp2_conn_set_early_remote_transport_params().  Server does +       not use this field.  Server must not set values for these +       parameters that are smaller than the remembered values. */ +    ngtcp2_early_transport_params transport_params; +  } early; + +  struct { +    ngtcp2_settings settings; +    /* transport_params is the local transport parameters.  It is used +       for Short packet only. */ +    ngtcp2_transport_params transport_params; +    struct { +      /* max_streams is the maximum number of bidirectional streams which +         the local endpoint can open. */ +      uint64_t max_streams; +      /* next_stream_id is the bidirectional stream ID which the local +         endpoint opens next. */ +      int64_t next_stream_id; +    } bidi; + +    struct { +      /* max_streams is the maximum number of unidirectional streams +         which the local endpoint can open. */ +      uint64_t max_streams; +      /* next_stream_id is the unidirectional stream ID which the +         local endpoint opens next. */ +      int64_t next_stream_id; +    } uni; +  } local; + +  struct { +    /* transport_params is the received transport parameters during +       handshake.  It is used for Short packet only. */ +    ngtcp2_transport_params *transport_params; +    /* pending_transport_params is received transport parameters +       during handshake.  It is copied to transport_params when 1RTT +       key is available. */ +    ngtcp2_transport_params *pending_transport_params; +    struct { +      ngtcp2_idtr idtr; +      /* unsent_max_streams is the maximum number of streams of peer +         initiated bidirectional stream which the local endpoint can +         accept.  This limit is not yet notified to the remote +         endpoint. */ +      uint64_t unsent_max_streams; +      /* max_streams is the maximum number of streams of peer +         initiated bidirectional stream which the local endpoint can +         accept. */ +      uint64_t max_streams; +    } bidi; + +    struct { +      ngtcp2_idtr idtr; +      /* unsent_max_streams is the maximum number of streams of peer +         initiated unidirectional stream which the local endpoint can +         accept.  This limit is not yet notified to the remote +         endpoint. */ +      uint64_t unsent_max_streams; +      /* max_streams is the maximum number of streams of peer +         initiated unidirectional stream which the local endpoint can +         accept. */ +      uint64_t max_streams; +    } uni; +  } remote; + +  struct { +    struct { +      /* new_tx_ckm is a new sender 1RTT key which has not been +         used. */ +      ngtcp2_crypto_km *new_tx_ckm; +      /* new_rx_ckm is a new receiver 1RTT key which has not +         successfully decrypted incoming packet yet. */ +      ngtcp2_crypto_km *new_rx_ckm; +      /* old_rx_ckm is an old receiver 1RTT key. */ +      ngtcp2_crypto_km *old_rx_ckm; +      /* confirmed_ts is the time instant when the key update is +         confirmed by the local endpoint last time.  UINT64_MAX means +         undefined value. */ +      ngtcp2_tstamp confirmed_ts; +    } key_update; + +    /* tls_native_handle is a native handle to TLS session object. */ +    void *tls_native_handle; +    /* decrypt_hp_buf is a buffer which is used to write unprotected +       packet header. */ +    ngtcp2_vec decrypt_hp_buf; +    /* decrypt_buf is a buffer which is used to write decrypted data. */ +    ngtcp2_vec decrypt_buf; +    /* retry_aead is AEAD to verify Retry packet integrity.  It is +       used by client only. */ +    ngtcp2_crypto_aead retry_aead; +    /* retry_aead_ctx is AEAD cipher context to verify Retry packet +       integrity.  It is used by client only. */ +    ngtcp2_crypto_aead_ctx retry_aead_ctx; +    /* tls_error is TLS related error. */ +    int tls_error; +    /* tls_alert is TLS alert generated by the local endpoint. */ +    uint8_t tls_alert; +    /* decryption_failure_count is the number of received packets that +       fail authentication. */ +    uint64_t decryption_failure_count; +  } 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; +    /* flags is bitwise OR of zero or more of +       NGTCP2_RTB_ENTRY_FLAG_*. */ +    uint16_t rtb_entry_flags; +    ngtcp2_ssize hs_spktlen; +    int require_padding; +  } pkt; + +  struct { +    /* last_ts is a timestamp when a last packet is sent or received +       on a current path. */ +    ngtcp2_tstamp last_ts; +    /* timeout is keep-alive timeout.  When it expires, a packet +       should be sent to a current path to keep connection alive.  It +       might be used to keep NAT binding intact.  If 0 is set, +       keep-alive timer is disabled. */ +    ngtcp2_duration timeout; +  } keep_alive; + +  struct { +    /* Initial keys for negotiated version.  If original version == +       negotiated version, these fields are not used. */ +    struct { +      ngtcp2_crypto_km *ckm; +      ngtcp2_crypto_cipher_ctx hp_ctx; +    } rx; +    struct { +      ngtcp2_crypto_km *ckm; +      ngtcp2_crypto_cipher_ctx hp_ctx; +    } tx; +    /* version is QUIC version that the above Initial keys are created +       for. */ +    uint32_t version; +    /* preferred_versions is the array of versions that are preferred +       by the local endpoint.  Server negotiates one of those versions +       in this array if a client initially selects a less preferred +       version.  Client uses this field and original_version field to +       prevent version downgrade attack if it reacted upon Version +       Negotiation packet. */ +    uint32_t *preferred_versions; +    /* preferred_versionslen is the number of versions stored in the +       array pointed by preferred_versions.  This field is only used +       by server. */ +    size_t preferred_versionslen; +    /* available_versions is the versions that the local endpoint +       sends in version_information transport parameter.  This is the +       wire image of available_versions field of version_information +       transport parameter. */ +    uint8_t *available_versions; +    /* available_versionslen is the length of data pointed by +       available_versions field. */ +    size_t available_versionslen; +  } vneg; + +  ngtcp2_map strms; +  ngtcp2_conn_stat cstat; +  ngtcp2_pv *pv; +  ngtcp2_pmtud *pmtud; +  ngtcp2_log log; +  ngtcp2_qlog qlog; +  ngtcp2_rst rst; +  ngtcp2_cc_algo cc_algo; +  union { +    ngtcp2_cc cc; +    ngtcp2_cc_reno reno; +    ngtcp2_cc_cubic cubic; +    ngtcp2_cc_bbr bbr; +  }; +  const ngtcp2_mem *mem; +  /* idle_ts is the time instant when idle timer started. */ +  ngtcp2_tstamp idle_ts; +  void *user_data; +  uint32_t client_chosen_version; +  uint32_t negotiated_version; +  /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */ +  uint32_t flags; +  int server; +}; + +typedef enum ngtcp2_vmsg_type { +  NGTCP2_VMSG_TYPE_STREAM, +  NGTCP2_VMSG_TYPE_DATAGRAM, +} ngtcp2_vmsg_type; + +typedef struct ngtcp2_vmsg_stream { +  /* strm is a stream that data is sent to. */ +  ngtcp2_strm *strm; +  /* flags is bitwise OR of zero or more of +     NGTCP2_WRITE_STREAM_FLAG_*. */ +  uint32_t flags; +  /* data is the pointer to ngtcp2_vec array which contains the stream +     data to send. */ +  const ngtcp2_vec *data; +  /* datacnt is the number of ngtcp2_vec pointed by data. */ +  size_t datacnt; +  /* pdatalen is the pointer to the variable which the number of bytes +     written is assigned to if pdatalen is not NULL. */ +  ngtcp2_ssize *pdatalen; +} ngtcp2_vmsg_stream; + +typedef struct ngtcp2_vmsg_datagram { +  /* data is the pointer to ngtcp2_vec array which contains the data +     to send. */ +  const ngtcp2_vec *data; +  /* datacnt is the number of ngtcp2_vec pointed by data. */ +  size_t datacnt; +  /* dgram_id is an opaque identifier chosen by an application. */ +  uint64_t dgram_id; +  /* flags is bitwise OR of zero or more of +     NGTCP2_WRITE_DATAGRAM_FLAG_*. */ +  uint32_t flags; +  /* paccepted is the pointer to the variable which, if it is not +     NULL, is assigned nonzero if data is written to a packet. */ +  int *paccepted; +} ngtcp2_vmsg_datagram; + +typedef struct ngtcp2_vmsg { +  ngtcp2_vmsg_type type; +  union { +    ngtcp2_vmsg_stream stream; +    ngtcp2_vmsg_datagram datagram; +  }; +} ngtcp2_vmsg; + +/* + * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its + * reception timestamp |ts| in order to send its ACK. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + * NGTCP2_ERR_PROTO + *     Same packet number has already been added. + */ +int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, +                          int64_t pkt_num, int active_ack, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_find_stream returns a stream whose stream ID is + * |stream_id|.  If no such stream is found, it returns NULL. + */ +ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id); + +/* + * conn_init_stream initializes |strm|.  Its stream ID is |stream_id|. + * This function adds |strm| to conn->strms.  |strm| must be allocated + * by the caller. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-callback function failed. + */ +int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, +                            int64_t stream_id, void *stream_user_data); + +/* + * ngtcp2_conn_close_stream closes stream |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + *     Stream is not found. + * NGTCP2_ERR_CALLBACK_FAILURE + *     User-defined callback function failed. + */ +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm); + +/* + * 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); + +/* + * 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|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + *     RTT sample is ignored. + */ +int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, +                           ngtcp2_duration ack_delay, ngtcp2_tstamp ts); + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +void ngtcp2_conn_cancel_loss_detection_timer(ngtcp2_conn *conn); + +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_detect_lost_pkt detects lost packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                                ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the + * top of queue.  tx_strmq must not be empty. + */ +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue. + * tx_strmq must not be empty. + */ +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq. + * + *  This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); + +/* + * ngtcp2_conn_internal_expiry returns the minimum expiry time among + * all timers in |conn|. + */ +ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); + +ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, +                                    int pkt_info_version, ngtcp2_pkt_info *pi, +                                    uint8_t *dest, size_t destlen, +                                    ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains + * |fr| frame only in the buffer pointed by |dest| whose length if + * |destlen|.  |type| is a long packet type to send.  If |type| is 0, + * Short packet is used.  |dcid| is used as a destination connection + * ID.  |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*.  Only + * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized. + * + * 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, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, +  uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr, +  uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_commit_local_transport_params commits the local + * transport parameters, which is currently set to + * conn->local.settings.transport_params.  This function will do some + * amends on transport parameters for adjusting default values. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + *     CID in preferred address equals to the original SCID. + */ +int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of + * lost packet. + */ +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_remove_lost_pkt removes the expired lost packet. + */ +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of + * delayed protected ACK.  One 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_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); + +/** + * @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. + */ +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, +                                                ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point + * of loss detection timer.  One 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_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); + +/** + * @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_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + +ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns); + +/* + * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq| + * of unacknowledged retiring Destination Connection ID. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONNECTION_ID_LIMIT + *     The number of unacknowledged retirement exceeds the limit. + */ +int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number + * |seq| of unacknowledged retiring Destination Connection ID.  It is + * fine if such sequence number is not found. + */ +void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_check_retired_dcid_tracked returns nonzero if |seq| has + * already been tracked. + */ +int ngtcp2_conn_check_retired_dcid_tracked(ngtcp2_conn *conn, uint64_t seq); + +/* + * ngtcp2_conn_server_negotiate_version negotiates QUIC version.  It + * is compatible version negotiation.  It returns the negotiated QUIC + * version.  This function must not be called by client. + */ +uint32_t +ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn, +                                     const ngtcp2_version_info *version_info); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent.  Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long.  The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds.  The metadata includes ECN markings. + * + * 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. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + *     Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt( +  ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, +  size_t destlen, uint64_t error_code, const uint8_t *reason, size_t reasonlen, +  ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close_pkt` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent.  Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long.  The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds.  The metadata includes ECN markings. + * + * If handshake has not been confirmed yet, CONNECTION_CLOSE (type + * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written + * instead. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close.  We may change this behaviour in the future to allow + * graceful shutdown. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + *     Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + *     The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + *     Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + */ +ngtcp2_ssize ngtcp2_conn_write_application_close_pkt( +  ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, +  size_t destlen, uint64_t app_error_code, const uint8_t *reason, +  size_t reasonlen, ngtcp2_tstamp ts); + +int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn); + +void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_remote_transport_params` sets transport parameter + * |params| from a remote endpoint to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` + *     Failed to validate a remote transport parameters. + * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` + *     Version negotiation failure. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + *     User callback failed + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +int ngtcp2_conn_set_remote_transport_params( +  ngtcp2_conn *conn, const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_set_0rtt_remote_transport_params` sets |params| as + * transport parameters previously received from a server.  The + * parameters are used to send early data.  QUIC requires that client + * application should remember transport parameters along with a + * session ticket. + * + * At least following fields should 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 + * - active_connection_id_limit + * - max_datagram_frame_size (if DATAGRAM extension was negotiated) + * + * The following fields are ignored: + * + * - ack_delay_exponent + * - max_ack_delay + * - initial_scid + * - original_dcid + * - preferred_address and preferred_address_present + * - retry_scid and retry_scid_present + * - stateless_reset_token and stateless_reset_token_present + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + *     Out of memory. + */ +int ngtcp2_conn_set_0rtt_remote_transport_params( +  ngtcp2_conn *conn, const ngtcp2_transport_params *params); + +/* + * ngtcp2_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. + * + * 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. + */ +int ngtcp2_conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, +                                 ngtcp2_pktns *pktns, uint8_t type, +                                 ngtcp2_tstamp ts, ngtcp2_duration ack_delay, +                                 uint64_t ack_delay_exponent); + +/* + * ngtcp2_conn_discard_initial_state discards state for Initial packet + * number space. + */ +void ngtcp2_conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_discard_handshake_state discards state for Handshake + * packet number space. + */ +void ngtcp2_conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +#endif /* !defined(NGTCP2_CONN_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conn_stat.h b/contrib/libs/ngtcp2/lib/ngtcp2_conn_stat.h new file mode 100644 index 00000000000..ad2b7329f48 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_conn_stat.h @@ -0,0 +1,133 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_STAT_H +#define NGTCP2_CONN_STAT_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/** + * @struct + * + * :type:`ngtcp2_conn_stat` holds various connection statistics, and + * computed data for recovery and congestion controller. + */ +typedef struct ngtcp2_conn_stat { +  /** +   * :member:`latest_rtt` is the latest RTT sample which is not +   * adjusted by acknowledgement delay. +   */ +  ngtcp2_duration latest_rtt; +  /** +   * :member:`min_rtt` is the minimum RTT seen so far.  It is not +   * adjusted by acknowledgement delay. +   */ +  ngtcp2_duration min_rtt; +  /** +   * :member:`smoothed_rtt` is the smoothed RTT. +   */ +  ngtcp2_duration smoothed_rtt; +  /** +   * :member:`rttvar` is a mean deviation of observed RTT. +   */ +  ngtcp2_duration rttvar; +  /** +   * :member:`initial_rtt` is the initial RTT which is used when no +   * RTT sample is available. +   */ +  ngtcp2_duration initial_rtt; +  /** +   * :member:`first_rtt_sample_ts` is the timestamp when the first RTT +   * sample is obtained. +   */ +  ngtcp2_tstamp first_rtt_sample_ts; +  /** +   * :member:`pto_count` is the count of successive PTO timer +   * expiration. +   */ +  size_t pto_count; +  /** +   * :member:`loss_detection_timer` is the deadline of the current +   * loss detection timer. +   */ +  ngtcp2_tstamp loss_detection_timer; +  /** +   * :member:`last_tx_pkt_ts` corresponds to +   * time_of_last_ack_eliciting_packet in :rfc:`9002`. +   */ +  ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; +  /** +   * :member:`loss_time` corresponds to loss_time in :rfc:`9002`. +   */ +  ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; +  /** +   * :member:`cwnd` is the size of congestion window. +   */ +  uint64_t cwnd; +  /** +   * :member:`ssthresh` is slow start threshold. +   */ +  uint64_t ssthresh; +  /** +   * :member:`congestion_recovery_start_ts` is the timestamp when +   * congestion recovery started. +   */ +  ngtcp2_tstamp congestion_recovery_start_ts; +  /** +   * :member:`bytes_in_flight` is the number in bytes of all sent +   * packets which have not been acknowledged. +   */ +  uint64_t bytes_in_flight; +  /** +   * :member:`max_tx_udp_payload_size` is the maximum size of UDP +   * datagram payload that this endpoint transmits to the current +   * path.  It is used by congestion controller to compute congestion +   * window. +   */ +  size_t max_tx_udp_payload_size; +  /** +   * :member:`delivery_rate_sec` is the current sending rate measured +   * in byte per second. +   */ +  uint64_t delivery_rate_sec; +  /** +   * :member:`pacing_interval` is the inverse of pacing rate, which is +   * the current packet sending rate computed by a congestion +   * controller.  0 if a congestion controller does not set pacing +   * interval.  Even if this value is set to 0, the library paces +   * packets. +   */ +  ngtcp2_duration pacing_interval; +  /** +   * :member:`send_quantum` is the maximum size of a data aggregate +   * scheduled and transmitted together. +   */ +  size_t send_quantum; +} ngtcp2_conn_stat; + +#endif /* !defined(NGTCP2_CONN_STAT_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conv.c b/contrib/libs/ngtcp2/lib/ngtcp2_conv.c new file mode 100644 index 00000000000..6528011cc0e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_conv.c @@ -0,0 +1,222 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conv.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_net.h" +#include "ngtcp2_unreachable.h" + +const uint8_t *ngtcp2_get_uint64be(uint64_t *dest, const uint8_t *p) { +  memcpy(dest, p, sizeof(*dest)); +  *dest = ngtcp2_ntohl64(*dest); +  return p + sizeof(*dest); +} + +const uint8_t *ngtcp2_get_uint32be(uint32_t *dest, const uint8_t *p) { +  memcpy(dest, p, sizeof(*dest)); +  *dest = ngtcp2_ntohl(*dest); +  return p + sizeof(*dest); +} + +const uint8_t *ngtcp2_get_uint24be(uint32_t *dest, const uint8_t *p) { +  *dest = 0; +  memcpy(((uint8_t *)dest) + 1, p, 3); +  *dest = ngtcp2_ntohl(*dest); +  return p + 3; +} + +const uint8_t *ngtcp2_get_uint16be(uint16_t *dest, const uint8_t *p) { +  memcpy(dest, p, sizeof(*dest)); +  *dest = ngtcp2_ntohs(*dest); +  return p + sizeof(*dest); +} + +const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p) { +  memcpy(dest, p, sizeof(*dest)); +  return p + sizeof(*dest); +} + +static const uint8_t *get_uvarint(uint64_t *dest, const uint8_t *p) { +  union { +    uint8_t n8; +    uint16_t n16; +    uint32_t n32; +    uint64_t n64; +  } n; + +  switch (*p >> 6) { +  case 0: +    *dest = *p++; +    return p; +  case 1: +    memcpy(&n, p, 2); +    n.n8 &= 0x3f; +    *dest = ngtcp2_ntohs(n.n16); + +    return p + 2; +  case 2: +    memcpy(&n, p, 4); +    n.n8 &= 0x3f; +    *dest = ngtcp2_ntohl(n.n32); + +    return p + 4; +  case 3: +    memcpy(&n, p, 8); +    n.n8 &= 0x3f; +    *dest = ngtcp2_ntohl64(n.n64); + +    return p + 8; +  default: +    ngtcp2_unreachable(); +  } +} + +const uint8_t *ngtcp2_get_uvarint(uint64_t *dest, const uint8_t *p) { +  return get_uvarint(dest, p); +} + +const uint8_t *ngtcp2_get_varint(int64_t *dest, const uint8_t *p) { +  return get_uvarint((uint64_t *)dest, p); +} + +int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) { +  uint32_t l; +  uint16_t s; + +  switch (pkt_numlen) { +  case 1: +    return *p; +  case 2: +    ngtcp2_get_uint16be(&s, p); +    return (int64_t)s; +  case 3: +    ngtcp2_get_uint24be(&l, p); +    return (int64_t)l; +  case 4: +    ngtcp2_get_uint32be(&l, p); +    return (int64_t)l; +  default: +    ngtcp2_unreachable(); +  } +} + +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_uint32be(uint8_t *p, uint32_t n) { +  n = ngtcp2_htonl(n); +  return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) { +  n = ngtcp2_htonl(n); +  return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) { +  n = ngtcp2_htons(n); +  return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint16(uint8_t *p, uint16_t n) { +  return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uvarint(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_uvarint30(uint8_t *p, uint32_t n) { +  uint8_t *rv; + +  assert(n < 1073741824); + +  rv = ngtcp2_put_uint32be(p, n); +  *p |= 0x80; + +  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: +    return ngtcp2_put_uint16be(p, (uint16_t)pkt_num); +  case 3: +    return ngtcp2_put_uint24be(p, (uint32_t)pkt_num); +  case 4: +    return ngtcp2_put_uint32be(p, (uint32_t)pkt_num); +  default: +    ngtcp2_unreachable(); +  } +} + +size_t ngtcp2_get_uvarintlen(const uint8_t *p) { +  return (size_t)(1u << (*p >> 6)); +} + +size_t ngtcp2_put_uvarintlen(uint64_t n) { +  if (n < 64) { +    return 1; +  } +  if (n < 16384) { +    return 2; +  } +  if (n < 1073741824) { +    return 4; +  } +  assert(n < 4611686018427387904ULL); +  return 8; +} + +uint64_t ngtcp2_ord_stream_id(int64_t stream_id) { +  return (uint64_t)(stream_id >> 2) + 1; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conv.h b/contrib/libs/ngtcp2/lib/ngtcp2_conv.h new file mode 100644 index 00000000000..ad924683b8d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_conv.h @@ -0,0 +1,162 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONV_H +#define NGTCP2_CONV_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_get_uint64be reads 8 bytes from |p| as 64 bits unsigned + * integer encoded as network byte order, and stores it in the buffer + * pointed by |dest| in host byte order.  It returns |p| + 8. + */ +const uint8_t *ngtcp2_get_uint64be(uint64_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_uint32be reads 4 bytes from |p| as 32 bits unsigned + * integer encoded as network byte order, and stores it in the buffer + * pointed by |dest| in host byte order.  It returns |p| + 4. + */ +const uint8_t *ngtcp2_get_uint32be(uint32_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_uint24be reads 3 bytes from |p| as 24 bits unsigned + * integer encoded as network byte order, and stores it in the buffer + * pointed by |dest| in host byte order.  It returns |p| + 3. + */ +const uint8_t *ngtcp2_get_uint24be(uint32_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_uint16be reads 2 bytes from |p| as 16 bits unsigned + * integer encoded as network byte order, and stores it in the buffer + * pointed by |dest| in host byte order.  It returns |p| + 2. + */ +const uint8_t *ngtcp2_get_uint16be(uint16_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned + * integer encoded as network byte order, and stores it in the buffer + * pointed by |dest| as is.  It returns |p| + 2. + */ +const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_uvarint reads variable-length unsigned integer from |p|, + * and stores it in the buffer pointed by |dest| in host byte order. + * It returns |p| plus the number of bytes read from |p|. + */ +const uint8_t *ngtcp2_get_uvarint(uint64_t *dest, const uint8_t *p); + +/* + * ngtcp2_get_varint reads variable-length unsigned integer from |p|, + * and casts it to the signed integer, and stores it in the buffer + * pointed by |dest| in host byte order.  No information should be + * lost in this cast, because the variable-length integer is 62 + * bits. It returns |p| plus the number of bytes read from |p|. + */ +const uint8_t *ngtcp2_get_varint(int64_t *dest, 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_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_uint16 writes |n| as is in |p|.  It returns the one + * beyond of the last written position. + */ +uint8_t *ngtcp2_put_uint16(uint8_t *p, uint16_t n); + +/* + * ngtcp2_put_uvarint writes |n| in |p| using variable-length integer + * encoding.  It returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_uvarint(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_uvarint30 writes |n| in |p| using variable-length + * integer encoding.  |n| must be strictly less than 1073741824.  The + * function always encodes |n| in 4 bytes.  It returns the one beyond + * of the last written position. + */ +uint8_t *ngtcp2_put_uvarint30(uint8_t *p, uint32_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_uvarintlen returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t ngtcp2_get_uvarintlen(const uint8_t *p); + +/* + * ngtcp2_put_uvarintlen returns the required number of bytes to + * encode |n|. + */ +size_t ngtcp2_put_uvarintlen(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 /* !defined(NGTCP2_CONV_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_crypto.c b/contrib/libs/ngtcp2/lib/ngtcp2_crypto.c new file mode 100644 index 00000000000..1f74e8c5839 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_crypto.c @@ -0,0 +1,112 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_crypto.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_net.h" + +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, +                         size_t secretlen, +                         const ngtcp2_crypto_aead_ctx *aead_ctx, +                         const uint8_t *iv, size_t ivlen, +                         const ngtcp2_mem *mem) { +  int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem); +  if (rv != 0) { +    return rv; +  } + +  if (secretlen) { +    memcpy((*pckm)->secret.base, secret, secretlen); +  } + +  if (aead_ctx) { +    (*pckm)->aead_ctx = *aead_ctx; +  } + +  memcpy((*pckm)->iv.base, iv, ivlen); + +  return 0; +} + +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, +                                size_t ivlen, const ngtcp2_mem *mem) { +  size_t len; +  uint8_t *p; + +  len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen; + +  *pckm = ngtcp2_mem_malloc(mem, len); +  if (*pckm == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km); +  (*pckm)->secret.base = p; +  (*pckm)->secret.len = secretlen; +  p += secretlen; +  (*pckm)->iv.base = p; +  (*pckm)->iv.len = ivlen; +  (*pckm)->aead_ctx.native_handle = NULL; +  (*pckm)->pkt_num = -1; +  (*pckm)->use_count = 0; +  (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + +  return 0; +} + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) { +  if (ckm == NULL) { +    return; +  } + +  if (ckm->secret.len) { +#ifdef WIN32 +    SecureZeroMemory(ckm->secret.base, ckm->secret.len); +#elif defined(HAVE_EXPLICIT_BZERO) +    explicit_bzero(ckm->secret.base, ckm->secret.len); +#elif defined(HAVE_MEMSET_S) +    memset_s(ckm->secret.base, ckm->secret.len, 0, ckm->secret.len); +#endif /* defined(HAVE_MEMSET_S) */ +  } + +  ngtcp2_mem_free(mem, ckm); +} + +void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, +                                int64_t pkt_num) { +  uint64_t n; + +  assert(ivlen >= sizeof(n)); + +  memcpy(dest, iv, ivlen); + +  dest += ivlen - sizeof(n); + +  memcpy(&n, dest, sizeof(n)); +  n ^= ngtcp2_htonl64((uint64_t)pkt_num); +  memcpy(dest, &n, sizeof(n)); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_crypto.h b/contrib/libs/ngtcp2/lib/ngtcp2_crypto.h new file mode 100644 index 00000000000..ca6d494846f --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial +   packets.  Because QUIC uses AEAD_AES_128_GCM, the overhead is 16 +   bytes. */ +#define NGTCP2_INITIAL_AEAD_OVERHEAD 16 + +/* NGTCP2_MAX_AEAD_OVERHEAD is the maximum AEAD overhead. */ +#define NGTCP2_MAX_AEAD_OVERHEAD 16 + +/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u +/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is +   set. */ +#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u + +typedef struct ngtcp2_crypto_km { +  ngtcp2_vec secret; +  ngtcp2_crypto_aead_ctx aead_ctx; +  ngtcp2_vec iv; +  /* pkt_num is a packet number of a packet which uses this keying +     material.  For encryption key, it is the lowest packet number of +     a packet.  For decryption key, it is the lowest packet number of +     a packet which can be decrypted with this keying material. */ +  int64_t pkt_num; +  /* use_count is the number of encryption applied with this key. */ +  uint64_t use_count; +  /* flags is the bitwise OR of zero or more of +     NGTCP2_CRYPTO_KM_FLAG_*. */ +  uint8_t flags; +} ngtcp2_crypto_km; + +/* + * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object, and + * assigns its pointer to |*pckm|.  The |secret| of length + * |secretlen|, |aead_ctx|, and the |iv| of length |ivlen| are copied + * to |*pckm|.  If |secretlen| == 0, the function assumes no secret is + * given which is acceptable.  The sole reason to store secret is + * update keys.  Only 1RTT key can be updated. + */ +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, +                         size_t secretlen, +                         const ngtcp2_crypto_aead_ctx *aead_ctx, +                         const uint8_t *iv, size_t ivlen, +                         const ngtcp2_mem *mem); + +/* + * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but + * it does not copy secret, aead context, and IV. + */ +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, +                                size_t ivlen, const ngtcp2_mem *mem); + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem); + +typedef struct ngtcp2_crypto_cc { +  ngtcp2_crypto_aead aead; +  ngtcp2_crypto_cipher hp; +  ngtcp2_crypto_km *ckm; +  ngtcp2_crypto_cipher_ctx hp_ctx; +  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 /* !defined(NGTCP2_CRYPTO_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_crypto_quictls.c b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_quictls.c new file mode 100644 index 00000000000..c5bd70aefb9 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_quictls.c @@ -0,0 +1,1013 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <assert.h> + +#include <ngtcp2/ngtcp2_crypto.h> +#include <ngtcp2/ngtcp2_crypto_quictls.h> + +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/rand.h> + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +# error #include <openssl/core_names.h> +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +#include "ngtcp2_crypto_shared.h" + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static int crypto_initialized; +static EVP_CIPHER *crypto_aes_128_gcm; +static EVP_CIPHER *crypto_aes_256_gcm; +static EVP_CIPHER *crypto_chacha20_poly1305; +static EVP_CIPHER *crypto_aes_128_ccm; +static EVP_CIPHER *crypto_aes_128_ctr; +static EVP_CIPHER *crypto_aes_256_ctr; +static EVP_CIPHER *crypto_chacha20; +static EVP_MD *crypto_sha256; +static EVP_MD *crypto_sha384; +static EVP_KDF *crypto_hkdf; + +int ngtcp2_crypto_quictls_init(void) { +  crypto_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL); +  if (crypto_aes_128_gcm == NULL) { +    return -1; +  } + +  crypto_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL); +  if (crypto_aes_256_gcm == NULL) { +    return -1; +  } + +  crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL); +  if (crypto_chacha20_poly1305 == NULL) { +    return -1; +  } + +  crypto_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL); +  if (crypto_aes_128_ccm == NULL) { +    return -1; +  } + +  crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL); +  if (crypto_aes_128_ctr == NULL) { +    return -1; +  } + +  crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL); +  if (crypto_aes_256_ctr == NULL) { +    return -1; +  } + +  crypto_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL); +  if (crypto_chacha20 == NULL) { +    return -1; +  } + +  crypto_sha256 = EVP_MD_fetch(NULL, "sha256", NULL); +  if (crypto_sha256 == NULL) { +    return -1; +  } + +  crypto_sha384 = EVP_MD_fetch(NULL, "sha384", NULL); +  if (crypto_sha384 == NULL) { +    return -1; +  } + +  crypto_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL); +  if (crypto_hkdf == NULL) { +    return -1; +  } + +  crypto_initialized = 1; + +  return 0; +} + +static const EVP_CIPHER *crypto_aead_aes_128_gcm(void) { +  if (crypto_aes_128_gcm) { +    return crypto_aes_128_gcm; +  } + +  return EVP_aes_128_gcm(); +} + +static const EVP_CIPHER *crypto_aead_aes_256_gcm(void) { +  if (crypto_aes_256_gcm) { +    return crypto_aes_256_gcm; +  } + +  return EVP_aes_256_gcm(); +} + +static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) { +  if (crypto_chacha20_poly1305) { +    return crypto_chacha20_poly1305; +  } + +  return EVP_chacha20_poly1305(); +} + +static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) { +  if (crypto_aes_128_ccm) { +    return crypto_aes_128_ccm; +  } + +  return EVP_aes_128_ccm(); +} + +static const EVP_CIPHER *crypto_cipher_aes_128_ctr(void) { +  if (crypto_aes_128_ctr) { +    return crypto_aes_128_ctr; +  } + +  return EVP_aes_128_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) { +  if (crypto_aes_256_ctr) { +    return crypto_aes_256_ctr; +  } + +  return EVP_aes_256_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_chacha20(void) { +  if (crypto_chacha20) { +    return crypto_chacha20; +  } + +  return EVP_chacha20(); +} + +static const EVP_MD *crypto_md_sha256(void) { +  if (crypto_sha256) { +    return crypto_sha256; +  } + +  return EVP_sha256(); +} + +static const EVP_MD *crypto_md_sha384(void) { +  if (crypto_sha384) { +    return crypto_sha384; +  } + +  return EVP_sha384(); +} + +static EVP_KDF *crypto_kdf_hkdf(void) { +  if (crypto_hkdf) { +    return crypto_hkdf; +  } + +  return EVP_KDF_fetch(NULL, "hkdf", NULL); +} +#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +#  define crypto_aead_aes_128_gcm EVP_aes_128_gcm +#  define crypto_aead_aes_256_gcm EVP_aes_256_gcm +#  define crypto_aead_chacha20_poly1305 EVP_chacha20_poly1305 +#  define crypto_aead_aes_128_ccm EVP_aes_128_ccm +#  define crypto_cipher_aes_128_ctr EVP_aes_128_ctr +#  define crypto_cipher_aes_256_ctr EVP_aes_256_ctr +#  define crypto_cipher_chacha20 EVP_chacha20 +#  define crypto_md_sha256 EVP_sha256 +#  define crypto_md_sha384 EVP_sha384 + +int ngtcp2_crypto_quictls_init(void) { return 0; } +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ + +static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { +  switch (EVP_CIPHER_nid(aead)) { +  case NID_aes_128_gcm: +  case NID_aes_256_gcm: +    return EVP_GCM_TLS_TAG_LEN; +  case NID_chacha20_poly1305: +    return EVP_CHACHAPOLY_TLS_TAG_LEN; +  case NID_aes_128_ccm: +    return EVP_CCM_TLS_TAG_LEN; +  default: +    assert(0); +    abort(); /* if NDEBUG is set */ +  } +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { +  return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { +  md->native_handle = (void *)crypto_md_sha256(); +  return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { +  ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_aead_aes_128_gcm()); +  ctx->md.native_handle = (void *)crypto_md_sha256(); +  ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ctr(); +  ctx->max_encryption = 0; +  ctx->max_decryption_failure = 0; +  return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, +                                            void *aead_native_handle) { +  aead->native_handle = aead_native_handle; +  aead->max_overhead = crypto_aead_max_overhead(aead_native_handle); +  return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { +  return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +static const EVP_CIPHER *crypto_cipher_id_get_aead(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +    return crypto_aead_aes_128_gcm(); +  case TLS1_3_CK_AES_256_GCM_SHA384: +    return crypto_aead_aes_256_gcm(); +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +    return crypto_aead_chacha20_poly1305(); +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return crypto_aead_aes_128_ccm(); +  default: +    return NULL; +  } +} + +static uint64_t crypto_cipher_id_get_aead_max_encryption(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +  case TLS1_3_CK_AES_256_GCM_SHA384: +    return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +    return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; +  default: +    return 0; +  } +} + +static uint64_t +crypto_cipher_id_get_aead_max_decryption_failure(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +  case TLS1_3_CK_AES_256_GCM_SHA384: +    return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +    return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; +  default: +    return 0; +  } +} + +static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return crypto_cipher_aes_128_ctr(); +  case TLS1_3_CK_AES_256_GCM_SHA384: +    return crypto_cipher_aes_256_ctr(); +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +    return crypto_cipher_chacha20(); +  default: +    return NULL; +  } +} + +static const EVP_MD *crypto_cipher_id_get_md(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return crypto_md_sha256(); +  case TLS1_3_CK_AES_256_GCM_SHA384: +    return crypto_md_sha384(); +  default: +    return NULL; +  } +} + +static int supported_cipher_id(uint32_t cipher_id) { +  switch (cipher_id) { +  case TLS1_3_CK_AES_128_GCM_SHA256: +  case TLS1_3_CK_AES_256_GCM_SHA384: +  case TLS1_3_CK_CHACHA20_POLY1305_SHA256: +  case TLS1_3_CK_AES_128_CCM_SHA256: +    return 1; +  default: +    return 0; +  } +} + +static ngtcp2_crypto_ctx *crypto_ctx_cipher_id(ngtcp2_crypto_ctx *ctx, +                                               uint32_t cipher_id) { +  ngtcp2_crypto_aead_init(&ctx->aead, +                          (void *)crypto_cipher_id_get_aead(cipher_id)); +  ctx->md.native_handle = (void *)crypto_cipher_id_get_md(cipher_id); +  ctx->hp.native_handle = (void *)crypto_cipher_id_get_hp(cipher_id); +  ctx->max_encryption = crypto_cipher_id_get_aead_max_encryption(cipher_id); +  ctx->max_decryption_failure = +    crypto_cipher_id_get_aead_max_decryption_failure(cipher_id); + +  return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, +                                         void *tls_native_handle) { +  SSL *ssl = tls_native_handle; +  const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); +  uint32_t cipher_id; + +  if (cipher == NULL) { +    return NULL; +  } + +  cipher_id = (uint32_t)SSL_CIPHER_get_id(cipher); + +  if (!supported_cipher_id(cipher_id)) { +    return NULL; +  } + +  return crypto_ctx_cipher_id(ctx, cipher_id); +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, +                                               void *tls_native_handle) { +  return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { +  return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { +  return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_CIPHER *aead) { +  return (size_t)EVP_CIPHER_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { +  return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) { +  return (size_t)EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { +  return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, +                                        const ngtcp2_crypto_aead *aead, +                                        const uint8_t *key, size_t noncelen) { +  const EVP_CIPHER *cipher = aead->native_handle; +  int cipher_nid = EVP_CIPHER_nid(cipher); +  EVP_CIPHER_CTX *actx; +  size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  actx = EVP_CIPHER_CTX_new(); +  if (actx == NULL) { +    return -1; +  } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + +  if (cipher_nid == NID_aes_128_ccm) { +    params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, +                                                  NULL, taglen); +    params[2] = OSSL_PARAM_construct_end(); +  } else { +    params[1] = OSSL_PARAM_construct_end(); +  } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +      !EVP_CIPHER_CTX_set_params(actx, params) || +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, +                           NULL) || +      (cipher_nid == NID_aes_128_ccm && +       !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { +    EVP_CIPHER_CTX_free(actx); +    return -1; +  } + +  aead_ctx->native_handle = actx; + +  return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, +                                        const ngtcp2_crypto_aead *aead, +                                        const uint8_t *key, size_t noncelen) { +  const EVP_CIPHER *cipher = aead->native_handle; +  int cipher_nid = EVP_CIPHER_nid(cipher); +  EVP_CIPHER_CTX *actx; +  size_t taglen = crypto_aead_max_overhead(cipher); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  OSSL_PARAM params[3]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  actx = EVP_CIPHER_CTX_new(); +  if (actx == NULL) { +    return -1; +  } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + +  if (cipher_nid == NID_aes_128_ccm) { +    params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, +                                                  NULL, taglen); +    params[2] = OSSL_PARAM_construct_end(); +  } else { +    params[1] = OSSL_PARAM_construct_end(); +  } +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +      !EVP_CIPHER_CTX_set_params(actx, params) || +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, +                           NULL) || +      (cipher_nid == NID_aes_128_ccm && +       !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { +    EVP_CIPHER_CTX_free(actx); +    return -1; +  } + +  aead_ctx->native_handle = actx; + +  return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { +  if (aead_ctx->native_handle) { +    EVP_CIPHER_CTX_free(aead_ctx->native_handle); +  } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, +                                          const ngtcp2_crypto_cipher *cipher, +                                          const uint8_t *key) { +  EVP_CIPHER_CTX *actx; + +  actx = EVP_CIPHER_CTX_new(); +  if (actx == NULL) { +    return -1; +  } + +  if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) { +    EVP_CIPHER_CTX_free(actx); +    return -1; +  } + +  cipher_ctx->native_handle = actx; + +  return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { +  if (cipher_ctx->native_handle) { +    EVP_CIPHER_CTX_free(cipher_ctx->native_handle); +  } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, +                               const uint8_t *secret, size_t secretlen, +                               const uint8_t *salt, size_t saltlen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  const EVP_MD *prf = md->native_handle; +  EVP_KDF *kdf = crypto_kdf_hkdf(); +  EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); +  int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; +  OSSL_PARAM params[] = { +    OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), +    OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, +                                     (char *)EVP_MD_get0_name(prf), 0), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, +                                      secretlen), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, +                                      saltlen), +    OSSL_PARAM_construct_end(), +  }; +  int rv = 0; + +  if (!crypto_initialized) { +    EVP_KDF_free(kdf); +  } + +  if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) { +    rv = -1; +  } + +  EVP_KDF_CTX_free(kctx); + +  return rv; +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +  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; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +} + +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) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  const EVP_MD *prf = md->native_handle; +  EVP_KDF *kdf = crypto_kdf_hkdf(); +  EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); +  int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; +  OSSL_PARAM params[] = { +    OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), +    OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, +                                     (char *)EVP_MD_get0_name(prf), 0), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, +                                      secretlen), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, +                                      infolen), +    OSSL_PARAM_construct_end(), +  }; +  int rv = 0; + +  if (!crypto_initialized) { +    EVP_KDF_free(kdf); +  } + +  if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { +    rv = -1; +  } + +  EVP_KDF_CTX_free(kctx); + +  return rv; +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +  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, (const unsigned char *)"", 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; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, +                       const ngtcp2_crypto_md *md, const uint8_t *secret, +                       size_t secretlen, const uint8_t *salt, size_t saltlen, +                       const uint8_t *info, size_t infolen) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  const EVP_MD *prf = md->native_handle; +  EVP_KDF *kdf = crypto_kdf_hkdf(); +  EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); +  OSSL_PARAM params[] = { +    OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, +                                     (char *)EVP_MD_get0_name(prf), 0), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, +                                      secretlen), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, +                                      saltlen), +    OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, +                                      infolen), +    OSSL_PARAM_construct_end(), +  }; +  int rv = 0; + +  if (!crypto_initialized) { +    EVP_KDF_free(kdf); +  } + +  if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { +    rv = -1; +  } + +  EVP_KDF_CTX_free(kctx); + +  return rv; +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +  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_EXTRACT_AND_EXPAND) != +        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_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; +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                          const ngtcp2_crypto_aead_ctx *aead_ctx, +                          const uint8_t *plaintext, size_t plaintextlen, +                          const uint8_t *nonce, size_t noncelen, +                          const uint8_t *aad, size_t aadlen) { +  const EVP_CIPHER *cipher = aead->native_handle; +  size_t taglen = crypto_aead_max_overhead(cipher); +  int cipher_nid = EVP_CIPHER_nid(cipher); +  EVP_CIPHER_CTX *actx = aead_ctx->native_handle; +  int len; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  OSSL_PARAM params[] = { +    OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, +                                      dest + plaintextlen, taglen), +    OSSL_PARAM_construct_end(), +  }; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  (void)noncelen; + +  if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || +      (cipher_nid == NID_aes_128_ccm && +       !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || +      !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) || +      !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || +      !EVP_EncryptFinal_ex(actx, dest + len, &len) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +      !EVP_CIPHER_CTX_get_params(actx, params) +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, +                           dest + plaintextlen) +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +  ) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                          const ngtcp2_crypto_aead_ctx *aead_ctx, +                          const uint8_t *ciphertext, size_t ciphertextlen, +                          const uint8_t *nonce, size_t noncelen, +                          const uint8_t *aad, size_t aadlen) { +  const EVP_CIPHER *cipher = aead->native_handle; +  size_t taglen = crypto_aead_max_overhead(cipher); +  int cipher_nid = EVP_CIPHER_nid(cipher); +  EVP_CIPHER_CTX *actx = aead_ctx->native_handle; +  int len; +  const uint8_t *tag; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  OSSL_PARAM params[2]; +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  (void)noncelen; + +  if (taglen > ciphertextlen) { +    return -1; +  } + +  ciphertextlen -= taglen; +  tag = ciphertext + ciphertextlen; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +  params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, +                                                (void *)tag, taglen); +  params[1] = OSSL_PARAM_construct_end(); +#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ + +  if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +      !EVP_CIPHER_CTX_set_params(actx, params) || +#else  /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, +                           (uint8_t *)tag) || +#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */ +      (cipher_nid == NID_aes_128_ccm && +       !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || +      !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) || +      !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || +      (cipher_nid != NID_aes_128_ccm && +       !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, +                          const ngtcp2_crypto_cipher_ctx *hp_ctx, +                          const uint8_t *sample) { +  static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; +  EVP_CIPHER_CTX *actx = hp_ctx->native_handle; +  int len; + +  (void)hp; + +  if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || +      !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || +      !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_read_write_crypto_data( +  ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, +  const uint8_t *data, size_t datalen) { +  SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); +  int rv; +  int err; + +  if (SSL_provide_quic_data( +        ssl, +        ngtcp2_crypto_quictls_from_ngtcp2_encryption_level(encryption_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_QUICTLS_ERR_TLS_WANT_CLIENT_HELLO_CB; +      case SSL_ERROR_WANT_X509_LOOKUP: +        return NGTCP2_CRYPTO_QUICTLS_ERR_TLS_WANT_X509_LOOKUP; +      case SSL_ERROR_SSL: +        return -1; +      default: +        return -1; +      } +    } + +    ngtcp2_conn_tls_handshake_completed(conn); +  } + +  rv = SSL_process_quic_post_handshake(ssl); +  if (rv != 1) { +    err = SSL_get_error(ssl, rv); +    switch (err) { +    case SSL_ERROR_WANT_READ: +    case SSL_ERROR_WANT_WRITE: +      return 0; +    case SSL_ERROR_SSL: +    case SSL_ERROR_ZERO_RETURN: +      return -1; +    default: +      return -1; +    } +  } + +  return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { +  SSL *ssl = tls; +  const uint8_t *tp; +  size_t tplen; +  int rv; + +  SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + +  rv = ngtcp2_conn_decode_and_set_remote_transport_params(conn, tp, tplen); +  if (rv != 0) { +    ngtcp2_conn_set_tls_error(conn, rv); +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, +                                             size_t len) { +  if (SSL_set_quic_transport_params(tls, buf, len) != 1) { +    return -1; +  } + +  return 0; +} + +ngtcp2_encryption_level ngtcp2_crypto_quictls_from_ossl_encryption_level( +  OSSL_ENCRYPTION_LEVEL ossl_level) { +  switch (ossl_level) { +  case ssl_encryption_initial: +    return NGTCP2_ENCRYPTION_LEVEL_INITIAL; +  case ssl_encryption_early_data: +    return NGTCP2_ENCRYPTION_LEVEL_0RTT; +  case ssl_encryption_handshake: +    return NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE; +  case ssl_encryption_application: +    return NGTCP2_ENCRYPTION_LEVEL_1RTT; +  default: +    assert(0); +    abort(); /* if NDEBUG is set */ +  } +} + +OSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_quictls_from_ngtcp2_encryption_level( +  ngtcp2_encryption_level encryption_level) { +  switch (encryption_level) { +  case NGTCP2_ENCRYPTION_LEVEL_INITIAL: +    return ssl_encryption_initial; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    return ssl_encryption_handshake; +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    return ssl_encryption_application; +  case NGTCP2_ENCRYPTION_LEVEL_0RTT: +    return ssl_encryption_early_data; +  default: +    assert(0); +    abort(); /* if NDEBUG is set */ +  } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, +                                             void *user_data) { +  (void)conn; +  (void)user_data; + +  if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { +  if (RAND_bytes(data, (int)datalen) != 1) { +    return -1; +  } + +  return 0; +} + +static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, +                                  const uint8_t *rx_secret, +                                  const uint8_t *tx_secret, size_t secretlen) { +  ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); +  ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); +  ngtcp2_encryption_level level = +    ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level); + +  if (rx_secret && +      ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, +                                              rx_secret, secretlen) != 0) { +    return 0; +  } + +  if (tx_secret && +      ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, +                                              tx_secret, secretlen) != 0) { +    return 0; +  } + +  return 1; +} + +static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, +                              const uint8_t *data, size_t datalen) { +  ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); +  ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); +  ngtcp2_encryption_level level = +    ngtcp2_crypto_quictls_from_ossl_encryption_level(ossl_level); +  int rv; + +  rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen); +  if (rv != 0) { +    ngtcp2_conn_set_tls_error(conn, rv); +    return 0; +  } + +  return 1; +} + +static int flush_flight(SSL *ssl) { +  (void)ssl; +  return 1; +} + +static int send_alert(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, uint8_t alert) { +  ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); +  ngtcp2_conn *conn = conn_ref->get_conn(conn_ref); +  (void)level; + +  ngtcp2_conn_set_tls_alert(conn, alert); + +  return 1; +} + +static SSL_QUIC_METHOD quic_method = { +  set_encryption_secrets, +  add_handshake_data, +  flush_flight, +  send_alert, +#ifdef LIBRESSL_VERSION_NUMBER +  NULL, +  NULL, +#endif /* defined(LIBRESSL_VERSION_NUMBER) */ +}; + +static void crypto_quictls_configure_context(SSL_CTX *ssl_ctx) { +  SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); +  SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); +  SSL_CTX_set_quic_method(ssl_ctx, &quic_method); +} + +int ngtcp2_crypto_quictls_configure_server_context(SSL_CTX *ssl_ctx) { +  crypto_quictls_configure_context(ssl_ctx); + +  return 0; +} + +int ngtcp2_crypto_quictls_configure_client_context(SSL_CTX *ssl_ctx) { +  crypto_quictls_configure_context(ssl_ctx); + +  return 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.c b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.c new file mode 100644 index 00000000000..5befb9041ac --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.c @@ -0,0 +1,1450 @@ +/* + * 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_crypto_shared.h" + +#ifdef WIN32 +#  include <winsock2.h> +#  include <ws2tcpip.h> +#elif defined(HAVE_NETINET_IN_H) +#  include <netinet/in.h> +#endif /* defined(HAVE_NETINET_IN_H) */ + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_net.h" + +ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, +                                        void *md_native_handle) { +  md->native_handle = md_native_handle; +  return md; +} + +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)); +} + +int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret, +                                         uint8_t *initial_secret, +                                         uint32_t version, +                                         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; +  const uint8_t *salt; +  size_t saltlen; + +  if (!initial_secret) { +    initial_secret = initial_secret_buf; +  } + +  ngtcp2_crypto_ctx_initial(&ctx); + +  switch (version) { +  case NGTCP2_PROTO_VER_V1: +  default: +    salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1; +    saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1; +    break; +  case NGTCP2_PROTO_VER_V2: +    salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2; +    saltlen = sizeof(NGTCP2_INITIAL_SALT_V2) - 1; +    break; +  } + +  if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data, +                                 client_dcid->datalen, salt, saltlen) != 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_size(8, noncelen); +} + +int ngtcp2_crypto_derive_packet_protection_key( +  uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version, +  const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md, +  const uint8_t *secret, size_t secretlen) { +  static const uint8_t KEY_LABEL_V1[] = "quic key"; +  static const uint8_t IV_LABEL_V1[] = "quic iv"; +  static const uint8_t HP_KEY_LABEL_V1[] = "quic hp"; +  static const uint8_t KEY_LABEL_V2[] = "quicv2 key"; +  static const uint8_t IV_LABEL_V2[] = "quicv2 iv"; +  static const uint8_t HP_KEY_LABEL_V2[] = "quicv2 hp"; +  size_t keylen = ngtcp2_crypto_aead_keylen(aead); +  size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); +  const uint8_t *key_label; +  size_t key_labellen; +  const uint8_t *iv_label; +  size_t iv_labellen; +  const uint8_t *hp_key_label; +  size_t hp_key_labellen; + +  switch (version) { +  case NGTCP2_PROTO_VER_V2: +    key_label = KEY_LABEL_V2; +    key_labellen = sizeof(KEY_LABEL_V2) - 1; +    iv_label = IV_LABEL_V2; +    iv_labellen = sizeof(IV_LABEL_V2) - 1; +    hp_key_label = HP_KEY_LABEL_V2; +    hp_key_labellen = sizeof(HP_KEY_LABEL_V2) - 1; +    break; +  default: +    key_label = KEY_LABEL_V1; +    key_labellen = sizeof(KEY_LABEL_V1) - 1; +    iv_label = IV_LABEL_V1; +    iv_labellen = sizeof(IV_LABEL_V1) - 1; +    hp_key_label = HP_KEY_LABEL_V1; +    hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1; +  } + +  if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, +                                      key_label, key_labellen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, +                                      iv_label, iv_labellen) != 0) { +    return -1; +  } + +  if (hp_key != NULL && +      ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen, +                                      hp_key_label, hp_key_labellen) != 0) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, uint32_t version, +                                        const ngtcp2_crypto_md *md, +                                        const uint8_t *secret, +                                        size_t secretlen) { +  static const uint8_t LABEL[] = "quic ku"; +  static const uint8_t LABEL_V2[] = "quicv2 ku"; +  const uint8_t *label; +  size_t labellen; + +  switch (version) { +  case NGTCP2_PROTO_VER_V2: +    label = LABEL_V2; +    labellen = sizeof(LABEL_V2) - 1; +    break; +  default: +    label = LABEL; +    labellen = sizeof(LABEL) - 1; +  } + +  if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen, +                                      label, labellen) != 0) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, +                                            uint8_t *iv, uint8_t *hp_key, +                                            ngtcp2_encryption_level level, +                                            const uint8_t *secret, +                                            size_t secretlen) { +  const ngtcp2_crypto_ctx *ctx; +  const ngtcp2_crypto_aead *aead; +  const ngtcp2_crypto_md *md; +  const ngtcp2_crypto_cipher *hp; +  ngtcp2_crypto_aead_ctx aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx hp_ctx = {0}; +  void *tls = ngtcp2_conn_get_tls_native_handle(conn); +  uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; +  size_t ivlen; +  int rv; +  ngtcp2_crypto_ctx cctx; +  uint32_t version; + +  if (level == NGTCP2_ENCRYPTION_LEVEL_0RTT && !ngtcp2_conn_is_server(conn)) { +    return 0; +  } + +  if (!key) { +    key = keybuf; +  } +  if (!iv) { +    iv = ivbuf; +  } +  if (!hp_key) { +    hp_key = hp_keybuf; +  } + +  switch (level) { +  case NGTCP2_ENCRYPTION_LEVEL_0RTT: +    if (ngtcp2_crypto_ctx_tls_early(&cctx, tls) == NULL) { +      return -1; +    } + +    ngtcp2_conn_set_0rtt_crypto_ctx(conn, &cctx); +    ctx = ngtcp2_conn_get_0rtt_crypto_ctx(conn); +    version = ngtcp2_conn_get_client_chosen_version(conn); +    break; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    if (ngtcp2_conn_is_server(conn) && +        !ngtcp2_conn_get_negotiated_version(conn)) { +      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); +      if (rv != 0) { +        return -1; +      } +    } +    /* fall through */ +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    ctx = ngtcp2_conn_get_crypto_ctx(conn); +    version = ngtcp2_conn_get_negotiated_version(conn); + +    if (!ctx->aead.native_handle) { +      if (ngtcp2_crypto_ctx_tls(&cctx, tls) == NULL) { +        return -1; +      } + +      ngtcp2_conn_set_crypto_ctx(conn, &cctx); +      ctx = ngtcp2_conn_get_crypto_ctx(conn); +    } +    break; +  default: +    return -1; +  } + +  aead = &ctx->aead; +  md = &ctx->md; +  hp = &ctx->hp; +  ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + +  if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, +                                                 md, secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { +    goto fail; +  } + +  switch (level) { +  case NGTCP2_ENCRYPTION_LEVEL_0RTT: +    rv = ngtcp2_conn_install_0rtt_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } +    break; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    rv = +      ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } +    break; +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    if (!ngtcp2_conn_is_server(conn)) { +      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); +      if (rv != 0) { +        goto fail; +      } +    } + +    rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv, +                                    ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } + +    break; +  default: +    goto fail; +  } + +  return 0; + +fail: +  ngtcp2_crypto_cipher_ctx_free(&hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  return -1; +} + +/* + * crypto_set_local_transport_params gets local QUIC transport + * parameters from |conn| and sets it to |tls|. + * + * This function returns 0 if it succeeds, or -1. + */ +static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) { +  ngtcp2_ssize nwrite; +  uint8_t buf[256]; + +  nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf)); +  if (nwrite < 0) { +    return -1; +  } + +  if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) { +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, +                                            uint8_t *iv, uint8_t *hp_key, +                                            ngtcp2_encryption_level level, +                                            const uint8_t *secret, +                                            size_t secretlen) { +  const ngtcp2_crypto_ctx *ctx; +  const ngtcp2_crypto_aead *aead; +  const ngtcp2_crypto_md *md; +  const ngtcp2_crypto_cipher *hp; +  ngtcp2_crypto_aead_ctx aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx hp_ctx = {0}; +  void *tls = ngtcp2_conn_get_tls_native_handle(conn); +  uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; +  size_t ivlen; +  int rv; +  ngtcp2_crypto_ctx cctx; +  uint32_t version; + +  if (level == NGTCP2_ENCRYPTION_LEVEL_0RTT && ngtcp2_conn_is_server(conn)) { +    return 0; +  } + +  if (!key) { +    key = keybuf; +  } +  if (!iv) { +    iv = ivbuf; +  } +  if (!hp_key) { +    hp_key = hp_keybuf; +  } + +  switch (level) { +  case NGTCP2_ENCRYPTION_LEVEL_0RTT: +    if (ngtcp2_crypto_ctx_tls_early(&cctx, tls) == NULL) { +      return -1; +    } + +    ngtcp2_conn_set_0rtt_crypto_ctx(conn, &cctx); +    ctx = ngtcp2_conn_get_0rtt_crypto_ctx(conn); +    version = ngtcp2_conn_get_client_chosen_version(conn); +    break; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    if (ngtcp2_conn_is_server(conn) && +        !ngtcp2_conn_get_negotiated_version(conn)) { +      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); +      if (rv != 0) { +        return -1; +      } +    } +    /* fall through */ +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    ctx = ngtcp2_conn_get_crypto_ctx(conn); +    version = ngtcp2_conn_get_negotiated_version(conn); + +    if (!ctx->aead.native_handle) { +      if (ngtcp2_crypto_ctx_tls(&cctx, tls) == NULL) { +        return -1; +      } + +      ngtcp2_conn_set_crypto_ctx(conn, &cctx); +      ctx = ngtcp2_conn_get_crypto_ctx(conn); +    } +    break; +  default: +    return -1; +  } + +  aead = &ctx->aead; +  md = &ctx->md; +  hp = &ctx->hp; +  ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + +  if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead, +                                                 md, secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { +    goto fail; +  } + +  switch (level) { +  case NGTCP2_ENCRYPTION_LEVEL_0RTT: +    rv = ngtcp2_conn_install_0rtt_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } +    break; +  case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: +    rv = +      ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } + +    if (ngtcp2_conn_is_server(conn) && +        crypto_set_local_transport_params(conn, tls) != 0) { +      goto fail; +    } + +    break; +  case NGTCP2_ENCRYPTION_LEVEL_1RTT: +    rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv, +                                    ivlen, &hp_ctx); +    if (rv != 0) { +      goto fail; +    } + +    break; +  default: +    goto fail; +  } + +  return 0; + +fail: +  ngtcp2_crypto_cipher_ctx_free(&hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  return -1; +} + +int ngtcp2_crypto_derive_and_install_initial_key( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp_key, +  uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, uint32_t version, +  const ngtcp2_cid *client_dcid) { +  uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; +  uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; +  uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  ngtcp2_crypto_ctx ctx; +  ngtcp2_crypto_aead retry_aead; +  ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; +  ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; +  ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; +  int rv; +  int server = ngtcp2_conn_is_server(conn); +  const uint8_t *retry_key; +  size_t retry_noncelen; + +  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, version, client_dcid, +        server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_derive_packet_protection_key( +        rx_key, rx_iv, rx_hp_key, version, &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, version, &ctx.aead, &ctx.md, tx_secret, +        NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx.aead, rx_key, +                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx.hp, rx_hp_key) != +      0) { +    goto fail; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx.aead, tx_key, +                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx.hp, tx_hp_key) != +      0) { +    goto fail; +  } + +  if (!server && !ngtcp2_conn_after_retry(conn)) { +    ngtcp2_crypto_aead_retry(&retry_aead); + +    switch (version) { +    case NGTCP2_PROTO_VER_V1: +    default: +      retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; +      retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; +      break; +    case NGTCP2_PROTO_VER_V2: +      retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2; +      retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1; +      break; +    } + +    if (ngtcp2_crypto_aead_ctx_encrypt_init(&retry_aead_ctx, &retry_aead, +                                            retry_key, retry_noncelen) != 0) { +      goto fail; +    } +  } + +  rv = ngtcp2_conn_install_initial_key(conn, &rx_aead_ctx, rx_iv, &rx_hp_ctx, +                                       &tx_aead_ctx, tx_iv, &tx_hp_ctx, +                                       NGTCP2_CRYPTO_INITIAL_IVLEN); +  if (rv != 0) { +    goto fail; +  } + +  if (retry_aead_ctx.native_handle) { +    ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx); +  } + +  return 0; + +fail: +  ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx); +  ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); +  ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + +  return -1; +} + +int ngtcp2_crypto_derive_and_install_vneg_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, uint32_t version, +  const ngtcp2_cid *client_dcid) { +  uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; +  uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; +  uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn); +  ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; +  ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; +  int rv; +  int server = ngtcp2_conn_is_server(conn); + +  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; +  } + +  if (ngtcp2_crypto_derive_initial_secrets( +        rx_secret, tx_secret, initial_secret, version, client_dcid, +        server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_derive_packet_protection_key( +        rx_key, rx_iv, rx_hp_key, version, &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, version, &ctx->aead, &ctx->md, tx_secret, +        NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key, +                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) != +      0) { +    goto fail; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key, +                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { +    goto fail; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) != +      0) { +    goto fail; +  } + +  rv = ngtcp2_conn_install_vneg_initial_key( +    conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv, +    &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN); +  if (rv != 0) { +    goto fail; +  } + +  return 0; + +fail: +  ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); +  ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + +  return -1; +} + +int ngtcp2_crypto_update_key( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, +  ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, +  const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, +  size_t secretlen) { +  const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn); +  const ngtcp2_crypto_aead *aead = &ctx->aead; +  const ngtcp2_crypto_md *md = &ctx->md; +  size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); +  uint32_t version = ngtcp2_conn_get_negotiated_version(conn); + +  if (ngtcp2_crypto_update_traffic_secret(rx_secret, version, md, +                                          current_rx_secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_derive_packet_protection_key( +        rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_update_traffic_secret(tx_secret, version, md, +                                          current_tx_secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_derive_packet_protection_key( +        tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) != +      0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) != +      0) { +    ngtcp2_crypto_aead_ctx_free(rx_aead_ctx); +    rx_aead_ctx->native_handle = NULL; +    return -1; +  } + +  return 0; +} + +int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                             const ngtcp2_crypto_aead_ctx *aead_ctx, +                             const uint8_t *plaintext, size_t plaintextlen, +                             const uint8_t *nonce, size_t noncelen, +                             const uint8_t *aad, size_t aadlen) { +  if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen, +                            nonce, noncelen, aad, aadlen) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } +  return 0; +} + +int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, +                             const ngtcp2_crypto_aead_ctx *aead_ctx, +                             const uint8_t *ciphertext, size_t ciphertextlen, +                             const uint8_t *nonce, size_t noncelen, +                             const uint8_t *aad, size_t aadlen) { +  if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen, +                            nonce, noncelen, aad, aadlen) != 0) { +    return NGTCP2_ERR_DECRYPT; +  } +  return 0; +} + +int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, +                             const ngtcp2_crypto_cipher_ctx *hp_ctx, +                             const uint8_t *sample) { +  if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } +  return 0; +} + +int ngtcp2_crypto_update_key_cb( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, +  ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, +  const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, +  size_t secretlen, void *user_data) { +  uint8_t rx_key[64]; +  uint8_t tx_key[64]; +  (void)conn; +  (void)user_data; + +  if (ngtcp2_crypto_update_key( +        conn, rx_secret, tx_secret, rx_aead_ctx, rx_key, rx_iv, tx_aead_ctx, +        tx_key, tx_iv, current_rx_secret, current_tx_secret, secretlen) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } +  return 0; +} + +int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, +                                                 const uint8_t *secret, +                                                 size_t secretlen, +                                                 const ngtcp2_cid *cid) { +  static const uint8_t info[] = "stateless_reset"; +  ngtcp2_crypto_md md; + +  if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN, +                         ngtcp2_crypto_md_sha256(&md), secret, secretlen, +                         cid->data, cid->datalen, info, +                         sizeof(info) - 1) != 0) { +    return -1; +  } + +  return 0; +} + +static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv, +                                   size_t ivlen, const ngtcp2_crypto_md *md, +                                   const uint8_t *secret, size_t secretlen, +                                   const uint8_t *salt, size_t saltlen, +                                   const uint8_t *info_prefix, +                                   size_t info_prefixlen) { +  static const uint8_t key_info_suffix[] = " key"; +  static const uint8_t iv_info_suffix[] = " iv"; +  uint8_t intsecret[32]; +  uint8_t info[32]; +  uint8_t *p; + +  assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret)); +  assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info)); +  assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info)); + +  if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt, +                                 saltlen) != 0) { +    return -1; +  } + +  memcpy(info, info_prefix, info_prefixlen); +  p = info + info_prefixlen; + +  memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1); +  p += sizeof(key_info_suffix) - 1; + +  if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret), +                                info, (size_t)(p - info)) != 0) { +    return -1; +  } + +  p = info + info_prefixlen; + +  memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1); +  p += sizeof(iv_info_suffix) - 1; + +  if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret), +                                info, (size_t)(p - info)) != 0) { +    return -1; +  } + +  return 0; +} + +static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version, +                                              const ngtcp2_sockaddr *sa, +                                              ngtcp2_socklen salen, +                                              const ngtcp2_cid *retry_scid) { +  uint8_t *p = dest; + +  version = ngtcp2_htonl(version); +  memcpy(p, &version, sizeof(version)); +  p += sizeof(version); +  memcpy(p, sa, (size_t)salen); +  p += salen; +  memcpy(p, retry_scid->data, retry_scid->datalen); +  p += retry_scid->datalen; + +  return (size_t)(p - dest); +} + +static const uint8_t retry_token_info_prefix[] = "retry_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_retry_token( +  uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) { +  uint8_t +    plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)]; +  uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; +  uint8_t key[16]; +  uint8_t iv[12]; +  size_t keylen; +  size_t ivlen; +  ngtcp2_crypto_aead aead; +  ngtcp2_crypto_md md; +  ngtcp2_crypto_aead_ctx aead_ctx; +  size_t plaintextlen; +  uint8_t +    aad[sizeof(version) + sizeof(ngtcp2_sockaddr_union) + NGTCP2_MAX_CIDLEN]; +  size_t aadlen; +  uint8_t *p = plaintext; +  ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); +  int rv; + +  assert((size_t)remote_addrlen <= sizeof(ngtcp2_sockaddr_union)); + +  memset(plaintext, 0, sizeof(plaintext)); + +  *p++ = (uint8_t)odcid->datalen; +  memcpy(p, odcid->data, odcid->datalen); +  p += NGTCP2_MAX_CIDLEN; +  memcpy(p, &ts_be, sizeof(ts_be)); +  p += sizeof(ts_be); + +  plaintextlen = (size_t)(p - plaintext); + +  if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { +    return -1; +  } + +  ngtcp2_crypto_aead_aes_128_gcm(&aead); +  ngtcp2_crypto_md_sha256(&md); + +  keylen = ngtcp2_crypto_aead_keylen(&aead); +  ivlen = ngtcp2_crypto_aead_noncelen(&aead); + +  assert(sizeof(key) == keylen); +  assert(sizeof(iv) == ivlen); + +  if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, +                              rand_data, sizeof(rand_data), +                              retry_token_info_prefix, +                              sizeof(retry_token_info_prefix) - 1) != 0) { +    return -1; +  } + +  aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, +                                           remote_addrlen, retry_scid); + +  p = token; +  *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY; + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { +    return -1; +  } + +  rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, +                             ivlen, aad, aadlen); + +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  if (rv != 0) { +    return -1; +  } + +  p += plaintextlen + aead.max_overhead; +  memcpy(p, rand_data, sizeof(rand_data)); +  p += sizeof(rand_data); + +  return p - token; +} + +int ngtcp2_crypto_verify_retry_token( +  ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen, +  const uint8_t *secret, size_t secretlen, uint32_t version, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) { +  uint8_t +    plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)]; +  uint8_t key[16]; +  uint8_t iv[12]; +  size_t keylen; +  size_t ivlen; +  ngtcp2_crypto_aead_ctx aead_ctx; +  ngtcp2_crypto_aead aead; +  ngtcp2_crypto_md md; +  uint8_t +    aad[sizeof(version) + sizeof(ngtcp2_sockaddr_union) + NGTCP2_MAX_CIDLEN]; +  size_t aadlen; +  const uint8_t *rand_data; +  const uint8_t *ciphertext; +  size_t ciphertextlen; +  size_t cil; +  int rv; +  ngtcp2_tstamp gen_ts; + +  assert((size_t)remote_addrlen <= sizeof(ngtcp2_sockaddr_union)); + +  if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN || +      token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) { +    return -1; +  } + +  rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; +  ciphertext = token + 1; +  ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + +  ngtcp2_crypto_aead_aes_128_gcm(&aead); +  ngtcp2_crypto_md_sha256(&md); + +  keylen = ngtcp2_crypto_aead_keylen(&aead); +  ivlen = ngtcp2_crypto_aead_noncelen(&aead); + +  assert(sizeof(key) == keylen); +  assert(sizeof(iv) == ivlen); + +  if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, +                              rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, +                              retry_token_info_prefix, +                              sizeof(retry_token_info_prefix) - 1) != 0) { +    return -1; +  } + +  aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr, +                                           remote_addrlen, dcid); + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { +    return -1; +  } + +  rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, +                             ciphertextlen, iv, ivlen, aad, aadlen); + +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  if (rv != 0) { +    return -1; +  } + +  cil = plaintext[0]; + +  if (cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) { +    return -1; +  } + +  memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN, +         sizeof(gen_ts)); + +  gen_ts = ngtcp2_ntohl64(gen_ts); +  if (gen_ts + timeout <= ts) { +    return -1; +  } + +  ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil); + +  return 0; +} + +static size_t crypto_generate_regular_token_aad(uint8_t *dest, +                                                const ngtcp2_sockaddr *sa) { +  const uint8_t *addr; +  size_t addrlen; + +  switch (sa->sa_family) { +  case NGTCP2_AF_INET: +    addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr; +    addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr); +    break; +  case NGTCP2_AF_INET6: +    addr = +      (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr; +    addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr); +    break; +  default: +    assert(0); +    abort(); +  } + +  memcpy(dest, addr, addrlen); + +  return addrlen; +} + +static const uint8_t regular_token_info_prefix[] = "regular_token"; + +ngtcp2_ssize ngtcp2_crypto_generate_regular_token( +  uint8_t *token, const uint8_t *secret, size_t secretlen, +  const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen, +  ngtcp2_tstamp ts) { +  uint8_t plaintext[sizeof(ngtcp2_tstamp)]; +  uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN]; +  uint8_t key[16]; +  uint8_t iv[12]; +  size_t keylen; +  size_t ivlen; +  ngtcp2_crypto_aead aead; +  ngtcp2_crypto_md md; +  ngtcp2_crypto_aead_ctx aead_ctx; +  size_t plaintextlen; +  uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; +  size_t aadlen; +  uint8_t *p = plaintext; +  ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts); +  int rv; +  (void)remote_addrlen; + +  memcpy(p, &ts_be, sizeof(ts_be)); +  p += sizeof(ts_be); + +  plaintextlen = (size_t)(p - plaintext); + +  if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) { +    return -1; +  } + +  ngtcp2_crypto_aead_aes_128_gcm(&aead); +  ngtcp2_crypto_md_sha256(&md); + +  keylen = ngtcp2_crypto_aead_keylen(&aead); +  ivlen = ngtcp2_crypto_aead_noncelen(&aead); + +  assert(sizeof(key) == keylen); +  assert(sizeof(iv) == ivlen); + +  if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, +                              rand_data, sizeof(rand_data), +                              regular_token_info_prefix, +                              sizeof(regular_token_info_prefix) - 1) != 0) { +    return -1; +  } + +  aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + +  p = token; +  *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR; + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { +    return -1; +  } + +  rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv, +                             ivlen, aad, aadlen); + +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  if (rv != 0) { +    return -1; +  } + +  p += plaintextlen + aead.max_overhead; +  memcpy(p, rand_data, sizeof(rand_data)); +  p += sizeof(rand_data); + +  return p - token; +} + +int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen, +                                       const uint8_t *secret, size_t secretlen, +                                       const ngtcp2_sockaddr *remote_addr, +                                       ngtcp2_socklen remote_addrlen, +                                       ngtcp2_duration timeout, +                                       ngtcp2_tstamp ts) { +  uint8_t plaintext[sizeof(ngtcp2_tstamp)]; +  uint8_t key[16]; +  uint8_t iv[12]; +  size_t keylen; +  size_t ivlen; +  ngtcp2_crypto_aead_ctx aead_ctx; +  ngtcp2_crypto_aead aead; +  ngtcp2_crypto_md md; +  uint8_t aad[sizeof(ngtcp2_sockaddr_in6)]; +  size_t aadlen; +  const uint8_t *rand_data; +  const uint8_t *ciphertext; +  size_t ciphertextlen; +  int rv; +  ngtcp2_tstamp gen_ts; +  (void)remote_addrlen; + +  if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN || +      token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) { +    return -1; +  } + +  rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; +  ciphertext = token + 1; +  ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN; + +  ngtcp2_crypto_aead_aes_128_gcm(&aead); +  ngtcp2_crypto_md_sha256(&md); + +  keylen = ngtcp2_crypto_aead_keylen(&aead); +  ivlen = ngtcp2_crypto_aead_noncelen(&aead); + +  assert(sizeof(key) == keylen); +  assert(sizeof(iv) == ivlen); + +  if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen, +                              rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN, +                              regular_token_info_prefix, +                              sizeof(regular_token_info_prefix) - 1) != 0) { +    return -1; +  } + +  aadlen = crypto_generate_regular_token_aad(aad, remote_addr); + +  if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) { +    return -1; +  } + +  rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext, +                             ciphertextlen, iv, ivlen, aad, aadlen); + +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  if (rv != 0) { +    return -1; +  } + +  memcpy(&gen_ts, plaintext, sizeof(gen_ts)); + +  gen_ts = ngtcp2_ntohl64(gen_ts); +  if (gen_ts + timeout <= ts) { +    return -1; +  } + +  return 0; +} + +ngtcp2_ssize ngtcp2_crypto_write_connection_close( +  uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason, +  size_t reasonlen) { +  uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; +  uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; +  uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; +  ngtcp2_crypto_ctx ctx; +  ngtcp2_ssize spktlen; +  ngtcp2_crypto_aead_ctx aead_ctx = {0}; +  ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + +  ngtcp2_crypto_ctx_initial(&ctx); + +  if (ngtcp2_crypto_derive_initial_secrets(rx_secret, tx_secret, initial_secret, +                                           version, scid, +                                           NGTCP2_CRYPTO_SIDE_SERVER) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_derive_packet_protection_key( +        tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret, +        NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { +    return -1; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key, +                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { +    spktlen = -1; +    goto end; +  } + +  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) { +    spktlen = -1; +    goto end; +  } + +  spktlen = ngtcp2_pkt_write_connection_close( +    dest, destlen, version, dcid, scid, error_code, reason, reasonlen, +    ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv, +    ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); +  if (spktlen < 0) { +    spktlen = -1; +  } + +end: +  ngtcp2_crypto_cipher_ctx_free(&hp_ctx); +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  return spktlen; +} + +ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, +                                       uint32_t version, const ngtcp2_cid *dcid, +                                       const ngtcp2_cid *scid, +                                       const ngtcp2_cid *odcid, +                                       const uint8_t *token, size_t tokenlen) { +  ngtcp2_crypto_aead aead; +  ngtcp2_ssize spktlen; +  ngtcp2_crypto_aead_ctx aead_ctx = {0}; +  const uint8_t *key; +  size_t noncelen; + +  ngtcp2_crypto_aead_retry(&aead); + +  switch (version) { +  case NGTCP2_PROTO_VER_V1: +  default: +    key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; +    break; +  case NGTCP2_PROTO_VER_V2: +    key = (const uint8_t *)NGTCP2_RETRY_KEY_V2; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1; +    break; +  } + +  if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, noncelen) != +      0) { +    return -1; +  } + +  spktlen = ngtcp2_pkt_write_retry(dest, destlen, version, dcid, scid, odcid, +                                   token, tokenlen, ngtcp2_crypto_encrypt_cb, +                                   &aead, &aead_ctx); +  if (spktlen < 0) { +    spktlen = -1; +  } + +  ngtcp2_crypto_aead_ctx_free(&aead_ctx); + +  return spktlen; +} + +int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { +  const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); +  void *tls = ngtcp2_conn_get_tls_native_handle(conn); +  (void)user_data; + +  if (ngtcp2_crypto_derive_and_install_initial_key( +        conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +        ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  if (crypto_set_local_transport_params(conn, tls) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  if (ngtcp2_crypto_read_write_crypto_data( +        conn, NGTCP2_ENCRYPTION_LEVEL_INITIAL, NULL, 0) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, +                                void *user_data) { +  (void)user_data; + +  if (ngtcp2_crypto_derive_and_install_initial_key( +        conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +        ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, +                                         const ngtcp2_cid *dcid, +                                         void *user_data) { +  (void)user_data; + +  if (ngtcp2_crypto_derive_and_install_initial_key( +        conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +        ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version, +                                         const ngtcp2_cid *client_dcid, +                                         void *user_data) { +  (void)user_data; + +  if (ngtcp2_crypto_derive_and_install_vneg_initial_key( +        conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version, +        client_dcid) != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  return 0; +} + +void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn, +                                             ngtcp2_crypto_aead_ctx *aead_ctx, +                                             void *user_data) { +  (void)conn; +  (void)user_data; + +  ngtcp2_crypto_aead_ctx_free(aead_ctx); +} + +void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( +  ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) { +  (void)conn; +  (void)user_data; + +  ngtcp2_crypto_cipher_ctx_free(cipher_ctx); +} + +int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn, +                                      ngtcp2_encryption_level encryption_level, +                                      uint64_t offset, const uint8_t *data, +                                      size_t datalen, void *user_data) { +  int rv; +  (void)offset; +  (void)user_data; + +  if (ngtcp2_crypto_read_write_crypto_data(conn, encryption_level, data, +                                           datalen) != 0) { +    rv = ngtcp2_conn_get_tls_error(conn); +    if (rv) { +      return rv; +    } +    return NGTCP2_ERR_CRYPTO; +  } + +  return 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.h b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.h new file mode 100644 index 00000000000..34158d3d02d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_crypto_shared.h @@ -0,0 +1,397 @@ +/* + * 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 SHARED_H +#define SHARED_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2_crypto.h> + +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to + * derive initial secret.  It is used for QUIC v1. + */ +#define NGTCP2_INITIAL_SALT_V1                                                 \ +  "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb"   \ +  "\x7f\x0a" + +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V2` is a salt value which is used to + * derive initial secret.  It is used for QUIC v2. + */ +#define NGTCP2_INITIAL_SALT_V2                                                 \ +  "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd"   \ +  "\x2e\xd9" + +/* Maximum key usage (encryption) limits */ +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23) +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL) + +/* Maximum authentication failure (decryption) limits during the +   lifetime of a connection. */ +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 52) +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL) + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_SECRETLEN` is the length of secret + * for Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_KEYLEN` is the length of key for + * Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_IVLEN` is the length of IV for + * Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_IVLEN 12 + +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_aead_init` initializes |aead| with the provided + * |aead_native_handle| which is an underlying AEAD object. + * + * If libngtcp2_crypto_quictls is linked, |aead_native_handle| must be + * a pointer to EVP_CIPHER. + * + * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be + * gnutls_cipher_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must + * be a pointer to EVP_AEAD. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, +                                            void *aead_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + +/** + * @enum + * + * :type:`ngtcp2_crypto_side` indicates which side the application + * implements; client or server. + */ +typedef enum ngtcp2_crypto_side { +  /** +   * :enum:`NGTCP2_CRYPTO_SIDE_CLIENT` indicates that the application +   * is client. +   */ +  NGTCP2_CRYPTO_SIDE_CLIENT, +  /** +   * :enum:`NGTCP2_CRYPTO_SIDE_SERVER` indicates that the application +   * is server. +   */ +  NGTCP2_CRYPTO_SIDE_SERVER +} ngtcp2_crypto_side; + +/** + * @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, +                                         uint32_t version, +                                         const ngtcp2_cid *client_dcid, +                                         ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` derives packet + * protection key.  This function writes packet protection key into + * the buffer pointed by |key|.  The length of derived key is + * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>` + * bytes.  |key| must have enough capacity to store the key.  This + * function writes packet protection IV into |iv|.  The length of + * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead) + * <ngtcp2_crypto_packet_protection_ivlen>` bytes.  |iv| must have + * enough capacity to store the IV. + * + * 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 derived key is `ngtcp2_crypto_aead_keylen(aead) + * <ngtcp2_crypto_aead_keylen>` bytes.  |hp|, if not NULL, must have + * enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv, +                                               uint8_t *hp, uint32_t version, +                                               const ngtcp2_crypto_aead *aead, +                                               const ngtcp2_crypto_md *md, +                                               const uint8_t *secret, +                                               size_t secretlen); + +/** + * @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, uint32_t version, +                                        const ngtcp2_crypto_md *md, +                                        const uint8_t *secret, +                                        size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport + * parameter, which is encoded in wire format and stored in the buffer + * pointed by |buf| of length |len|, to the native handle |tls|. + * + * |tls| points to a implementation dependent TLS session object.  If + * libngtcp2_crypto_quictls is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, +                                             size_t len); + +/** + * @function + * + * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC + * transport parameters from |tls| and sets it to |conn| using + * `ngtcp2_conn_set_remote_transport_params`. + * + * |tls| points to a implementation dependent TLS session object.  If + * libngtcp2_crypto_quictls is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_initial_key` derives initial + * keying materials and installs keys to |conn|. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|.  The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|.  The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|.  The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|.  If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|.  If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|.  If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|.  If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long.  The length of packet protection IV is 12 bytes long. + * + * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set + * initial AEAD and message digest algorithm.  After the successful + * call of this function, application can use + * `ngtcp2_conn_get_initial_crypto_ctx` to get the object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_initial_key( +  ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, +  uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, +  uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version, +  const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial + * keying materials and installs keys to |conn|.  This function is + * dedicated to install keys for |version| which is negotiated, or + * being negotiated. + * + * 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 returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_vneg_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, uint32_t version, +  const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx| + * with new cipher context object for encryption which is constructed + * to use |key| as encryption key.  |cipher| specifies cipher to use. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, +                                          const ngtcp2_crypto_cipher *cipher, +                                          const uint8_t *key); + +/** + * @function + * + * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by + * |cipher_ctx|.  This function does not free the memory pointed by + * |cipher_ctx| itself. + */ +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx); + +/* + * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message + * digest algorithm and returns |md|. + */ +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md); + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead); + +/* + * `ngtcp2_crypto_random` writes cryptographically-secure random + * |datalen| bytes into the buffer pointed by |data|. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_random(uint8_t *data, size_t datalen); + +/** + * @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. + */ +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); + +#endif /* !defined(SHARED_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_err.c b/contrib/libs/ngtcp2/lib/ngtcp2_err.c new file mode 100644 index 00000000000..5e4794cd72e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_err.c @@ -0,0 +1,157 @@ +/* + * 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_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_DECRYPT: +    return "ERR_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_CONN_ID_BLOCKED: +    return "ERR_CONN_ID_BLOCKED"; +  case NGTCP2_ERR_CALLBACK_FAILURE: +    return "ERR_CALLBACK_FAILURE"; +  case NGTCP2_ERR_INTERNAL: +    return "ERR_INTERNAL"; +  case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: +    return "ERR_CRYPTO_BUFFER_EXCEEDED"; +  case NGTCP2_ERR_WRITE_MORE: +    return "ERR_WRITE_MORE"; +  case NGTCP2_ERR_RETRY: +    return "ERR_RETRY"; +  case NGTCP2_ERR_DROP_CONN: +    return "ERR_DROP_CONN"; +  case NGTCP2_ERR_AEAD_LIMIT_REACHED: +    return "ERR_AEAD_LIMIT_REACHED"; +  case NGTCP2_ERR_NO_VIABLE_PATH: +    return "ERR_NO_VIABLE_PATH"; +  case NGTCP2_ERR_VERSION_NEGOTIATION: +    return "ERR_VERSION_NEGOTIATION"; +  case NGTCP2_ERR_HANDSHAKE_TIMEOUT: +    return "ERR_HANDSHAKE_TIMEOUT"; +  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: +    return "ERR_VERSION_NEGOTIATION_FAILURE"; +  case NGTCP2_ERR_IDLE_CLOSE: +    return "ERR_IDLE_CLOSE"; +  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: +  case NGTCP2_ERR_NOMEM: +  case NGTCP2_ERR_CALLBACK_FAILURE: +  case NGTCP2_ERR_HANDSHAKE_TIMEOUT: +  case NGTCP2_ERR_PKT_NUM_EXHAUSTED: +  case NGTCP2_ERR_INTERNAL: +    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; +  case NGTCP2_ERR_AEAD_LIMIT_REACHED: +    return NGTCP2_AEAD_LIMIT_REACHED; +  case NGTCP2_ERR_NO_VIABLE_PATH: +    return NGTCP2_NO_VIABLE_PATH; +  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: +    return NGTCP2_VERSION_NEGOTIATION_ERROR; +  default: +    return NGTCP2_PROTOCOL_VIOLATION; +  } +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_err.h b/contrib/libs/ngtcp2/lib/ngtcp2_err.h new file mode 100644 index 00000000000..44527b11bde --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#endif /* !defined(NGTCP2_ERR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.c b/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.c new file mode 100644 index 00000000000..cf0f1171796 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.c @@ -0,0 +1,215 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_frame_chain.h" + +#include <string.h> +#include <assert.h> + +ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent); + +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_objalloc_new(ngtcp2_frame_chain **pfrc, +                                    ngtcp2_objalloc *objalloc) { +  *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc); +  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_objalloc_new(ngtcp2_frame_chain **pfrc, +                                                   size_t datacnt, +                                                   ngtcp2_objalloc *objalloc, +                                                   const ngtcp2_mem *mem) { +  if (datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) { +    return ngtcp2_frame_chain_extralen_new(pfrc, +                                           sizeof(ngtcp2_vec) * (datacnt - 1) - +                                             NGTCP2_FRAME_CHAIN_STREAM_AVAIL, +                                           mem); +  } + +  return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); +} + +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, +                                              const uint8_t *token, +                                              size_t tokenlen, +                                              ngtcp2_objalloc *objalloc, +                                              const ngtcp2_mem *mem) { +  size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); +  int rv; +  uint8_t *p; +  ngtcp2_frame *fr; + +  if (tokenlen > avail) { +    rv = ngtcp2_frame_chain_extralen_new(pfrc, tokenlen - avail, mem); +  } else { +    rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); +  } +  if (rv != 0) { +    return rv; +  } + +  fr = &(*pfrc)->fr; +  fr->type = NGTCP2_FRAME_NEW_TOKEN; + +  p = (uint8_t *)fr + sizeof(ngtcp2_new_token); +  memcpy(p, token, tokenlen); + +  fr->new_token.token = p; +  fr->new_token.tokenlen = tokenlen; + +  return 0; +} + +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { +  ngtcp2_frame_chain_binder *binder; + +  if (frc == NULL) { +    return; +  } + +  binder = frc->binder; +  if (binder && --binder->refcount == 0) { +    ngtcp2_mem_free(mem, binder); +  } + +  ngtcp2_mem_free(mem, frc); +} + +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, +                                     ngtcp2_objalloc *objalloc, +                                     const ngtcp2_mem *mem) { +  ngtcp2_frame_chain_binder *binder; + +  if (frc == NULL) { +    return; +  } + +  switch (frc->fr.type) { +  case NGTCP2_FRAME_CRYPTO: +  case NGTCP2_FRAME_STREAM: +    if (frc->fr.stream.datacnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) { +      ngtcp2_frame_chain_del(frc, mem); + +      return; +    } + +    break; +  case NGTCP2_FRAME_NEW_TOKEN: +    if (frc->fr.new_token.tokenlen > +        sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) { +      ngtcp2_frame_chain_del(frc, mem); + +      return; +    } + +    break; +  } + +  binder = frc->binder; +  if (binder && --binder->refcount == 0) { +    ngtcp2_mem_free(mem, binder); +  } + +  frc->binder = NULL; + +  ngtcp2_objalloc_frame_chain_release(objalloc, frc); +} + +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { +  frc->next = NULL; +  frc->binder = NULL; +} + +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, +                                          ngtcp2_objalloc *objalloc, +                                          const ngtcp2_mem *mem) { +  ngtcp2_frame_chain *next; + +  for (; frc; frc = next) { +    next = frc->next; + +    ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem); +  } +} + +int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, +                                  const ngtcp2_mem *mem) { +  *pbinder = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_frame_chain_binder)); +  if (*pbinder == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  return 0; +} + +int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, +                             const ngtcp2_mem *mem) { +  ngtcp2_frame_chain_binder *binder; +  int rv; + +  assert(b->binder == NULL); + +  if (a->binder == NULL) { +    rv = ngtcp2_frame_chain_binder_new(&binder, mem); +    if (rv != 0) { +      return rv; +    } + +    a->binder = binder; +    ++a->binder->refcount; +  } + +  b->binder = a->binder; +  ++b->binder->refcount; + +  return 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h b/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h new file mode 100644 index 00000000000..c38f1aeac94 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h @@ -0,0 +1,188 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_FRAME_CHAIN_H +#define NGTCP2_FRAME_CHAIN_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_objalloc.h" + +/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is +   set. */ +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u +/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information +   which a frame carries has been acknowledged. */ +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u + +/* + * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to + * share the acknowledgement state.  In general, all + * ngtcp2_frame_chains bound to the same binder must have the same + * information. + */ +typedef struct ngtcp2_frame_chain_binder { +  size_t refcount; +  /* flags is bitwise OR of zero or more of +     NGTCP2_FRAME_CHAIN_BINDER_FLAG_*. */ +  uint32_t flags; +} ngtcp2_frame_chain_binder; + +int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, +                                  const ngtcp2_mem *mem); + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* + * ngtcp2_frame_chain chains frames in a single packet. + */ +struct ngtcp2_frame_chain { +  union { +    struct { +      ngtcp2_frame_chain *next; +      ngtcp2_frame_chain_binder *binder; +      ngtcp2_frame fr; +    }; + +    ngtcp2_opl_entry oplent; +  }; +}; + +ngtcp2_objalloc_decl(frame_chain, ngtcp2_frame_chain, oplent); + +/* + * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using + * new or existing ngtcp2_frame_chain_binder.  |a| might have non-NULL + * a->binder.  |b| must not have non-NULL b->binder. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, +                             const ngtcp2_mem *mem); + +/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that +   a ngtcp2_stream can include. */ +#define NGTCP2_MAX_STREAM_DATACNT 256 + +/* + * 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_objalloc_new behaves like + * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object. + */ +int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, +                                    ngtcp2_objalloc *objalloc); + +/* + * 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_AVAIL is the number of additional bytes +   available after ngtcp2_stream when it is embedded in +   ngtcp2_frame. */ +#define NGTCP2_FRAME_CHAIN_STREAM_AVAIL                                        \ +  (sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) + +/* NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES is the number of datacnt +   that changes allocation method.  If datacnt is more than this +   value, ngtcp2_frame_chain is allocated without ngtcp2_objalloc. +   Otherwise, it is allocated using ngtcp2_objalloc.  */ +#define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES                                \ +  (NGTCP2_FRAME_CHAIN_STREAM_AVAIL / sizeof(ngtcp2_vec) + 1) + +/* + * ngtcp2_frame_chain_stream_datacnt_objalloc_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 no additional space is required, in other words, + * |datacnt| <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES, + * ngtcp2_frame_chain_objalloc_new is called internally.  Otherwise, + * ngtcp2_frame_chain_extralen_new is used and objalloc is not used. + * Therefore, it is important to call ngtcp2_frame_chain_objalloc_del + * without changing datacnt field. + */ +int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, +                                                   size_t datacnt, +                                                   ngtcp2_objalloc *objalloc, +                                                   const ngtcp2_mem *mem); + +int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, +                                              const uint8_t *token, +                                              size_t tokenlen, +                                              ngtcp2_objalloc *objalloc, +                                              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_objalloc_del adds |frc| to |objalloc| for reuse. + * It might just delete |frc| depending on the frame type and the size + * of |frc|. + */ +void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, +                                     ngtcp2_objalloc *objalloc, +                                     const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_init initializes |frc|. + */ +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); + +/* + * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain + * linked from |frc| to |objalloc| for reuse.  Depending on the frame type + * and its size, ngtcp2_frame_chain might be deleted instead. + */ +void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc, +                                          ngtcp2_objalloc *objalloc, +                                          const ngtcp2_mem *mem); + +#endif /* !defined(NGTCP2_FRAME_CHAIN_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.c b/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.c new file mode 100644 index 00000000000..1475c228656 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.c @@ -0,0 +1,161 @@ +/* + * 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 <string.h> +#include <assert.h> + +void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { +  ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), +                  mem); + +  gaptr->mem = mem; +} + +static int gaptr_gap_init(ngtcp2_gaptr *gaptr) { +  ngtcp2_range range = {0, UINT64_MAX}; + +  return ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); +} + +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, uint64_t datalen) { +  int rv; +  ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; +  ngtcp2_ksl_it it; + +  if (ngtcp2_ksl_len(&gaptr->gap) == 0) { +    rv = gaptr_gap_init(gaptr); +    if (rv != 0) { +      return rv; +    } +  } + +  it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, +                                     ngtcp2_ksl_range_exclusive_compar); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); +    m = ngtcp2_range_intersect(&q, &k); +    if (!ngtcp2_range_len(&m)) { +      break; +    } + +    if (ngtcp2_range_eq(&k, &m)) { +      ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k); +      continue; +    } + +    ngtcp2_range_cut(&l, &r, &k, &m); + +    if (ngtcp2_range_len(&l)) { +      ngtcp2_ksl_update_key(&gaptr->gap, &k, &l); + +      if (ngtcp2_range_len(&r)) { +        rv = ngtcp2_ksl_insert(&gaptr->gap, &it, &r, NULL); +        if (rv != 0) { +          return rv; +        } +      } +    } else if (ngtcp2_range_len(&r)) { +      ngtcp2_ksl_update_key(&gaptr->gap, &k, &r); +    } + +    ngtcp2_ksl_it_next(&it); +  } + +  return 0; +} + +uint64_t ngtcp2_gaptr_first_gap_offset(const ngtcp2_gaptr *gaptr) { +  ngtcp2_ksl_it it; + +  if (ngtcp2_ksl_len(&gaptr->gap) == 0) { +    return 0; +  } + +  it = ngtcp2_ksl_begin(&gaptr->gap); + +  return ((ngtcp2_range *)ngtcp2_ksl_it_key(&it))->begin; +} + +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr, +                                              uint64_t offset) { +  ngtcp2_range q = {offset, offset + 1}; +  ngtcp2_ksl_it it; + +  if (ngtcp2_ksl_len(&gaptr->gap) == 0) { +    ngtcp2_range r = {0, UINT64_MAX}; +    return r; +  } + +  it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, +                                     ngtcp2_ksl_range_exclusive_compar); + +  assert(!ngtcp2_ksl_it_end(&it)); + +  return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); +} + +int ngtcp2_gaptr_is_pushed(const ngtcp2_gaptr *gaptr, uint64_t offset, +                           uint64_t datalen) { +  ngtcp2_range q = {offset, offset + datalen}; +  ngtcp2_ksl_it it; +  ngtcp2_range m; + +  if (ngtcp2_ksl_len(&gaptr->gap) == 0) { +    return 0; +  } + +  it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, +                                     ngtcp2_ksl_range_exclusive_compar); +  m = ngtcp2_range_intersect(&q, (ngtcp2_range *)ngtcp2_ksl_it_key(&it)); + +  return ngtcp2_range_len(&m) == 0; +} + +void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { +  ngtcp2_ksl_it it; +  ngtcp2_range r; + +  if (ngtcp2_ksl_len(&gaptr->gap) == 0) { +    return; +  } + +  it = ngtcp2_ksl_begin(&gaptr->gap); + +  assert(!ngtcp2_ksl_it_end(&it)); + +  r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + +  ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.h b/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.h new file mode 100644 index 00000000000..3120676cf84 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_gaptr.h @@ -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. + */ +#ifndef NGTCP2_GAPTR_H +#define NGTCP2_GAPTR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_range.h" + +/* + * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct ngtcp2_gaptr { +  /* gap maintains the range of offset which is not pushed +     yet. Initially, its range is [0, UINT64_MAX).  "gap" is the range +     that is not pushed yet. */ +  ngtcp2_ksl gap; +  /* mem is custom memory allocator */ +  const ngtcp2_mem *mem; +} ngtcp2_gaptr; + +/* + * ngtcp2_gaptr_init initializes |gaptr|. + */ +void 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 pushes the range [offset, offset + datalen). + * + * 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, uint64_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(const ngtcp2_gaptr *gaptr); + +/* + * ngtcp2_gaptr_get_first_gap_after returns the first gap which + * includes or comes after |offset|. + */ +ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const 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(const ngtcp2_gaptr *gaptr, uint64_t offset, +                           uint64_t datalen); + +/* + * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if + * the range is pushed.  This function assumes that at least one gap + * exists. + */ +void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr); + +#endif /* !defined(NGTCP2_GAPTR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_idtr.c b/contrib/libs/ngtcp2/lib/ngtcp2_idtr.c new file mode 100644 index 00000000000..2cf9d3cbfd1 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_idtr.c @@ -0,0 +1,66 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_idtr.h" + +#include <assert.h> + +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, const ngtcp2_mem *mem) { +  ngtcp2_gaptr_init(&idtr->gap, mem); +} + +void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { +  if (idtr == NULL) { +    return; +  } + +  ngtcp2_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to an internal ID. + */ +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; + +  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(const ngtcp2_idtr *idtr, int64_t stream_id) { +  uint64_t q; + +  q = id_from_stream_id(stream_id); + +  return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_idtr.h b/contrib/libs/ngtcp2/lib/ngtcp2_idtr.h new file mode 100644 index 00000000000..0671f5ed91a --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_idtr.h @@ -0,0 +1,76 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_IDTR_H +#define NGTCP2_IDTR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_gaptr.h" + +/* + * ngtcp2_idtr tracks the usage of stream ID. + */ +typedef struct ngtcp2_idtr { +  /* gap maintains the range of an internal ID which is not used yet. +     Initially, its range is [0, UINT64_MAX).  The internal ID and +     stream ID are in the different number spaces.  See +     id_from_stream_id to convert a stream ID to an internal ID. */ +  ngtcp2_gaptr gap; +} ngtcp2_idtr; + +/* + * ngtcp2_idtr_init initializes |idtr|. + */ +void ngtcp2_idtr_init(ngtcp2_idtr *idtr, 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 use. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_STREAM_IN_USE + *     |stream_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 returns nonzero if |stream_id| is in use. + */ +int ngtcp2_idtr_is_open(const ngtcp2_idtr *idtr, int64_t stream_id); + +#endif /* !defined(NGTCP2_IDTR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_ksl.c b/contrib/libs/ngtcp2/lib/ngtcp2_ksl.c new file mode 100644 index 00000000000..2280b462cbd --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ksl.c @@ -0,0 +1,827 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ksl.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" + +static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; + +ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent); + +static size_t ksl_nodelen(size_t keylen) { +  assert(keylen >= sizeof(uint64_t)); + +  return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0x7u) & +         ~(uintptr_t)0x7u; +} + +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); +} + +void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, +                     const ngtcp2_mem *mem) { +  size_t nodelen = ksl_nodelen(keylen); + +  ngtcp2_objalloc_init(&ksl->blkalloc, +                       (ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu, mem); + +  ksl->head = NULL; +  ksl->front = ksl->back = NULL; +  ksl->compar = compar; +  ksl->n = 0; +  ksl->keylen = keylen; +  ksl->nodelen = nodelen; +} + +static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) { +  return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc, +                                         ksl_blklen(ksl->nodelen)); +} + +static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { +  ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk); +} + +static int ksl_head_init(ngtcp2_ksl *ksl) { +  ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl); + +  if (!head) { +    return NGTCP2_ERR_NOMEM; +  } + +  head->next = head->prev = NULL; +  head->n = 0; +  head->leaf = 1; + +  ksl->head = head; +  ksl->front = ksl->back = head; + +  return 0; +} + +#ifdef NOMEMPOOL +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { +  size_t i; + +  if (!blk->leaf) { +    for (i = 0; i < blk->n; ++i) { +      ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk); +    } +  } + +  ksl_blk_objalloc_del(ksl, blk); +} +#endif /* defined(NOMEMPOOL) */ + +void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { +  if (!ksl || !ksl->head) { +    return; +  } + +#ifdef NOMEMPOOL +  ksl_free_blk(ksl, ksl->head); +#endif /* defined(NOMEMPOOL) */ + +  ngtcp2_objalloc_free(&ksl->blkalloc); +} + +/* + * 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 = ksl_blk_objalloc_new(ksl); +  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; +  blk->n -= rblk->n; + +  memcpy(rblk->nodes, blk->nodes + ksl->nodelen * blk->n, +         ksl->nodelen * rblk->n); + +  assert(blk->n >= NGTCP2_KSL_MIN_NBLK); +  assert(rblk->n >= NGTCP2_KSL_MIN_NBLK); + +  return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes.  The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { +  ngtcp2_ksl_node *node; +  ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk; + +  rblk = ksl_split_blk(ksl, lblk); +  if (rblk == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  memmove(blk->nodes + (i + 2) * ksl->nodelen, +          blk->nodes + (i + 1) * ksl->nodelen, +          ksl->nodelen * (blk->n - (i + 1))); + +  node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); +  node->blk = rblk; +  ++blk->n; +  ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + +  node = ngtcp2_ksl_nth_node(ksl, blk, i); +  ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + +  return 0; +} + +/* + * ksl_split_head splits a head (root) block.  It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +static int ksl_split_head(ngtcp2_ksl *ksl) { +  ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; +  ngtcp2_ksl_node *node; + +  rblk = ksl_split_blk(ksl, ksl->head); +  if (rblk == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  lblk = ksl->head; + +  nhead = ksl_blk_objalloc_new(ksl); + +  if (nhead == NULL) { +    ksl_blk_objalloc_del(ksl, rblk); +    return NGTCP2_ERR_NOMEM; +  } + +  nhead->next = nhead->prev = NULL; +  nhead->n = 2; +  nhead->leaf = 0; + +  node = ngtcp2_ksl_nth_node(ksl, nhead, 0); +  ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +  node->blk = lblk; + +  node = ngtcp2_ksl_nth_node(ksl, nhead, 1); +  ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); +  node->blk = rblk; + +  ksl->head = nhead; + +  return 0; +} + +/* + * ksl_insert_node inserts a node whose key is |key| with the + * associated |data| at the index of |i|.  This function assumes that + * the number of nodes contained by |blk| is strictly less than + * NGTCP2_KSL_MAX_NBLK. + */ +static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, +                            const ngtcp2_ksl_key *key, void *data) { +  ngtcp2_ksl_node *node; + +  assert(blk->n < NGTCP2_KSL_MAX_NBLK); + +  memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, +          ksl->nodelen * (blk->n - i)); + +  node = ngtcp2_ksl_nth_node(ksl, blk, i); +  ksl_node_set_key(ksl, node, key); +  node->data = data; + +  ++blk->n; +} + +static size_t ksl_search(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, +                         const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { +  size_t i; +  ngtcp2_ksl_node *node; + +  for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes; +       i < blk->n && compar((ngtcp2_ksl_key *)node->key, key); +       ++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) +    ; + +  return i; +} + +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                      const ngtcp2_ksl_key *key, void *data) { +  ngtcp2_ksl_blk *blk; +  ngtcp2_ksl_node *node; +  size_t i; +  int rv; + +  if (!ksl->head) { +    rv = ksl_head_init(ksl); +    if (rv != 0) { +      return rv; +    } +  } + +  if (ksl->head->n == NGTCP2_KSL_MAX_NBLK) { +    rv = ksl_split_head(ksl); +    if (rv != 0) { +      return rv; +    } +  } + +  blk = ksl->head; + +  for (;;) { +    i = ksl_search(ksl, blk, key, ksl->compar); + +    if (blk->leaf) { +      if (i < blk->n && +          !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { +        if (it) { +          *it = ngtcp2_ksl_end(ksl); +        } + +        return NGTCP2_ERR_INVALID_ARGUMENT; +      } + +      ksl_insert_node(ksl, blk, i, key, data); +      ++ksl->n; + +      if (it) { +        ngtcp2_ksl_it_init(it, ksl, blk, i); +      } + +      return 0; +    } + +    if (i == blk->n) { +      /* This insertion extends the largest key in this subtree. */ +      for (; !blk->leaf;) { +        node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); +        if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { +          rv = ksl_split_node(ksl, blk, blk->n - 1); +          if (rv != 0) { +            return rv; +          } + +          node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); +        } + +        ksl_node_set_key(ksl, node, key); +        blk = node->blk; +      } + +      ksl_insert_node(ksl, blk, blk->n, key, data); +      ++ksl->n; + +      if (it) { +        ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1); +      } + +      return 0; +    } + +    node = ngtcp2_ksl_nth_node(ksl, blk, i); + +    if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { +      rv = ksl_split_node(ksl, blk, i); +      if (rv != 0) { +        return rv; +      } + +      if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { +        node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); + +        if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { +          ksl_node_set_key(ksl, node, key); +        } +      } +    } + +    blk = node->blk; +  } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { +  memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, +          ksl->nodelen * (blk->n - (i + 1))); + +  --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the head (root) block and it contains just 2 nodes + * before merging 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_node *lnode; +  ngtcp2_ksl_blk *lblk, *rblk; + +  assert(i + 1 < blk->n); + +  lnode = ngtcp2_ksl_nth_node(ksl, blk, i); + +  lblk = lnode->blk; +  rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk; + +  assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK); + +  memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, +         ksl->nodelen * rblk->n); + +  lblk->n += rblk->n; +  lblk->next = rblk->next; + +  if (lblk->next) { +    lblk->next->prev = lblk; +  } else if (ksl->back == rblk) { +    ksl->back = lblk; +  } + +  ksl_blk_objalloc_del(ksl, rblk); + +  if (ksl->head == blk && blk->n == 2) { +    ksl_blk_objalloc_del(ksl, ksl->head); +    ksl->head = lblk; +  } else { +    ksl_remove_node(ksl, blk, i + 1); +    ksl_node_set_key(ksl, lnode, +                     ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +  } + +  return lblk; +} + +/* + * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i - 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { +  ngtcp2_ksl_node *lnode, *rnode; +  ngtcp2_ksl_blk *lblk, *rblk; +  size_t n; + +  assert(i > 0); + +  lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1); +  rnode = ngtcp2_ksl_nth_node(ksl, blk, i); + +  lblk = lnode->blk; +  rblk = rnode->blk; + +  assert(lblk->n < NGTCP2_KSL_MAX_NBLK); +  assert(rblk->n > NGTCP2_KSL_MIN_NBLK); + +  n = (lblk->n + rblk->n + 1) / 2 - lblk->n; + +  assert(n > 0); +  assert(lblk->n <= NGTCP2_KSL_MAX_NBLK - n); +  assert(rblk->n >= NGTCP2_KSL_MIN_NBLK + n); + +  memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, ksl->nodelen * n); + +  lblk->n += (uint32_t)n; +  rblk->n -= (uint32_t)n; + +  ksl_node_set_key(ksl, lnode, +                   ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + +  memmove(rblk->nodes, rblk->nodes + ksl->nodelen * n, ksl->nodelen * rblk->n); +} + +/* + * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i + 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { +  ngtcp2_ksl_node *lnode, *rnode; +  ngtcp2_ksl_blk *lblk, *rblk; +  size_t n; + +  assert(i < blk->n - 1); + +  lnode = ngtcp2_ksl_nth_node(ksl, blk, i); +  rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1); + +  lblk = lnode->blk; +  rblk = rnode->blk; + +  assert(lblk->n > NGTCP2_KSL_MIN_NBLK); +  assert(rblk->n < NGTCP2_KSL_MAX_NBLK); + +  n = (lblk->n + rblk->n + 1) / 2 - rblk->n; + +  assert(n > 0); +  assert(lblk->n >= NGTCP2_KSL_MIN_NBLK + n); +  assert(rblk->n <= NGTCP2_KSL_MAX_NBLK - n); + +  memmove(rblk->nodes + ksl->nodelen * n, rblk->nodes, ksl->nodelen * rblk->n); + +  rblk->n += (uint32_t)n; +  lblk->n -= (uint32_t)n; + +  memcpy(rblk->nodes, lblk->nodes + ksl->nodelen * lblk->n, ksl->nodelen * n); + +  ksl_node_set_key(ksl, lnode, +                   ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, +                     const ngtcp2_ksl_key *rhs) { +  return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                           const ngtcp2_ksl_it *hint, +                           const ngtcp2_ksl_key *key) { +  ngtcp2_ksl_blk *blk = hint->blk; + +  assert(ksl->head); + +  if (blk->n <= NGTCP2_KSL_MIN_NBLK) { +    return ngtcp2_ksl_remove(ksl, it, key); +  } + +  ksl_remove_node(ksl, blk, hint->i); + +  --ksl->n; + +  if (it) { +    if (hint->i == blk->n && blk->next) { +      ngtcp2_ksl_it_init(it, ksl, blk->next, 0); +    } else { +      ngtcp2_ksl_it_init(it, ksl, blk, hint->i); +    } +  } + +  return 0; +} + +int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                      const ngtcp2_ksl_key *key) { +  ngtcp2_ksl_blk *blk = ksl->head; +  ngtcp2_ksl_node *node; +  size_t i; + +  if (!blk) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (!blk->leaf && blk->n == 2 && +      ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && +      ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { +    blk = ksl_merge_node(ksl, blk, 0); +  } + +  for (;;) { +    i = ksl_search(ksl, blk, key, ksl->compar); + +    if (i == blk->n) { +      if (it) { +        *it = ngtcp2_ksl_end(ksl); +      } + +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    if (blk->leaf) { +      if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { +        if (it) { +          *it = ngtcp2_ksl_end(ksl); +        } + +        return NGTCP2_ERR_INVALID_ARGUMENT; +      } + +      ksl_remove_node(ksl, blk, i); +      --ksl->n; + +      if (it) { +        if (blk->n == i && blk->next) { +          ngtcp2_ksl_it_init(it, ksl, blk->next, 0); +        } else { +          ngtcp2_ksl_it_init(it, ksl, blk, i); +        } +      } + +      return 0; +    } + +    node = ngtcp2_ksl_nth_node(ksl, blk, i); + +    if (node->blk->n > NGTCP2_KSL_MIN_NBLK) { +      blk = node->blk; +      continue; +    } + +    assert(node->blk->n == NGTCP2_KSL_MIN_NBLK); + +    if (i + 1 < blk->n && +        ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { +      ksl_shift_left(ksl, blk, i + 1); +      blk = node->blk; + +      continue; +    } + +    if (i > 0 && +        ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { +      ksl_shift_right(ksl, blk, i - 1); +      blk = node->blk; + +      continue; +    } + +    if (i + 1 < blk->n) { +      blk = ksl_merge_node(ksl, blk, i); +      continue; +    } + +    assert(i > 0); + +    blk = ksl_merge_node(ksl, blk, i - 1); +  } +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound(const ngtcp2_ksl *ksl, +                                     const ngtcp2_ksl_key *key) { +  return ngtcp2_ksl_lower_bound_compar(ksl, key, ksl->compar); +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(const 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; + +  if (!blk) { +    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); +    return it; +  } + +  for (;;) { +    i = ksl_search(ksl, blk, key, compar); + +    if (blk->leaf) { +      if (i == blk->n && blk->next) { +        blk = blk->next; +        i = 0; +      } + +      ngtcp2_ksl_it_init(&it, ksl, blk, i); + +      return it; +    } + +    if (i == blk->n) { +      /* This happens if descendant has smaller key.  Fast forward to +         find last node in this subtree. */ +      for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) +        ; + +      if (blk->next) { +        blk = blk->next; +        i = 0; +      } else { +        i = blk->n; +      } + +      ngtcp2_ksl_it_init(&it, ksl, blk, i); + +      return it; +    } + +    blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; +  } +} + +void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, +                           const ngtcp2_ksl_key *new_key) { +  ngtcp2_ksl_blk *blk = ksl->head; +  ngtcp2_ksl_node *node; +  size_t i; + +  assert(ksl->head); + +  for (;;) { +    i = ksl_search(ksl, blk, old_key, ksl->compar); + +    assert(i < blk->n); +    node = ngtcp2_ksl_nth_node(ksl, blk, i); + +    if (blk->leaf) { +      assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key)); +      ksl_node_set_key(ksl, node, new_key); + +      return; +    } + +    if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) || +        ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) { +      ksl_node_set_key(ksl, node, new_key); +    } + +    blk = node->blk; +  } +} + +size_t ngtcp2_ksl_len(const ngtcp2_ksl *ksl) { return ksl->n; } + +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { +  if (!ksl->head) { +    return; +  } + +#ifdef NOMEMPOOL +  ksl_free_blk(ksl, ksl->head); +#endif /* defined(NOMEMPOOL) */ + +  ksl->front = ksl->back = ksl->head = NULL; +  ksl->n = 0; + +  ngtcp2_objalloc_clear(&ksl->blkalloc); +} + +#ifndef WIN32 +static void ksl_print(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, +                      size_t level) { +  size_t i; +  ngtcp2_ksl_node *node; + +  fprintf(stderr, "LV=%zu n=%u\n", level, blk->n); + +  if (blk->leaf) { +    for (i = 0; i < blk->n; ++i) { +      node = ngtcp2_ksl_nth_node(ksl, blk, i); +      fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); +    } + +    fprintf(stderr, "\n"); + +    return; +  } + +  for (i = 0; i < blk->n; ++i) { +    ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1); +  } +} + +void ngtcp2_ksl_print(const ngtcp2_ksl *ksl) { +  if (!ksl->head) { +    return; +  } + +  ksl_print(ksl, ksl->head, 0); +} +#endif /* !defined(WIN32) */ + +ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { +  ngtcp2_ksl_it it; + +  if (ksl->head) { +    ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); +  } else { +    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); +  } + +  return it; +} + +ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { +  ngtcp2_ksl_it it; + +  if (ksl->head) { +    ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); +  } else { +    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0); +  } + +  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_prev(ngtcp2_ksl_it *it) { +  assert(!ngtcp2_ksl_it_begin(it)); + +  if (it->i == 0) { +    it->blk = it->blk->prev; +    it->i = it->blk->n - 1; +  } else { +    --it->i; +  } +} + +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) { +  return it->i == 0 && it->blk->prev == NULL; +} + +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, +                            const ngtcp2_ksl_key *rhs) { +  const ngtcp2_range *a = lhs, *b = rhs; +  return a->begin < b->begin; +} + +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, +                                      const ngtcp2_ksl_key *rhs) { +  const ngtcp2_range *a = lhs, *b = rhs; +  return a->begin < b->begin && !(ngtcp2_max_uint64(a->begin, b->begin) < +                                  ngtcp2_min_uint64(a->end, b->end)); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_ksl.h b/contrib/libs/ngtcp2/lib/ngtcp2_ksl.h new file mode 100644 index 00000000000..d8374b9b5bb --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ksl.h @@ -0,0 +1,349 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_KSL_H +#define NGTCP2_KSL_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <stdlib.h> + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_objalloc.h" + +#define NGTCP2_KSL_DEGR 16 +/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single +   block can contain. */ +#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1) +/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single +   block other than root must contain. */ +#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1) + +/* + * ngtcp2_ksl_key represents key in ngtcp2_ksl. + */ +typedef void ngtcp2_ksl_key; + +typedef struct ngtcp2_ksl_node ngtcp2_ksl_node; + +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 { +  union { +    struct { +      /* 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. */ +      uint32_t n; +      /* leaf is nonzero if this block contains leaf nodes. */ +      uint32_t 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_opl_entry oplent; +  }; +}; + +ngtcp2_objalloc_decl(ksl_blk, ngtcp2_ksl_blk, oplent); + +/* + * 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); + +typedef struct ngtcp2_ksl ngtcp2_ksl; + +typedef struct ngtcp2_ksl_it ngtcp2_ksl_it; + +/* + * ngtcp2_ksl_it is a bidirectional 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 { +  ngtcp2_objalloc blkalloc; +  /* 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; +  /* n is the number of elements stored. */ +  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; +}; + +/* + * ngtcp2_ksl_init initializes |ksl|.  |compar| specifies compare + * function.  |keylen| is the length of key and must be at least + * sizeof(uint64_t). + */ +void 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| 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. + * NGTCP2_ERR_INVALID_ARGUMENT + *     |key| already exists. + */ +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                      const ngtcp2_ksl_key *key, void *data); + +/* + * ngtcp2_ksl_remove removes the |key| from |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL.  If |key| is not found, no deletion takes place and + * the return value of ngtcp2_ksl_end(ksl) is assigned to |*it| if + * |it| is not NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + *     |key| does not exist. + */ +int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                      const ngtcp2_ksl_key *key); + +/* + * ngtcp2_ksl_remove_hint removes the |key| from |ksl|.  |hint| must + * point to the same node denoted by |key|.  |hint| is used to remove + * a node efficiently in some cases.  Other than that, it behaves + * exactly like ngtcp2_ksl_remove.  |it| and |hint| can point to the + * same object. + */ +int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, +                           const ngtcp2_ksl_it *hint, +                           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(const 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(const 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 both ngtcp2_ksl_it_begin(it) != 0 and + * 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 and + * ngtcp2_ksl_it_end(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(const ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_clear removes all elements stored in |ksl|. + */ +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. + */ +#define ngtcp2_ksl_nth_node(KSL, BLK, N)                                       \ +  ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) + +#ifndef WIN32 +/* + * 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(const ngtcp2_ksl *ksl); +#endif /* !defined(WIN32) */ + +/* + * 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. + */ +#define ngtcp2_ksl_it_get(IT)                                                  \ +  ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data + +/* + * ngtcp2_ksl_it_next advances the iterator by one.  It is undefined + * if this function is called when ngtcp2_ksl_it_end(it) returns + * nonzero. + */ +#define ngtcp2_ksl_it_next(IT)                                                 \ +  (++(IT)->i == (IT)->blk->n && (IT)->blk->next                                \ +     ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0)                              \ +     : 0) + +/* + * 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 one beyond + * the last node. + */ +#define ngtcp2_ksl_it_end(IT)                                                  \ +  ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) + +/* + * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first + * node.  |it| might satisfy both ngtcp2_ksl_it_begin(it) != 0 and + * ngtcp2_ksl_it_end(it) != 0 if the skip list has no node. + */ +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when ngtcp2_ksl_it_end(it) + * returns nonzero. + */ +#define ngtcp2_ksl_it_key(IT)                                                  \ +  ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) + +/* + * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. + * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the + * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin + * < (const ngtcp2_range *)(rhs->ptr)->begin. + */ +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, +                            const ngtcp2_ksl_key *rhs); + +/* + * ngtcp2_ksl_range_exclusive_compar is an implementation of + * ngtcp2_ksl_compar.  lhs->ptr and rhs->ptr must point to + * ngtcp2_range object and the function returns nonzero if (const + * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, +                                      const ngtcp2_ksl_key *rhs); + +#endif /* !defined(NGTCP2_KSL_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_log.c b/contrib/libs/ngtcp2/lib/ngtcp2_log.c new file mode 100644 index 00000000000..fc4eb443517 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_log.c @@ -0,0 +1,834 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_log.h" + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#  include <unistd.h> +#endif /* defined(HAVE_UNISTD_H) */ +#include <assert.h> +#include <string.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_unreachable.h" +#include "ngtcp2_net.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->events = 0xff; +  log->ts = log->last_ts = ts; +  log->user_data = user_data; +} + +/* + * # Log header + * + * <LEVEL><TIMESTAMP> <SCID> <EVENT> + * + * <LEVEL>: + *   Log level.  I=Info, W=Warning, E=Error + * + * <TIMESTAMP>: + *   Timestamp relative to ngtcp2_log.ts field in milliseconds + *   resolution. + * + * <SCID>: + *   Source Connection ID in hex string. + * + * <EVENT>: + *   Event.  See ngtcp2_log_event. + * + * # Frame event + * + * <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>) + * + * <DIR>: + *   Flow direction.  tx=transmission, rx=reception + * + * <PKN>: + *   Packet number. + * + * <PKTNAME>: + *   Packet name.  (e.g., Initial, Handshake, 1RTT) + * + * <FRAMENAME>: + *   Frame name.  (e.g., STREAM, ACK, PING) + * + * <FRAMETYPE>: + *   Frame type in hex string. + */ + +#define NGTCP2_LOG_BUFLEN 4096 + +/* TODO Split second and remaining fraction with comma */ +#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s" +#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) + +#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) + +#define NGTCP2_LOG_TP_HD_FIELDS                                                \ +  timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" + +static const char *strerrorcode(uint64_t error_code) { +  switch (error_code) { +  case NGTCP2_NO_ERROR: +    return "NO_ERROR"; +  case NGTCP2_INTERNAL_ERROR: +    return "INTERNAL_ERROR"; +  case NGTCP2_CONNECTION_REFUSED: +    return "CONNECTION_REFUSED"; +  case NGTCP2_FLOW_CONTROL_ERROR: +    return "FLOW_CONTROL_ERROR"; +  case NGTCP2_STREAM_LIMIT_ERROR: +    return "STREAM_LIMIT_ERROR"; +  case NGTCP2_STREAM_STATE_ERROR: +    return "STREAM_STATE_ERROR"; +  case NGTCP2_FINAL_SIZE_ERROR: +    return "FINAL_SIZE_ERROR"; +  case NGTCP2_FRAME_ENCODING_ERROR: +    return "FRAME_ENCODING_ERROR"; +  case NGTCP2_TRANSPORT_PARAMETER_ERROR: +    return "TRANSPORT_PARAMETER_ERROR"; +  case NGTCP2_CONNECTION_ID_LIMIT_ERROR: +    return "CONNECTION_ID_LIMIT_ERROR"; +  case NGTCP2_PROTOCOL_VIOLATION: +    return "PROTOCOL_VIOLATION"; +  case NGTCP2_INVALID_TOKEN: +    return "INVALID_TOKEN"; +  case NGTCP2_APPLICATION_ERROR: +    return "APPLICATION_ERROR"; +  case NGTCP2_CRYPTO_BUFFER_EXCEEDED: +    return "CRYPTO_BUFFER_EXCEEDED"; +  case NGTCP2_KEY_UPDATE_ERROR: +    return "KEY_UPDATE_ERROR"; +  case NGTCP2_VERSION_NEGOTIATION_ERROR: +    return "VERSION_NEGOTIATION_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_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); +  } + +  switch (hd->type) { +  case NGTCP2_PKT_VERSION_NEGOTIATION: +    return "VN"; +  case NGTCP2_PKT_STATELESS_RESET: +    return "SR"; +  case NGTCP2_PKT_1RTT: +    return "1RTT"; +  default: +    return "(unknown)"; +  } +} + +static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) { +  ngtcp2_pkt_hd hd = {0}; + +  hd.type = type; +  hd.flags = flags; + +  return strpkttype(&hd); +} + +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_LDC: +    return "ldc"; +  case NGTCP2_LOG_EVENT_CRY: +    return "cry"; +  case NGTCP2_LOG_EVENT_PTV: +    return "ptv"; +  case NGTCP2_LOG_EVENT_CCA: +    return "cca"; +  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%02" PRIx64 ") 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%02" PRIx64 ") largest_ack=%" PRId64 +                    " ack_delay=%" PRIu64 "(%" PRIu64 ") ack_range_count=%zu"), +    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack, +    fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay, fr->rangecnt); + +  largest_ack = fr->largest_ack; +  min_ack = fr->largest_ack - (int64_t)fr->first_ack_range; + +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64 +                                  "..%" PRId64 "] len=%" PRIu64), +                  NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack, +                  fr->first_ack_range); + +  for (i = 0; i < fr->rangecnt; ++i) { +    const ngtcp2_ack_range *range = &fr->ranges[i]; +    largest_ack = min_ack - (int64_t)range->gap - 2; +    min_ack = largest_ack - (int64_t)range->len; +    log->log_printf(log->user_data, +                    (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") range=[%" PRId64 +                                    "..%" PRId64 "] gap=%" PRIu64 +                                    " len=%" PRIu64), +                    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, +                    min_ack, range->gap, range->len); +  } + +  if (fr->type == NGTCP2_FRAME_ACK_ECN) { +    log->log_printf(log->user_data, +                    (NGTCP2_LOG_PKT " ACK(0x%02" PRIx64 ") ect0=%" PRIu64 +                                    " ect1=%" PRIu64 " ce=%" PRIu64), +                    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0, +                    fr->ecn.ect1, fr->ecn.ce); +  } +} + +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%02" PRIx64 ") len=%zu"), +                  NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len); +} + +static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                                const ngtcp2_reset_stream *fr, +                                const char *dir) { +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_PKT " RESET_STREAM(0x%02" PRIx64 ") 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_size(sizeof(reason) - 1, fr->reasonlen); + +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_PKT " CONNECTION_CLOSE(0x%02" PRIx64 +                    ") error_code=%s(0x%" PRIx64 ") " +                    "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"), +    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, +    fr->type == NGTCP2_FRAME_CONNECTION_CLOSE ? strerrorcode(fr->error_code) +                                              : strapperrorcode(fr->error_code), +    fr->error_code, fr->frame_type, fr->reasonlen, +    ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen)); +} + +static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                            const ngtcp2_max_data *fr, const char *dir) { +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_PKT " MAX_DATA(0x%02" PRIx64 ") 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%02" PRIx64 +                                  ") 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%02" PRIx64 ") 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%02" PRIx64 ")"), +                  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%02" PRIx64 ") 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%02" PRIx64 +                                  ") id=0x%" PRIx64 " offset=%" PRIu64), +                  NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, +                  fr->offset); +} + +static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                                   const ngtcp2_streams_blocked *fr, +                                   const char *dir) { +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02" PRIx64 ") max_streams=%" PRIu64), +    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams); +} + +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%02" PRIx64 ") 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%02" PRIx64 ") 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%02" PRIx64 ") 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%02" PRIx64 ") 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_stream *fr, const char *dir) { +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_PKT " CRYPTO(0x%02" PRIx64 ") offset=%" PRIu64 " len=%" PRIu64), +    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, +    ngtcp2_vec_len(fr->data, fr->datacnt)); +} + +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%02" PRIx64 ") token=0x%s 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%02" PRIx64 ") 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%02" PRIx64 ")"), +                  NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + +static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                            const ngtcp2_datagram *fr, const char *dir) { +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_PKT " DATAGRAM(0x%02" PRIx64 ") len=%" PRIu64), +                  NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, +                  ngtcp2_vec_len(fr->data, fr->datacnt)); +} + +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->stream, 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; +  case NGTCP2_FRAME_DATAGRAM: +  case NGTCP2_FRAME_DATAGRAM_LEN: +    log_fr_datagram(log, hd, &fr->datagram, dir); +    break; +  default: +    ngtcp2_unreachable(); +  } +} + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                      const ngtcp2_frame *fr) { +  if (!log->log_printf || !(log->events & NGTCP2_LOG_EVENT_FRM)) { +    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 || !(log->events & NGTCP2_LOG_EVENT_FRM)) { +    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 || !(log->events & NGTCP2_LOG_EVENT_PKT)) { +    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 || !(log->events & NGTCP2_LOG_EVENT_PKT)) { +    return; +  } + +  memset(&shd, 0, sizeof(shd)); + +  shd.type = NGTCP2_PKT_STATELESS_RESET; + +  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, +                          const ngtcp2_transport_params *params) { +  uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; +  uint8_t addr[16 * 2 + 7 + 1]; +  uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; +  size_t i; +  const ngtcp2_sockaddr_in *sa_in; +  const ngtcp2_sockaddr_in6 *sa_in6; +  const uint8_t *p; +  uint32_t version; + +  if (!log->log_printf || !(log->events & NGTCP2_LOG_EVENT_CRY)) { +    return; +  } + +  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_addr_present) { +    if (params->preferred_addr.ipv4_present) { +      sa_in = ¶ms->preferred_addr.ipv4; + +      log->log_printf(log->user_data, +                      (NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s"), +                      NGTCP2_LOG_TP_HD_FIELDS, +                      (const char *)ngtcp2_encode_ipv4( +                        addr, (const uint8_t *)&sa_in->sin_addr)); +      log->log_printf(log->user_data, +                      (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"), +                      NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in->sin_port)); +    } + +    if (params->preferred_addr.ipv6_present) { +      sa_in6 = ¶ms->preferred_addr.ipv6; + +      log->log_printf(log->user_data, +                      (NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s"), +                      NGTCP2_LOG_TP_HD_FIELDS, +                      (const char *)ngtcp2_encode_ipv6( +                        addr, (const uint8_t *)&sa_in6->sin6_addr)); +      log->log_printf(log->user_data, +                      (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"), +                      NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in6->sin6_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_addr.cid.data, +                                      params->preferred_addr.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_addr.stateless_reset_token, +        sizeof(params->preferred_addr.stateless_reset_token))); +  } + +  if (params->original_dcid_present) { +    log->log_printf( +      log->user_data, +      (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"), +      NGTCP2_LOG_TP_HD_FIELDS, +      (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data, +                                      params->original_dcid.datalen)); +  } + +  if (params->retry_scid_present) { +    log->log_printf( +      log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"), +      NGTCP2_LOG_TP_HD_FIELDS, +      (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data, +                                      params->retry_scid.datalen)); +  } + +  if (params->initial_scid_present) { +    log->log_printf( +      log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"), +      NGTCP2_LOG_TP_HD_FIELDS, +      (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data, +                                      params->initial_scid.datalen)); +  } + +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64), +    NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local); +  log->log_printf( +    log->user_data, +    (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64), +    NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni); +  log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); +  log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, +                  params->max_idle_timeout / NGTCP2_MILLISECONDS); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent); +  log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, +                  params->max_ack_delay / NGTCP2_MILLISECONDS); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " disable_active_migration=%d"), +                  NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration); +  log->log_printf(log->user_data, +                  (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64), +                  NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size); +  log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"), +                  NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit); + +  if (params->version_info_present) { +    log->log_printf( +      log->user_data, +      (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"), +      NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version); + +    assert(!(params->version_info.available_versionslen & 0x3)); + +    for (i = 0, p = params->version_info.available_versions; +         i < params->version_info.available_versionslen; +         i += sizeof(uint32_t)) { +      p = ngtcp2_get_uint32be(&version, p); + +      log->log_printf( +        log->user_data, +        (NGTCP2_LOG_TP " version_information.available_versions[%zu]=0x%08x"), +        NGTCP2_LOG_TP_HD_FIELDS, i >> 2, version); +    } +  } +} + +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 || !(log->events & NGTCP2_LOG_EVENT_LDC)) { +    return; +  } + +  ngtcp2_log_info(log, NGTCP2_LOG_EVENT_LDC, +                  "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num, +                  strpkttype_type_flags(type, flags), 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 || !(log->events & NGTCP2_LOG_EVENT_PKT)) { +    return; +  } + +  if (hd->type == NGTCP2_PKT_1RTT) { +    ngtcp2_log_info( +      log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d", +      dir, hd->pkt_num, +      (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), +      strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); +  } else { +    ngtcp2_log_info( +      log, NGTCP2_LOG_EVENT_PKT, +      "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu", +      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->version, strpkttype(hd), hd->len); +  } +} + +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 || !(log->events & ev)) { +    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", hd->pkt_num, +                  strpkttype(hd)); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_log.h b/contrib/libs/ngtcp2/lib/ngtcp2_log.h new file mode 100644 index 00000000000..13fb81a72e1 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_log.h @@ -0,0 +1,132 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_LOG_H +#define NGTCP2_LOG_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" + +typedef struct ngtcp2_log { +  /* log_printf is a sink to write log.  NULL means no logging +     output. */ +  ngtcp2_printf log_printf; +  /* events is an event filter.  Only events set in this field are +     emitted. */ +  uint8_t events; +  /* 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]; +} ngtcp2_log; + +/** + * @enum + * + * :type:`ngtcp2_log_event` defines an event of ngtcp2 library + * internal logger. + */ +typedef enum ngtcp2_log_event { +  /** +   * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. +   */ +  NGTCP2_LOG_EVENT_NONE, +  /** +   * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event +   */ +  NGTCP2_LOG_EVENT_CON = 0x1, +  /** +   * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. +   */ +  NGTCP2_LOG_EVENT_PKT = 0x2, +  /** +   * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. +   */ +  NGTCP2_LOG_EVENT_FRM = 0x4, +  /** +   * :enum:`NGTCP2_LOG_EVENT_LDC` is a loss detection and congestion +   * control event. +   */ +  NGTCP2_LOG_EVENT_LDC = 0x8, +  /** +   * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. +   */ +  NGTCP2_LOG_EVENT_CRY = 0x10, +  /** +   * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. +   */ +  NGTCP2_LOG_EVENT_PTV = 0x20, +  /** +   * :enum:`NGTCP2_LOG_EVENT_CCA` is a congestion controller algorithm +   * event. +   */ +  NGTCP2_LOG_EVENT_CCA = 0x40, +} ngtcp2_log_event; + +void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, +                     ngtcp2_printf log_printf, ngtcp2_tstamp ts, +                     void *user_data); + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                      const ngtcp2_frame *fr); +void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                      const ngtcp2_frame *fr); + +void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, +                      const uint32_t *sv, size_t nsv); + +void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr); + +void ngtcp2_log_remote_tp(ngtcp2_log *log, +                          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); + +/** + * @function + * + * `ngtcp2_log_info` writes info level log. + */ +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, +                     ...); + +#endif /* !defined(NGTCP2_LOG_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_macro.h b/contrib/libs/ngtcp2/lib/ngtcp2_macro.h new file mode 100644 index 00000000000..649ed8434a4 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_macro.h @@ -0,0 +1,81 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MACRO_H +#define NGTCP2_MACRO_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <stddef.h> + +#include <ngtcp2/ngtcp2.h> + +#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) + +/* + * ngtcp2_arraylen returns the number of elements in array |A|. + */ +#define ngtcp2_arraylen(A) (sizeof(A) / sizeof(A[0])) + +#define ngtcp2_max_def(SUFFIX, T)                                              \ +  static inline T ngtcp2_max_##SUFFIX(T a, T b) { return a < b ? b : a; } + +ngtcp2_max_def(int8, int8_t); +ngtcp2_max_def(int16, int16_t); +ngtcp2_max_def(int32, int32_t); +ngtcp2_max_def(int64, int64_t); +ngtcp2_max_def(uint8, uint8_t); +ngtcp2_max_def(uint16, uint16_t); +ngtcp2_max_def(uint32, uint32_t); +ngtcp2_max_def(uint64, uint64_t); +ngtcp2_max_def(size, size_t); + +#define ngtcp2_min_def(SUFFIX, T)                                              \ +  static inline T ngtcp2_min_##SUFFIX(T a, T b) { return a < b ? a : b; } + +ngtcp2_min_def(int8, int8_t); +ngtcp2_min_def(int16, int16_t); +ngtcp2_min_def(int32, int32_t); +ngtcp2_min_def(int64, int64_t); +ngtcp2_min_def(uint8, uint8_t); +ngtcp2_min_def(uint16, uint16_t); +ngtcp2_min_def(uint32, uint32_t); +ngtcp2_min_def(uint64, uint64_t); +ngtcp2_min_def(size, size_t); + +#endif /* !defined(NGTCP2_MACRO_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_map.c b/contrib/libs/ngtcp2/lib/ngtcp2_map.c new file mode 100644 index 00000000000..9eb102f16b3 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_map.c @@ -0,0 +1,302 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_map.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "ngtcp2_conv.h" + +#define NGTCP2_INITIAL_TABLE_LENBITS 4 + +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +  map->mem = mem; +  map->hashbits = 0; +  map->table = NULL; +  map->size = 0; +} + +void ngtcp2_map_free(ngtcp2_map *map) { +  if (!map) { +    return; +  } + +  ngtcp2_mem_free(map->mem, map->table); +} + +int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr), +                    void *ptr) { +  int rv; +  size_t i; +  ngtcp2_map_bucket *bkt; +  size_t tablelen; + +  if (map->size == 0) { +    return 0; +  } + +  tablelen = 1u << map->hashbits; + +  for (i = 0; i < tablelen; ++i) { +    bkt = &map->table[i]; + +    if (bkt->data == NULL) { +      continue; +    } + +    rv = func(bkt->data, ptr); +    if (rv != 0) { +      return rv; +    } +  } + +  return 0; +} + +static size_t hash(ngtcp2_map_key_type key, size_t bits) { +  return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +} + +static void map_bucket_swap(ngtcp2_map_bucket *a, ngtcp2_map_bucket *b) { +  ngtcp2_map_bucket c = *a; + +  *a = *b; +  *b = c; +} + +#ifndef WIN32 +void ngtcp2_map_print_distance(const ngtcp2_map *map) { +  size_t i; +  size_t idx; +  ngtcp2_map_bucket *bkt; +  size_t tablelen; + +  if (map->size == 0) { +    return; +  } + +  tablelen = 1u << map->hashbits; + +  for (i = 0; i < tablelen; ++i) { +    bkt = &map->table[i]; + +    if (bkt->data == NULL) { +      fprintf(stderr, "@%zu <EMPTY>\n", i); +      continue; +    } + +    idx = hash(bkt->key, map->hashbits); +    fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, +            hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); +  } +} +#endif /* !defined(WIN32) */ + +static int insert(ngtcp2_map_bucket *table, size_t hashbits, +                  ngtcp2_map_key_type key, void *data) { +  size_t idx = hash(key, hashbits); +  ngtcp2_map_bucket b = {0, key, data}, *bkt; +  size_t mask = (1u << hashbits) - 1; + +  for (;;) { +    bkt = &table[idx]; + +    if (bkt->data == NULL) { +      *bkt = b; +      return 0; +    } + +    if (b.psl > bkt->psl) { +      map_bucket_swap(bkt, &b); +    } else if (bkt->key == key) { +      /* TODO This check is just a waste after first swap or if this +         function is called from map_resize.  That said, there is no +         difference with or without this conditional in performance +         wise. */ +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    ++b.psl; +    idx = (idx + 1) & mask; +  } +} + +static int map_resize(ngtcp2_map *map, size_t new_hashbits) { +  size_t i; +  ngtcp2_map_bucket *new_table; +  ngtcp2_map_bucket *bkt; +  size_t tablelen; +  int rv; +  (void)rv; + +  new_table = +    ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(ngtcp2_map_bucket)); +  if (new_table == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  if (map->size) { +    tablelen = 1u << map->hashbits; + +    for (i = 0; i < tablelen; ++i) { +      bkt = &map->table[i]; +      if (bkt->data == NULL) { +        continue; +      } + +      rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + +      assert(0 == rv); +    } +  } + +  ngtcp2_mem_free(map->mem, map->table); +  map->hashbits = new_hashbits; +  map->table = new_table; + +  return 0; +} + +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { +  int rv; + +  assert(data); + +  /* Load factor is 0.75 */ +  /* Under the very initial condition, that is map->size == 0 and +     map->hashbits == 0, 4 > 3 still holds nicely. */ +  if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { +    if (map->hashbits) { +      rv = map_resize(map, map->hashbits + 1); +      if (rv != 0) { +        return rv; +      } +    } else { +      rv = map_resize(map, NGTCP2_INITIAL_TABLE_LENBITS); +      if (rv != 0) { +        return rv; +      } +    } +  } + +  rv = insert(map->table, map->hashbits, key, data); +  if (rv != 0) { +    return rv; +  } + +  ++map->size; + +  return 0; +} + +void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key) { +  size_t idx; +  ngtcp2_map_bucket *bkt; +  size_t psl = 0; +  size_t mask; + +  if (map->size == 0) { +    return NULL; +  } + +  idx = hash(key, map->hashbits); +  mask = (1u << map->hashbits) - 1; + +  for (;;) { +    bkt = &map->table[idx]; + +    if (bkt->data == NULL || psl > bkt->psl) { +      return NULL; +    } + +    if (bkt->key == key) { +      return bkt->data; +    } + +    ++psl; +    idx = (idx + 1) & mask; +  } +} + +int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { +  size_t idx; +  ngtcp2_map_bucket *b, *bkt; +  size_t psl = 0; +  size_t mask; + +  if (map->size == 0) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  idx = hash(key, map->hashbits); +  mask = (1u << map->hashbits) - 1; + +  for (;;) { +    bkt = &map->table[idx]; + +    if (bkt->data == NULL || psl > bkt->psl) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    if (bkt->key == key) { +      b = bkt; +      idx = (idx + 1) & mask; + +      for (;;) { +        bkt = &map->table[idx]; +        if (bkt->data == NULL || bkt->psl == 0) { +          b->data = NULL; +          break; +        } + +        --bkt->psl; +        *b = *bkt; +        b = bkt; + +        idx = (idx + 1) & mask; +      } + +      --map->size; + +      return 0; +    } + +    ++psl; +    idx = (idx + 1) & mask; +  } +} + +void ngtcp2_map_clear(ngtcp2_map *map) { +  if (map->size == 0) { +    return; +  } + +  memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits)); +  map->size = 0; +} + +size_t ngtcp2_map_size(const ngtcp2_map *map) { return map->size; } diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_map.h b/contrib/libs/ngtcp2/lib/ngtcp2_map.h new file mode 100644 index 00000000000..9d882fb2008 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_map.h @@ -0,0 +1,128 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MAP_H +#define NGTCP2_MAP_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* Implementation of unordered map */ + +typedef uint64_t ngtcp2_map_key_type; + +typedef struct ngtcp2_map_bucket { +  uint32_t psl; +  ngtcp2_map_key_type key; +  void *data; +} ngtcp2_map_bucket; + +typedef struct ngtcp2_map { +  ngtcp2_map_bucket *table; +  const ngtcp2_mem *mem; +  size_t size; +  size_t hashbits; +} ngtcp2_map; + +/* + * ngtcp2_map_init initializes the map |map|. + */ +void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); + +/* + * ngtcp2_map_free deallocates any resources allocated for |map|.  The + * stored entries are not freed by this function.  Use + * ngtcp2_map_each() to free each entry. + */ +void ngtcp2_map_free(ngtcp2_map *map); + +/* + * ngtcp2_map_insert inserts the new |data| with the |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_key_type key, void *data); + +/* + * ngtcp2_map_find returns the entry associated by the key |key|.  If + * there is no such entry, this function returns NULL. + */ +void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key); + +/* + * ngtcp2_map_remove 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, ngtcp2_map_key_type key); + +/* + * ngtcp2_map_clear removes all entries from |map|.  The removed entry + * is not freed by this function. + */ +void ngtcp2_map_clear(ngtcp2_map *map); + +/* + * ngtcp2_map_size returns the number of items stored in the map + * |map|. + */ +size_t ngtcp2_map_size(const ngtcp2_map *map); + +/* + * ngtcp2_map_each 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. + */ +int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr), +                    void *ptr); + +#ifndef WIN32 +void ngtcp2_map_print_distance(const ngtcp2_map *map); +#endif /* !defined(WIN32) */ + +#endif /* !defined(NGTCP2_MAP_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_mem.c b/contrib/libs/ngtcp2/lib/ngtcp2_mem.c new file mode 100644 index 00000000000..d30e1f986e9 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_mem.c @@ -0,0 +1,113 @@ +/* + * 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" + +#include <stdio.h> + +static void *default_malloc(size_t size, void *user_data) { +  (void)user_data; + +  return malloc(size); +} + +static void default_free(void *ptr, void *user_data) { +  (void)user_data; + +  free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *user_data) { +  (void)user_data; + +  return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *user_data) { +  (void)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; } + +#ifndef MEMDEBUG +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { +  return mem->malloc(size, mem->user_data); +} + +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { +  mem->free(ptr, mem->user_data); +} + +void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { +  return mem->calloc(nmemb, size, mem->user_data); +} + +void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { +  return mem->realloc(ptr, size, mem->user_data); +} +#else  /* defined(MEMDEBUG) */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, +                              const char *func, const char *file, size_t line) { +  void *nptr = mem->malloc(size, mem->user_data); + +  fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func, +          file, line); + +  return nptr; +} + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, +                           const char *file, size_t line) { +  fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line); + +  mem->free(ptr, mem->user_data); +} + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, +                              const char *func, const char *file, size_t line) { +  void *nptr = mem->calloc(nmemb, size, mem->user_data); + +  fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb, +          size, func, file, line); + +  return nptr; +} + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, +                               const char *func, const char *file, +                               size_t line) { +  void *nptr = mem->realloc(ptr, size, mem->user_data); + +  fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr, +          size, func, file, line); + +  return nptr; +} +#endif /* defined(MEMDEBUG) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_mem.h b/contrib/libs/ngtcp2/lib/ngtcp2_mem.h new file mode 100644 index 00000000000..9f818752125 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_mem.h @@ -0,0 +1,72 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MEM_H +#define NGTCP2_MEM_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* Convenient wrapper functions to call allocator function in +   |mem|. */ +#ifndef MEMDEBUG +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); + +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); + +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); +#else /* defined(MEMDEBUG) */ +void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size, +                              const char *func, const char *file, size_t line); + +#  define ngtcp2_mem_malloc(MEM, SIZE)                                         \ +    ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__) + +void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func, +                           const char *file, size_t line); + +#  define ngtcp2_mem_free(MEM, PTR)                                            \ +    ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__) + +void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size, +                              const char *func, const char *file, size_t line); + +#  define ngtcp2_mem_calloc(MEM, NMEMB, SIZE)                                  \ +    ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__,        \ +                            __LINE__) + +void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size, +                               const char *func, const char *file, size_t line); + +#  define ngtcp2_mem_realloc(MEM, PTR, SIZE)                                   \ +    ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__) +#endif /* defined(MEMDEBUG) */ + +#endif /* !defined(NGTCP2_MEM_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_net.h b/contrib/libs/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 00000000000..103a2fb2d80 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,141 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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 + +/* This header file is explicitly allowed to be shared with +   ngtcp2_crypto library. */ + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#ifdef HAVE_ARPA_INET_H +#  include <arpa/inet.h> +#endif /* defined(HAVE_ARPA_INET_H) */ + +#ifdef HAVE_NETINET_IN_H +#  include <netinet/in.h> +#endif /* defined(HAVE_NETINET_IN_H) */ + +#ifdef HAVE_BYTESWAP_H +#  include <byteswap.h> +#endif /* defined(HAVE_BYTESWAP_H) */ + +#ifdef HAVE_ENDIAN_H +#  include <endian.h> +#endif /* defined(HAVE_ENDIAN_H) */ + +#ifdef HAVE_SYS_ENDIAN_H +#  include <sys/endian.h> +#endif /* defined(HAVE_SYS_ENDIAN_H) */ + +#ifdef __APPLE__ +#  include <libkern/OSByteOrder.h> +#endif /* defined(__APPLE__) */ + +#include <ngtcp2/ngtcp2.h> + +#if HAVE_DECL_BE64TOH +#  define ngtcp2_ntohl64(N) be64toh(N) +#  define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_DECL_BE64TOH */ +#  ifdef WORDS_BIGENDIAN +#    define ngtcp2_ntohl64(N) (N) +#    define ngtcp2_htonl64(N) (N) +#  else /* !defined(WORDS_BIGENDIAN) */ +#    if HAVE_DECL_BSWAP_64 +#      define ngtcp2_bswap64 bswap_64 +#    elif defined(WIN32) +#      define ngtcp2_bswap64 _byteswap_uint64 +#    elif defined(__APPLE__) +#      define ngtcp2_bswap64 OSSwapInt64 +#    else /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#      define ngtcp2_bswap64(N)                                                \ +        ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 |                       \ +         ngtcp2_ntohl((uint32_t)((N) >> 32))) +#    endif /* !(HAVE_DECL_BSWAP_64 || defined(WIN32) || defined(__APPLE__)) */ +#    define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +#    define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +#  endif /* !defined(WORDS_BIGENDIAN) */ +#endif   /* !HAVE_DECL_BE64TOH */ + +#ifdef WIN32 +/* Windows requires ws2_32 library for ntonl family functions.  We +   define inline functions for those function so that we don't have +   dependency on that lib. */ + +#  ifdef _MSC_VER +#    define STIN static __inline +#  else /* !defined(_MSC_VER) */ +#    define STIN static inline +#  endif /* !defined(_MSC_VER) */ + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&res; +  *p++ = (unsigned char)(hostlong >> 24); +  *p++ = (hostlong >> 16) & 0xffu; +  *p++ = (hostlong >> 8) & 0xffu; +  *p = hostlong & 0xffu; +  return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { +  uint16_t res; +  unsigned char *p = (unsigned char *)&res; +  *p++ = (unsigned char)(hostshort >> 8); +  *p = hostshort & 0xffu; +  return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { +  uint32_t res; +  unsigned char *p = (unsigned char *)&netlong; +  res = (uint32_t)(*p++ << 24); +  res += (uint32_t)(*p++ << 16); +  res += (uint32_t)(*p++ << 8); +  res += *p; +  return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { +  uint16_t res; +  unsigned char *p = (unsigned char *)&netshort; +  res = (uint16_t)(*p++ << 8); +  res += *p; +  return res; +} + +#else /* !defined(WIN32) */ + +#  define ngtcp2_htonl htonl +#  define ngtcp2_htons htons +#  define ngtcp2_ntohl ntohl +#  define ngtcp2_ntohs ntohs + +#endif /* !defined(WIN32) */ + +#endif /* !defined(NGTCP2_NET_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.c b/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.c new file mode 100644 index 00000000000..8b06cdd5de3 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.c @@ -0,0 +1,40 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_objalloc.h" + +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, +                          const ngtcp2_mem *mem) { +  ngtcp2_balloc_init(&objalloc->balloc, blklen, mem); +  ngtcp2_opl_init(&objalloc->opl); +} + +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) { +  ngtcp2_balloc_free(&objalloc->balloc); +} + +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) { +  ngtcp2_opl_clear(&objalloc->opl); +  ngtcp2_balloc_clear(&objalloc->balloc); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.h b/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.h new file mode 100644 index 00000000000..cf23de7b2b7 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_objalloc.h @@ -0,0 +1,147 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_OBJALLOC_H +#define NGTCP2_OBJALLOC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_balloc.h" +#include "ngtcp2_opl.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +/* + * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides + * an object pool with the custom allocator to reduce the allocation + * and deallocation overheads for small objects. + */ +typedef struct ngtcp2_objalloc { +  ngtcp2_balloc balloc; +  ngtcp2_opl opl; +} ngtcp2_objalloc; + +/* + * ngtcp2_objalloc_init initializes |objalloc|.  |blklen| is directly + * passed to ngtcp2_balloc_init. + */ +void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen, +                          const ngtcp2_mem *mem); + +/* + * ngtcp2_objalloc_free releases all allocated resources. + */ +void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_objalloc_clear releases all allocated resources and + * initializes its state. + */ +void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc); + +#ifndef NOMEMPOOL +#  define ngtcp2_objalloc_decl(NAME, TYPE, OPLENTFIELD)                        \ +    inline static void ngtcp2_objalloc_##NAME##_init(                          \ +      ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) {        \ +      ngtcp2_objalloc_init(                                                    \ +        objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem);    \ +    }                                                                          \ +                                                                               \ +    TYPE *ngtcp2_objalloc_##NAME##_get(ngtcp2_objalloc *objalloc);             \ +                                                                               \ +    TYPE *ngtcp2_objalloc_##NAME##_len_get(ngtcp2_objalloc *objalloc,          \ +                                           size_t len);                        \ +                                                                               \ +    inline static void ngtcp2_objalloc_##NAME##_release(                       \ +      ngtcp2_objalloc *objalloc, TYPE *obj) {                                  \ +      ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD);                      \ +    } + +#  define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD)                         \ +    TYPE *ngtcp2_objalloc_##NAME##_get(ngtcp2_objalloc *objalloc) {            \ +      ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl);               \ +      TYPE *obj;                                                               \ +      int rv;                                                                  \ +                                                                               \ +      if (!oplent) {                                                           \ +        rv =                                                                   \ +          ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE));   \ +        if (rv != 0) {                                                         \ +          return NULL;                                                         \ +        }                                                                      \ +                                                                               \ +        return obj;                                                            \ +      }                                                                        \ +                                                                               \ +      return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD);                      \ +    }                                                                          \ +                                                                               \ +    TYPE *ngtcp2_objalloc_##NAME##_len_get(ngtcp2_objalloc *objalloc,          \ +                                           size_t len) {                       \ +      ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl);               \ +      TYPE *obj;                                                               \ +      int rv;                                                                  \ +                                                                               \ +      if (!oplent) {                                                           \ +        rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len);         \ +        if (rv != 0) {                                                         \ +          return NULL;                                                         \ +        }                                                                      \ +                                                                               \ +        return obj;                                                            \ +      }                                                                        \ +                                                                               \ +      return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD);                      \ +    } +#else /* defined(NOMEMPOOL) */ +#  define ngtcp2_objalloc_decl(NAME, TYPE, OPLENTFIELD)                        \ +    inline static void ngtcp2_objalloc_##NAME##_init(                          \ +      ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) {        \ +      ngtcp2_objalloc_init(                                                    \ +        objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem);    \ +    }                                                                          \ +                                                                               \ +    inline static TYPE *ngtcp2_objalloc_##NAME##_get(                          \ +      ngtcp2_objalloc *objalloc) {                                             \ +      return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE));            \ +    }                                                                          \ +                                                                               \ +    inline static TYPE *ngtcp2_objalloc_##NAME##_len_get(                      \ +      ngtcp2_objalloc *objalloc, size_t len) {                                 \ +      return ngtcp2_mem_malloc(objalloc->balloc.mem, len);                     \ +    }                                                                          \ +                                                                               \ +    inline static void ngtcp2_objalloc_##NAME##_release(                       \ +      ngtcp2_objalloc *objalloc, TYPE *obj) {                                  \ +      ngtcp2_mem_free(objalloc->balloc.mem, obj);                              \ +    } + +#  define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) +#endif /* defined(NOMEMPOOL) */ + +#endif /* !defined(NGTCP2_OBJALLOC_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_opl.c b/contrib/libs/ngtcp2/lib/ngtcp2_opl.c new file mode 100644 index 00000000000..a29361c6349 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_opl.c @@ -0,0 +1,46 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_opl.h" + +void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; } + +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) { +  ent->next = opl->head; +  opl->head = ent; +} + +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) { +  ngtcp2_opl_entry *ent = opl->head; + +  if (!ent) { +    return NULL; +  } + +  opl->head = ent->next; + +  return ent; +} + +void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; } diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_opl.h b/contrib/libs/ngtcp2/lib/ngtcp2_opl.h new file mode 100644 index 00000000000..f2df3f6dccd --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_opl.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_OPL_H +#define NGTCP2_OPL_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_opl_entry ngtcp2_opl_entry; + +struct ngtcp2_opl_entry { +  ngtcp2_opl_entry *next; +}; + +/* + * ngtcp2_opl is an object memory pool. + */ +typedef struct ngtcp2_opl { +  ngtcp2_opl_entry *head; +} ngtcp2_opl; + +/* + * ngtcp2_opl_init initializes |opl|. + */ +void ngtcp2_opl_init(ngtcp2_opl *opl); + +/* + * ngtcp2_opl_push inserts |ent| to |opl| head. + */ +void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent); + +/* + * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and + * returns it.  If |opl| does not have any entry, it returns NULL. + */ +ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl); + +void ngtcp2_opl_clear(ngtcp2_opl *opl); + +#endif /* !defined(NGTCP2_OPL_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_path.c b/contrib/libs/ngtcp2/lib/ngtcp2_path.c new file mode 100644 index 00000000000..83238730033 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_path.c @@ -0,0 +1,77 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_path.h" + +#include <string.h> + +#include "ngtcp2_addr.h" + +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, +                      const ngtcp2_addr *remote) { +  path->local = *local; +  path->remote = *remote; +} + +void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { +  ngtcp2_addr_copy(&dest->local, &src->local); +  ngtcp2_addr_copy(&dest->remote, &src->remote); +  dest->user_data = src->user_data; +} + +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 ngtcp2_sockaddr *local_addr, +                              ngtcp2_socklen local_addrlen, +                              const ngtcp2_sockaddr *remote_addr, +                              ngtcp2_socklen remote_addrlen, void *user_data) { +  ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, +                   0); +  ngtcp2_addr_init(&ps->path.remote, +                   (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); + +  ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); +  ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); + +  ps->path.user_data = user_data; +} + +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->remote.addr, path->remote.addrlen, +                           path->user_data); +} + +void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { +  ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf, +                   0); +  ngtcp2_addr_init(&ps->path.remote, +                   (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0); +  ps->path.user_data = NULL; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_path.h b/contrib/libs/ngtcp2/lib/ngtcp2_path.h new file mode 100644 index 00000000000..a708378db32 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_path.h @@ -0,0 +1,49 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PATH_H +#define NGTCP2_PATH_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_path_init initializes |path| with the given addresses.  Note + * that the buffer pointed by local->addr and remote->addr are not + * copied.  Their pointer values are assigned instead. + */ +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, +                      const ngtcp2_addr *remote); + +/* + * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial + * data. + */ +void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, +                               const ngtcp2_path *path); + +#endif /* !defined(NGTCP2_PATH_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c new file mode 100644 index 00000000000..5c82e1bd503 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c @@ -0,0 +1,2575 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pkt.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_conv.h" +#include "ngtcp2_str.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_unreachable.h" + +int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, +                         const ngtcp2_pkt_info *pi, const uint8_t *pkt, +                         size_t pktlen, size_t dgramlen, 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)->pi = *pi; +  (*ppc)->next = NULL; +  (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain); +  (*ppc)->pktlen = pktlen; +  (*ppc)->dgramlen = dgramlen; +  (*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(ngtcp2_version_cid *dest, const uint8_t *data, +                                  size_t datalen, size_t short_dcidlen) { +  size_t len; +  uint32_t version; +  size_t dcidlen, scidlen; +  int supported_version; + +  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; +    } + +    ngtcp2_get_uint32be(&version, &data[1]); + +    supported_version = ngtcp2_is_supported_version(version); + +    if (supported_version && +        (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    if (version && !supported_version && +        datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    dest->version = version; +    dest->dcid = &data[6]; +    dest->dcidlen = dcidlen; +    dest->scid = &data[6 + dcidlen + 1]; +    dest->scidlen = scidlen; + +    if (!version) { +      /* VN */ +      return 0; +    } + +    if (!supported_version) { +      return NGTCP2_ERR_VERSION_NEGOTIATION; +    } + +    return 0; +  } + +  assert(short_dcidlen <= NGTCP2_MAX_CIDLEN); + +  len = 1 + short_dcidlen; +  if (datalen < len) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  dest->version = 0; +  dest->dcid = &data[1]; +  dest->dcidlen = short_dcidlen; +  dest->scid = NULL; +  dest->scidlen = 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; +} + +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 ntokenlen = 0; +  const uint8_t *token = NULL; +  size_t tokenlen = 0; +  size_t nlonglen = 0; +  size_t longlen = 0; +  uint64_t vi; +  uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM; + +  if (pktlen < 5) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  ngtcp2_get_uint32be(&version, &pkt[1]); + +  if (version == 0) { +    type = NGTCP2_PKT_VERSION_NEGOTIATION; +    /* Version Negotiation is not a long header packet. */ +    flags = NGTCP2_PKT_FLAG_NONE; +    /* 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)) { +      flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; +    } + +    type = ngtcp2_pkt_get_type_long(version, pkt[0]); +    switch (type) { +    case 0: +      return NGTCP2_ERR_INVALID_ARGUMENT; +    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 */ +      ngtcp2_unreachable(); +    } +  } + +  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_uvarintlen(p); +    len += ntokenlen - 1; + +    if (pktlen < len) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    p = ngtcp2_get_uvarint(&vi, p); +    if (pktlen - len < vi) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    tokenlen = (size_t)vi; +    len += tokenlen; + +    if (tokenlen) { +      token = p; +    } + +    p += tokenlen; +  } + +  switch (type) { +  case NGTCP2_PKT_RETRY: +    break; +  default: +    if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) { +      assert(type == NGTCP2_PKT_VERSION_NEGOTIATION); +      /* Version Negotiation is not a long header packet. */ +      break; +    } + +    /* Length */ +    nlonglen = ngtcp2_get_uvarintlen(p); +    len += nlonglen - 1; + +    if (pktlen < len) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } + +    ngtcp2_get_uvarint(&vi, p); +#if SIZE_MAX > UINT32_MAX +    if (vi > SIZE_MAX) { +      return NGTCP2_ERR_INVALID_ARGUMENT; +    } +#endif /* SIZE_MAX > UINT32_MAX */ + +    longlen = (size_t)vi; +  } + +  dest->flags = flags; +  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 = token; +  dest->tokenlen = tokenlen; +  p += ntokenlen + tokenlen; + +  dest->len = longlen; + +#ifndef NDEBUG +  p += nlonglen; +#endif /* !defined(NDEBUG) */ + +  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; +  uint8_t flags = NGTCP2_PKT_FLAG_NONE; + +  assert(dcidlen <= NGTCP2_MAX_CIDLEN); + +  if (pktlen < len) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { +    return NGTCP2_ERR_INVALID_ARGUMENT; +  } + +  if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { +    flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR; +  } + +  p = &pkt[1]; + +  dest->type = NGTCP2_PKT_1RTT; + +  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 = flags; +  dest->version = 0; +  dest->len = 0; +  dest->pkt_num = 0; +  dest->pkt_numlen = 0; +  dest->token = NULL; +  dest->tokenlen = 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 += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen; +  } + +  if (hd->type == NGTCP2_PKT_INITIAL) { +    len += ngtcp2_put_uvarintlen(hd->tokenlen) + hd->tokenlen; +  } + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | +                 (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) | +                 (uint8_t)(hd->pkt_numlen - 1)); + +  if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { +    *p |= NGTCP2_FIXED_BIT_MASK; +  } + +  ++p; + +  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_uvarint(p, hd->tokenlen); + +    if (hd->tokenlen) { +      p = ngtcp2_cpymem(p, hd->token, hd->tokenlen); +    } +  } + +  if (hd->type != NGTCP2_PKT_RETRY) { +    p = ngtcp2_put_uvarint30(p, (uint32_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 = (uint8_t)(hd->pkt_numlen - 1); + +  if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) { +    *p |= NGTCP2_FIXED_BIT_MASK; +  } + +  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 NGTCP2_ERR_FRAME_ENCODING; +  } + +  type = payload[0]; + +  switch (type) { +  case NGTCP2_FRAME_PADDING: +    return ngtcp2_pkt_decode_padding_frame(&dest->padding, payload, payloadlen); +  case NGTCP2_FRAME_RESET_STREAM: +    return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload, +                                                payloadlen); +  case NGTCP2_FRAME_CONNECTION_CLOSE: +  case NGTCP2_FRAME_CONNECTION_CLOSE_APP: +    return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close, +                                                    payload, payloadlen); +  case NGTCP2_FRAME_MAX_DATA: +    return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload, +                                            payloadlen); +  case NGTCP2_FRAME_MAX_STREAM_DATA: +    return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data, +                                                   payload, payloadlen); +  case NGTCP2_FRAME_MAX_STREAMS_BIDI: +  case NGTCP2_FRAME_MAX_STREAMS_UNI: +    return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload, +                                               payloadlen); +  case NGTCP2_FRAME_PING: +    return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen); +  case NGTCP2_FRAME_DATA_BLOCKED: +    return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload, +                                                payloadlen); +  case NGTCP2_FRAME_STREAM_DATA_BLOCKED: +    return ngtcp2_pkt_decode_stream_data_blocked_frame( +      &dest->stream_data_blocked, payload, payloadlen); +  case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: +  case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: +    return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked, +                                                   payload, payloadlen); +  case NGTCP2_FRAME_NEW_CONNECTION_ID: +    return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id, +                                                     payload, payloadlen); +  case NGTCP2_FRAME_STOP_SENDING: +    return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload, +                                                payloadlen); +  case NGTCP2_FRAME_ACK: +  case NGTCP2_FRAME_ACK_ECN: +    return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen); +  case NGTCP2_FRAME_PATH_CHALLENGE: +    return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge, +                                                  payload, payloadlen); +  case NGTCP2_FRAME_PATH_RESPONSE: +    return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload, +                                                 payloadlen); +  case NGTCP2_FRAME_CRYPTO: +    return ngtcp2_pkt_decode_crypto_frame(&dest->stream, 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); +  case NGTCP2_FRAME_DATAGRAM: +  case NGTCP2_FRAME_DATAGRAM_LEN: +    return ngtcp2_pkt_decode_datagram_frame(&dest->datagram, payload, +                                            payloadlen); +  default: +    if ((type & ~(NGTCP2_FRAME_STREAM - 1)) == NGTCP2_FRAME_STREAM) { +      return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen); +    } + +    /* For frame types > 0xff, use ngtcp2_get_uvarintlen and +       ngtcp2_get_uvarint to get a frame type, and then switch over +       it.  Verify that payloadlen >= ngtcp2_get_uvarintlen(payload) +       before calling ngtcp2_get_uvarint(payload). */ + +    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 = 0; +  size_t ndatalen = 0; +  size_t n; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  type = payload[0]; + +  p = payload + 1; + +  n = ngtcp2_get_uvarintlen(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_uvarintlen(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_uvarintlen(p); +    len += ndatalen - 1; + +    if (payloadlen < len) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    /* p = */ ngtcp2_get_uvarint(&vi, p); +    if (payloadlen - len < vi) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    datalen = (size_t)vi; +    len += datalen; +  } else { +    len = payloadlen; +  } + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_STREAM; +  dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM); +  dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0; +  p = ngtcp2_get_varint(&dest->stream_id, p); + +  if (type & NGTCP2_STREAM_OFF_BIT) { +    p = ngtcp2_get_uvarint(&dest->offset, p); +  } 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 rangecnt, max_rangecnt; +  size_t nrangecnt; +  size_t len = 1 + 1 + 1 + 1 + 1; +  const uint8_t *p; +  size_t i, j; +  ngtcp2_ack_range *range; +  size_t n; +  uint8_t type; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  type = payload[0]; + +  p = payload + 1; + +  /* Largest Acknowledged */ +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  /* ACK Delay */ +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  /* ACK Range Count */ +  nrangecnt = ngtcp2_get_uvarintlen(p); +  len += nrangecnt - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = ngtcp2_get_uvarint(&vi, p); +  if (vi > SIZE_MAX / (1 + 1) || payloadlen - len < vi * (1 + 1)) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  rangecnt = (size_t)vi; +  len += rangecnt * (1 + 1); + +  /* First ACK Range */ +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  for (i = 0; i < rangecnt; ++i) { +    /* Gap, and Additional ACK Range */ +    for (j = 0; j < 2; ++j) { +      n = ngtcp2_get_uvarintlen(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_uvarintlen(p); +      len += n - 1; + +      if (payloadlen < len) { +        return NGTCP2_ERR_FRAME_ENCODING; +      } + +      p += n; +    } +  } + +  /* TODO We might not decode all ranges.  It could be very large. */ +  max_rangecnt = ngtcp2_min_size(NGTCP2_MAX_ACK_RANGES, rangecnt); + +  p = payload + 1; + +  dest->type = type; +  p = ngtcp2_get_varint(&dest->largest_ack, p); +  p = ngtcp2_get_uvarint(&dest->ack_delay, p); +  /* This value will be assigned in the upper layer. */ +  dest->ack_delay_unscaled = 0; +  dest->rangecnt = max_rangecnt; +  p += nrangecnt; +  p = ngtcp2_get_uvarint(&dest->first_ack_range, p); + +  for (i = 0; i < max_rangecnt; ++i) { +    range = &dest->ranges[i]; +    p = ngtcp2_get_uvarint(&range->gap, p); +    p = ngtcp2_get_uvarint(&range->len, p); +  } + +  for (; i < rangecnt; ++i) { +    p += ngtcp2_get_uvarintlen(p); +    p += ngtcp2_get_uvarintlen(p); +  } + +  if (type == NGTCP2_FRAME_ACK_ECN) { +    p = ngtcp2_get_uvarint(&dest->ecn.ect0, p); +    p = ngtcp2_get_uvarint(&dest->ecn.ect1, p); +    p = ngtcp2_get_uvarint(&dest->ecn.ce, p); +  } + +  assert((size_t)(p - payload) == len); + +  return (ngtcp2_ssize)len; +} + +ngtcp2_ssize 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 (ngtcp2_ssize)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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_RESET_STREAM; +  p = ngtcp2_get_varint(&dest->stream_id, p); +  p = ngtcp2_get_uvarint(&dest->app_error_code, p); +  p = ngtcp2_get_uvarint(&dest->final_size, p); + +  assert((size_t)(p - payload) == len); + +  return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( +  ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) { +  size_t len = 1 + 1 + 1; +  const uint8_t *p; +  size_t reasonlen; +  size_t nreasonlen; +  size_t n; +  uint8_t type; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  type = payload[0]; + +  p = payload + 1; + +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { +    ++len; + +    n = ngtcp2_get_uvarintlen(p); +    len += n - 1; + +    if (payloadlen < len) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    p += n; +  } + +  nreasonlen = ngtcp2_get_uvarintlen(p); +  len += nreasonlen - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = ngtcp2_get_uvarint(&vi, p); +  if (payloadlen - len < vi) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  reasonlen = (size_t)vi; +  len += reasonlen; + +  p = payload + 1; + +  dest->type = type; +  p = ngtcp2_get_uvarint(&dest->error_code, p); + +  if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { +    p = ngtcp2_get_uvarint(&dest->frame_type, p); +  } 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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  dest->type = NGTCP2_FRAME_MAX_DATA; +  p = ngtcp2_get_uvarint(&dest->max_data, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_MAX_STREAM_DATA; +  p = ngtcp2_get_varint(&dest->stream_id, p); +  p = ngtcp2_get_uvarint(&dest->max_stream_data, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  dest->type = payload[0]; +  p = ngtcp2_get_uvarint(&dest->max_streams, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  dest->type = NGTCP2_FRAME_DATA_BLOCKED; +  p = ngtcp2_get_uvarint(&dest->offset, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; +  p = ngtcp2_get_varint(&dest->stream_id, p); +  p = ngtcp2_get_uvarint(&dest->offset, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  dest->type = payload[0]; +  p = ngtcp2_get_uvarint(&dest->max_streams, p); + +  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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  n = ngtcp2_get_uvarintlen(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; +  p = ngtcp2_get_uvarint(&dest->seq, p); +  p = ngtcp2_get_uvarint(&dest->retire_prior_to, p); +  ++p; +  ngtcp2_cid_init(&dest->cid, p, cil); +  p += cil; +  p = ngtcp2_get_bytes(dest->stateless_reset_token, 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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_STOP_SENDING; +  p = ngtcp2_get_varint(&dest->stream_id, p); +  p = ngtcp2_get_uvarint(&dest->app_error_code, p); + +  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_stream *dest, +                                            const uint8_t *payload, +                                            size_t payloadlen) { +  size_t len = 1 + 1 + 1; +  const uint8_t *p; +  size_t datalen; +  size_t ndatalen; +  size_t n; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p += n; + +  ndatalen = ngtcp2_get_uvarintlen(p); +  len += ndatalen - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = ngtcp2_get_uvarint(&vi, p); +  if (payloadlen - len < vi) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  datalen = (size_t)vi; +  len += datalen; + +  p = payload + 1; + +  dest->type = NGTCP2_FRAME_CRYPTO; +  dest->flags = 0; +  dest->fin = 0; +  dest->stream_id = 0; +  p = ngtcp2_get_uvarint(&dest->offset, p); +  dest->data[0].len = datalen; +  p += ndatalen; + +  if (dest->data[0].len) { +    dest->data[0].base = (uint8_t *)p; +    p += dest->data[0].len; +    dest->datacnt = 1; +  } else { +    dest->data[0].base = NULL; +    dest->datacnt = 0; +  } + +  assert((size_t)(p - payload) == len); + +  return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, +                                               const uint8_t *payload, +                                               size_t payloadlen) { +  size_t len = 1 + 1; +  const uint8_t *p; +  size_t n; +  size_t datalen; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = payload + 1; + +  n = ngtcp2_get_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  p = ngtcp2_get_uvarint(&vi, p); +  if (payloadlen - len < vi) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  datalen = (size_t)vi; +  len += datalen; + +  dest->type = NGTCP2_FRAME_NEW_TOKEN; +  dest->tokenlen = datalen; +  dest->token = (uint8_t *)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_uvarintlen(p); +  len += n - 1; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; +  p = ngtcp2_get_uvarint(&dest->seq, p); + +  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_decode_datagram_frame(ngtcp2_datagram *dest, +                                              const uint8_t *payload, +                                              size_t payloadlen) { +  size_t len = 1; +  const uint8_t *p; +  uint8_t type; +  size_t datalen; +  size_t n; +  uint64_t vi; + +  if (payloadlen < len) { +    return NGTCP2_ERR_FRAME_ENCODING; +  } + +  type = payload[0]; + +  p = payload + 1; + +  switch (type) { +  case NGTCP2_FRAME_DATAGRAM: +    datalen = payloadlen - 1; +    len = payloadlen; +    break; +  case NGTCP2_FRAME_DATAGRAM_LEN: +    ++len; +    if (payloadlen < len) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    n = ngtcp2_get_uvarintlen(p); +    len += n - 1; + +    if (payloadlen < len) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    p = ngtcp2_get_uvarint(&vi, p); +    if (payloadlen - len < vi) { +      return NGTCP2_ERR_FRAME_ENCODING; +    } + +    datalen = (size_t)vi; +    len += datalen; + +    break; +  default: +    ngtcp2_unreachable(); +  } + +  dest->type = type; + +  if (datalen == 0) { +    dest->datacnt = 0; +    dest->data = NULL; +  } else { +    dest->datacnt = 1; +    dest->data = dest->rdata; +    dest->rdata[0].len = datalen; + +    dest->rdata[0].base = (uint8_t *)p; +    p += datalen; +  } + +  assert((size_t)(p - payload) == len); + +  return (ngtcp2_ssize)len; +} + +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: +  case NGTCP2_FRAME_ACK_ECN: +    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->stream); +  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); +  case NGTCP2_FRAME_DATAGRAM: +  case NGTCP2_FRAME_DATAGRAM_LEN: +    return ngtcp2_pkt_encode_datagram_frame(out, outlen, &fr->datagram); +  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_uvarintlen(fr->offset); +  } + +  len += ngtcp2_put_uvarintlen((uint64_t)fr->stream_id); + +  for (i = 0; i < fr->datacnt; ++i) { +    datalen += fr->data[i].len; +  } + +  len += ngtcp2_put_uvarintlen(datalen); +  len += datalen; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = flags | NGTCP2_FRAME_STREAM; + +  fr->flags = flags; + +  p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id); + +  if (fr->offset) { +    p = ngtcp2_put_uvarint(p, fr->offset); +  } + +  p = ngtcp2_put_uvarint(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, +                                         const ngtcp2_ack *fr) { +  size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->largest_ack) + +               ngtcp2_put_uvarintlen(fr->ack_delay) + +               ngtcp2_put_uvarintlen(fr->rangecnt) + +               ngtcp2_put_uvarintlen(fr->first_ack_range); +  uint8_t *p; +  size_t i; +  const ngtcp2_ack_range *range; + +  for (i = 0; i < fr->rangecnt; ++i) { +    range = &fr->ranges[i]; +    len += ngtcp2_put_uvarintlen(range->gap); +    len += ngtcp2_put_uvarintlen(range->len); +  } + +  if (fr->type == NGTCP2_FRAME_ACK_ECN) { +    len += ngtcp2_put_uvarintlen(fr->ecn.ect0) + +           ngtcp2_put_uvarintlen(fr->ecn.ect1) + +           ngtcp2_put_uvarintlen(fr->ecn.ce); +  } + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = (uint8_t)fr->type; +  p = ngtcp2_put_uvarint(p, (uint64_t)fr->largest_ack); +  p = ngtcp2_put_uvarint(p, fr->ack_delay); +  p = ngtcp2_put_uvarint(p, fr->rangecnt); +  p = ngtcp2_put_uvarint(p, fr->first_ack_range); + +  for (i = 0; i < fr->rangecnt; ++i) { +    range = &fr->ranges[i]; +    p = ngtcp2_put_uvarint(p, range->gap); +    p = ngtcp2_put_uvarint(p, range->len); +  } + +  if (fr->type == NGTCP2_FRAME_ACK_ECN) { +    p = ngtcp2_put_uvarint(p, fr->ecn.ect0); +    p = ngtcp2_put_uvarint(p, fr->ecn.ect1); +    p = ngtcp2_put_uvarint(p, fr->ecn.ce); +  } + +  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_uvarintlen((uint64_t)fr->stream_id) + +               ngtcp2_put_uvarintlen(fr->app_error_code) + +               ngtcp2_put_uvarintlen(fr->final_size); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_RESET_STREAM; +  p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id); +  p = ngtcp2_put_uvarint(p, fr->app_error_code); +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->error_code) + +               (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE +                  ? ngtcp2_put_uvarintlen(fr->frame_type) +                  : 0) + +               ngtcp2_put_uvarintlen(fr->reasonlen) + fr->reasonlen; +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = (uint8_t)fr->type; +  p = ngtcp2_put_uvarint(p, fr->error_code); + +  if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { +    p = ngtcp2_put_uvarint(p, fr->frame_type); +  } + +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->max_data); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_MAX_DATA; +  p = ngtcp2_put_uvarint(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_uvarintlen((uint64_t)fr->stream_id) + +               ngtcp2_put_uvarintlen(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_uvarint(p, (uint64_t)fr->stream_id); +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->max_streams); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = (uint8_t)fr->type; +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->offset); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_DATA_BLOCKED; +  p = ngtcp2_put_uvarint(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_uvarintlen((uint64_t)fr->stream_id) + +               ngtcp2_put_uvarintlen(fr->offset); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED; +  p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id); +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->max_streams); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = (uint8_t)fr->type; +  p = ngtcp2_put_uvarint(p, fr->max_streams); + +  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_uvarintlen(fr->seq) + +               ngtcp2_put_uvarintlen(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_uvarint(p, fr->seq); +  p = ngtcp2_put_uvarint(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_uvarintlen((uint64_t)fr->stream_id) + +               ngtcp2_put_uvarintlen(fr->app_error_code); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_STOP_SENDING; +  p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id); +  p = ngtcp2_put_uvarint(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_stream *fr) { +  size_t len = 1; +  uint8_t *p; +  size_t i; +  size_t datalen = 0; + +  len += ngtcp2_put_uvarintlen(fr->offset); + +  for (i = 0; i < fr->datacnt; ++i) { +    datalen += fr->data[i].len; +  } + +  len += ngtcp2_put_uvarintlen(datalen); +  len += datalen; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_CRYPTO; + +  p = ngtcp2_put_uvarint(p, fr->offset); +  p = ngtcp2_put_uvarint(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_uvarintlen(fr->tokenlen) + fr->tokenlen; +  uint8_t *p; + +  assert(fr->tokenlen); + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_NEW_TOKEN; + +  p = ngtcp2_put_uvarint(p, 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_uvarintlen(fr->seq); +  uint8_t *p; + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + +  p = ngtcp2_put_uvarint(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_encode_datagram_frame(uint8_t *out, size_t outlen, +                                              const ngtcp2_datagram *fr) { +  uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); +  uint64_t len = +    1 + +    (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_uvarintlen(datalen)) + +    datalen; +  uint8_t *p; +  size_t i; + +  assert(fr->type == NGTCP2_FRAME_DATAGRAM || +         fr->type == NGTCP2_FRAME_DATAGRAM_LEN); + +  if (outlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = out; + +  *p++ = (uint8_t)fr->type; + +  if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) { +    p = ngtcp2_put_uvarint(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_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++ = 0xc0 | 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 = ngtcp2_get_uint32be(dest++, 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 = (uint8_t *)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 pkt_numlen) { +  int64_t expected = max_pkt_num + 1; +  int64_t win = (int64_t)1 << (pkt_numlen * 8); +  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(const ngtcp2_ack *fr, int64_t min_pkt_num) { +  int64_t largest_ack = fr->largest_ack; +  size_t i; + +  if (largest_ack < (int64_t)fr->first_ack_range) { +    return NGTCP2_ERR_ACK_FRAME; +  } + +  largest_ack -= (int64_t)fr->first_ack_range; + +  if (largest_ack < min_pkt_num) { +    return NGTCP2_ERR_PROTO; +  } + +  for (i = 0; i < fr->rangecnt; ++i) { +    if (largest_ack < (int64_t)fr->ranges[i].gap + 2) { +      return NGTCP2_ERR_ACK_FRAME; +    } + +    largest_ack -= (int64_t)fr->ranges[i].gap + 2; + +    if (largest_ack < (int64_t)fr->ranges[i].len) { +      return NGTCP2_ERR_ACK_FRAME; +    } + +    largest_ack -= (int64_t)fr->ranges[i].len; + +    if (largest_ack < min_pkt_num) { +      return NGTCP2_ERR_PROTO; +    } +  } + +  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_size(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen); + +  p = ngtcp2_cpymem(p, rand, randlen); +  p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN); +  *dest = (uint8_t)((*dest & 0x7fu) | 0x40u); + +  return p - dest; +} + +ngtcp2_ssize ngtcp2_pkt_write_retry( +  uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, +  const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, +  size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, +  const ngtcp2_crypto_aead_ctx *aead_ctx) { +  ngtcp2_pkt_hd hd; +  uint8_t pseudo_retry[1500]; +  ngtcp2_ssize pseudo_retrylen; +  uint8_t tag[NGTCP2_RETRY_TAGLEN]; +  int rv; +  uint8_t *p; +  size_t offset; +  const uint8_t *nonce; +  size_t noncelen; + +  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 NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */ +  if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { +    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, version, +                     /* 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; +  } + +  switch (version) { +  case NGTCP2_PROTO_VER_V1: +  default: +    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; +    break; +  case NGTCP2_PROTO_VER_V2: +    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1; +    break; +  } + +  /* OpenSSL does not like NULL plaintext. */ +  rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen, +               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(uint32_t version, const ngtcp2_pkt_retry *retry, +                                const uint8_t *pkt, size_t pktlen, +                                ngtcp2_encrypt encrypt, +                                const ngtcp2_crypto_aead *aead, +                                const ngtcp2_crypto_aead_ctx *aead_ctx) { +  uint8_t pseudo_retry[1500]; +  size_t pseudo_retrylen; +  uint8_t *p = pseudo_retry; +  int rv; +  uint8_t tag[NGTCP2_RETRY_TAGLEN]; +  const uint8_t *nonce; +  size_t noncelen; + +  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); + +  switch (version) { +  case NGTCP2_PROTO_VER_V1: +  default: +    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; +    break; +  case NGTCP2_PROTO_VER_V2: +    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2; +    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1; +    break; +  } + +  /* OpenSSL does not like NULL plaintext. */ +  rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen, +               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, +                                     uint64_t len, size_t left) { +  size_t n = 1 /* type */ + ngtcp2_put_uvarintlen((uint64_t)stream_id) + +             (offset ? ngtcp2_put_uvarintlen(offset) : 0); + +  if (left <= n) { +    return (size_t)-1; +  } + +  left -= n; + +  if (left > 8 + 1073741823 && len > 1073741823) { +#if SIZE_MAX > UINT32_MAX +    len = ngtcp2_min_uint64(len, 4611686018427387903lu); +#endif /* SIZE_MAX > UINT32_MAX */ +    return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 8)); +  } + +  if (left > 4 + 16383 && len > 16383) { +    len = ngtcp2_min_uint64(len, 1073741823); +    return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 4)); +  } + +  if (left > 2 + 63 && len > 63) { +    len = ngtcp2_min_uint64(len, 16383); +    return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 2)); +  } + +  len = ngtcp2_min_uint64(len, 63); +  return (size_t)ngtcp2_min_uint64(len, (uint64_t)(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_uvarintlen(offset); + +  /* CRYPTO frame must contain nonzero length data.  Return -1 if +     there is no space to write crypto data. */ +  if (left <= n + 1) { +    return (size_t)-1; +  } + +  left -= n; + +  if (left > 8 + 1073741823 && len > 1073741823) { +#if SIZE_MAX > UINT32_MAX +    len = ngtcp2_min_size(len, 4611686018427387903lu); +#endif /* SIZE_MAX > UINT32_MAX */ +    return ngtcp2_min_size(len, left - 8); +  } + +  if (left > 4 + 16383 && len > 16383) { +    len = ngtcp2_min_size(len, 1073741823); +    return ngtcp2_min_size(len, left - 4); +  } + +  if (left > 2 + 63 && len > 63) { +    len = ngtcp2_min_size(len, 16383); +    return ngtcp2_min_size(len, left - 2); +  } + +  len = ngtcp2_min_size(len, 63); +  return ngtcp2_min_size(len, left - 1); +} + +size_t ngtcp2_pkt_datagram_framelen(size_t len) { +  return 1 /* type */ + ngtcp2_put_uvarintlen(len) + len; +} + +int ngtcp2_is_supported_version(uint32_t version) { +  switch (version) { +  case NGTCP2_PROTO_VER_V1: +  case NGTCP2_PROTO_VER_V2: +    return 1; +  default: +    return 0; +  } +} + +int ngtcp2_is_reserved_version(uint32_t version) { +  return (version & NGTCP2_RESERVED_VERSION_MASK) == +         NGTCP2_RESERVED_VERSION_MASK; +} + +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) { +  uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); + +  switch (version) { +  case NGTCP2_PROTO_VER_V2: +    switch (pkt_type) { +    case NGTCP2_PKT_TYPE_INITIAL_V2: +      return NGTCP2_PKT_INITIAL; +    case NGTCP2_PKT_TYPE_0RTT_V2: +      return NGTCP2_PKT_0RTT; +    case NGTCP2_PKT_TYPE_HANDSHAKE_V2: +      return NGTCP2_PKT_HANDSHAKE; +    case NGTCP2_PKT_TYPE_RETRY_V2: +      return NGTCP2_PKT_RETRY; +    default: +      return 0; +    } +  default: +    if (!ngtcp2_is_supported_version(version)) { +      return 0; +    } + +    switch (pkt_type) { +    case NGTCP2_PKT_TYPE_INITIAL_V1: +      return NGTCP2_PKT_INITIAL; +    case NGTCP2_PKT_TYPE_0RTT_V1: +      return NGTCP2_PKT_0RTT; +    case NGTCP2_PKT_TYPE_HANDSHAKE_V1: +      return NGTCP2_PKT_HANDSHAKE; +    case NGTCP2_PKT_TYPE_RETRY_V1: +      return NGTCP2_PKT_RETRY; +    default: +      return 0; +    } +  } +} + +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) { +  switch (version) { +  case NGTCP2_PROTO_VER_V2: +    switch (pkt_type) { +    case NGTCP2_PKT_INITIAL: +      return NGTCP2_PKT_TYPE_INITIAL_V2; +    case NGTCP2_PKT_0RTT: +      return NGTCP2_PKT_TYPE_0RTT_V2; +    case NGTCP2_PKT_HANDSHAKE: +      return NGTCP2_PKT_TYPE_HANDSHAKE_V2; +    case NGTCP2_PKT_RETRY: +      return NGTCP2_PKT_TYPE_RETRY_V2; +    default: +      ngtcp2_unreachable(); +    } +  default: +    /* Assume that unsupported versions share the numeric long packet +       types with QUIC v1 in order to send a packet to elicit Version +       Negotiation packet. */ + +    switch (pkt_type) { +    case NGTCP2_PKT_INITIAL: +      return NGTCP2_PKT_TYPE_INITIAL_V1; +    case NGTCP2_PKT_0RTT: +      return NGTCP2_PKT_TYPE_0RTT_V1; +    case NGTCP2_PKT_HANDSHAKE: +      return NGTCP2_PKT_TYPE_HANDSHAKE_V1; +    case NGTCP2_PKT_RETRY: +      return NGTCP2_PKT_TYPE_RETRY_V1; +    default: +      ngtcp2_unreachable(); +    } +  } +} + +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/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h new file mode 100644 index 00000000000..86ebecef7bc --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h @@ -0,0 +1,1228 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PKT_H +#define NGTCP2_PKT_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* QUIC header macros */ +#define NGTCP2_HEADER_FORM_BIT 0x80 +#define NGTCP2_FIXED_BIT_MASK 0x40 +#define NGTCP2_PKT_NUMLEN_MASK 0x03 + +/* Long header specific macros */ +#define NGTCP2_LONG_TYPE_MASK 0x30 +#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c + +/* Short header specific macros */ +#define NGTCP2_SHORT_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) + +/* STREAM frame specific macros */ +#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_DATAGRAM_OVERHEAD is the maximum number of bytes required +   other than payload for DATAGRAM frame.  That is from type field to +   the beginning of the payload. */ +#define NGTCP2_DATAGRAM_OVERHEAD (1 + 8) + +/* 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_RANGES is the maximum number of Additional ACK +   ranges which this library can create, or decode. */ +#define NGTCP2_MAX_ACK_RANGES 32 + +/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ +#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1)) + +/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion to +   hide/trigger Stateless Reset. */ +#define NGTCP2_MIN_PKT_EXPANDLEN 22 + +/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ +#define NGTCP2_RETRY_TAGLEN 16 + +/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP datagram +   payload size that this library can write. */ +#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1) + +/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by +   Length field in Long packet header. */ +#define NGTCP2_PKT_LENGTHLEN 4 + +/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for +   QUIC v1. */ +#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0 +/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC +   v1. */ +#define NGTCP2_PKT_TYPE_0RTT_V1 0x1 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type +   for QUIC v1. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2 +/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC +   v1. */ +#define NGTCP2_PKT_TYPE_RETRY_V1 0x3 + +/* NGTCP2_PKT_TYPE_INITIAL_V2 is Initial long header packet type for +   QUIC v2. */ +#define NGTCP2_PKT_TYPE_INITIAL_V2 0x1 +/* NGTCP2_PKT_TYPE_0RTT_V2 is 0RTT long header packet type for QUIC +   v2. */ +#define NGTCP2_PKT_TYPE_0RTT_V2 0x2 +/* NGTCP2_PKT_TYPE_HANDSHAKE_V2 is Handshake long header packet type +   for QUIC v2. */ +#define NGTCP2_PKT_TYPE_HANDSHAKE_V2 0x3 +/* NGTCP2_PKT_TYPE_RETRY_V2 is Retry long header packet type for QUIC +   v2. */ +#define NGTCP2_PKT_TYPE_RETRY_V2 0x0 + +typedef struct ngtcp2_pkt_retry { +  ngtcp2_cid odcid; +  uint8_t *token; +  size_t tokenlen; +  uint8_t tag[NGTCP2_RETRY_TAGLEN]; +} ngtcp2_pkt_retry; + +#define NGTCP2_FRAME_PADDING 0x00 +#define NGTCP2_FRAME_PING 0x01 +#define NGTCP2_FRAME_ACK 0x02 +#define NGTCP2_FRAME_ACK_ECN 0x03 +#define NGTCP2_FRAME_RESET_STREAM 0x04 +#define NGTCP2_FRAME_STOP_SENDING 0x05 +#define NGTCP2_FRAME_CRYPTO 0x06 +#define NGTCP2_FRAME_NEW_TOKEN 0x07 +#define NGTCP2_FRAME_STREAM 0x08 +#define NGTCP2_FRAME_MAX_DATA 0x10 +#define NGTCP2_FRAME_MAX_STREAM_DATA 0x11 +#define NGTCP2_FRAME_MAX_STREAMS_BIDI 0x12 +#define NGTCP2_FRAME_MAX_STREAMS_UNI 0x13 +#define NGTCP2_FRAME_DATA_BLOCKED 0x14 +#define NGTCP2_FRAME_STREAM_DATA_BLOCKED 0x15 +#define NGTCP2_FRAME_STREAMS_BLOCKED_BIDI 0x16 +#define NGTCP2_FRAME_STREAMS_BLOCKED_UNI 0x17 +#define NGTCP2_FRAME_NEW_CONNECTION_ID 0x18 +#define NGTCP2_FRAME_RETIRE_CONNECTION_ID 0x19 +#define NGTCP2_FRAME_PATH_CHALLENGE 0x1a +#define NGTCP2_FRAME_PATH_RESPONSE 0x1b +#define NGTCP2_FRAME_CONNECTION_CLOSE 0x1c +#define NGTCP2_FRAME_CONNECTION_CLOSE_APP 0x1d +#define NGTCP2_FRAME_HANDSHAKE_DONE 0x1e +#define NGTCP2_FRAME_DATAGRAM 0x30 +#define NGTCP2_FRAME_DATAGRAM_LEN 0x31 + +/* ngtcp2_stream represents STREAM and CRYPTO frames. */ +typedef struct ngtcp2_stream { +  uint64_t type; +  /** +   * flags of decoded STREAM frame.  This gets ignored when encoding +   * STREAM frame.  CRYPTO frame does not include this field, and must +   * set it to 0. +   */ +  uint8_t flags; +  /* CRYPTO frame does not include this field, and must set it to +     0. */ +  uint8_t fin; +  /* CRYPTO frame does not include this field, and must set it to +     0. */ +  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 ngtcp2_ack_range { +  uint64_t gap; +  uint64_t len; +} ngtcp2_ack_range; + +typedef struct ngtcp2_ack { +  uint64_t type; +  int64_t largest_ack; +  uint64_t ack_delay; +  /** +   * ack_delay_unscaled is an ack_delay multiplied by +   * 2**ack_delay_component * NGTCP2_MICROSECONDS. +   */ +  ngtcp2_duration ack_delay_unscaled; +  struct { +    uint64_t ect0; +    uint64_t ect1; +    uint64_t ce; +  } ecn; +  uint64_t first_ack_range; +  size_t rangecnt; +  ngtcp2_ack_range ranges[1]; +} ngtcp2_ack; + +typedef struct ngtcp2_padding { +  uint64_t type; +  /** +   * The length of contiguous PADDING frames. +   */ +  size_t len; +} ngtcp2_padding; + +typedef struct ngtcp2_reset_stream { +  uint64_t type; +  int64_t stream_id; +  uint64_t app_error_code; +  uint64_t final_size; +} ngtcp2_reset_stream; + +typedef struct ngtcp2_connection_close { +  uint64_t type; +  uint64_t error_code; +  uint64_t frame_type; +  size_t reasonlen; +  uint8_t *reason; +} ngtcp2_connection_close; + +typedef struct ngtcp2_max_data { +  uint64_t type; +  /** +   * max_data is Maximum Data. +   */ +  uint64_t max_data; +} ngtcp2_max_data; + +typedef struct ngtcp2_max_stream_data { +  uint64_t type; +  int64_t stream_id; +  uint64_t max_stream_data; +} ngtcp2_max_stream_data; + +typedef struct ngtcp2_max_streams { +  uint64_t type; +  uint64_t max_streams; +} ngtcp2_max_streams; + +typedef struct ngtcp2_ping { +  uint64_t type; +} ngtcp2_ping; + +typedef struct ngtcp2_data_blocked { +  uint64_t type; +  uint64_t offset; +} ngtcp2_data_blocked; + +typedef struct ngtcp2_stream_data_blocked { +  uint64_t type; +  int64_t stream_id; +  uint64_t offset; +} ngtcp2_stream_data_blocked; + +typedef struct ngtcp2_streams_blocked { +  uint64_t type; +  uint64_t max_streams; +} ngtcp2_streams_blocked; + +typedef struct ngtcp2_new_connection_id { +  uint64_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 ngtcp2_stop_sending { +  uint64_t type; +  int64_t stream_id; +  uint64_t app_error_code; +} ngtcp2_stop_sending; + +typedef struct ngtcp2_path_challenge { +  uint64_t type; +  uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; +} ngtcp2_path_challenge; + +typedef struct ngtcp2_path_response { +  uint64_t type; +  uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN]; +} ngtcp2_path_response; + +typedef struct ngtcp2_new_token { +  uint64_t type; +  uint8_t *token; +  size_t tokenlen; +} ngtcp2_new_token; + +typedef struct ngtcp2_retire_connection_id { +  uint64_t type; +  uint64_t seq; +} ngtcp2_retire_connection_id; + +typedef struct ngtcp2_handshake_done { +  uint64_t type; +} ngtcp2_handshake_done; + +typedef struct ngtcp2_datagram { +  uint64_t type; +  /* dgram_id is an opaque identifier chosen by an application. */ +  uint64_t dgram_id; +  /* datacnt is the number of elements that data contains. */ +  size_t datacnt; +  /* data is a pointer to ngtcp2_vec array that stores data. */ +  ngtcp2_vec *data; +  /* rdata is conveniently embedded to ngtcp2_datagram, so that data +     field can just point to the address of this field to store a +     single vector which is the case when DATAGRAM is received from a +     remote endpoint. */ +  ngtcp2_vec rdata[1]; +} ngtcp2_datagram; + +typedef union ngtcp2_frame { +  uint64_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_new_token new_token; +  ngtcp2_retire_connection_id retire_connection_id; +  ngtcp2_handshake_done handshake_done; +  ngtcp2_datagram datagram; +  /* Extend ngtcp2_frame so that ngtcp2_stream has at least additional +     3 ngtcp2_vec, totaling 4 slots, which can store HEADERS header, +     HEADERS payload, DATA header, and DATA payload in the standard +     sized ngtcp2_frame_chain. */ +  uint8_t pad[sizeof(ngtcp2_stream) + sizeof(ngtcp2_vec) * 3]; +} ngtcp2_frame; + +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_info pi; +  ngtcp2_pkt_chain *next; +  uint8_t *pkt; +  /* pktlen is length of a QUIC packet. */ +  size_t pktlen; +  /* dgramlen is length of UDP datagram payload that a QUIC packet is +     included. */ +  size_t dgramlen; +  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 ngtcp2_pkt_info *pi, const uint8_t *pkt, +                         size_t pktlen, size_t dgramlen, 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, Destination Connection ID and/or + * Source Connection ID 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 |out| 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 |out| 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; or + *     |payloadlen| is 0. + */ +ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, +                                     size_t payloadlen); + +/** + * @function + * + * `ngtcp2_pkt_encode_frame` encodes a frame |fr| 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.  It 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. + */ +ngtcp2_ssize 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 when 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. + */ +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 Connection ID 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_stream *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. + */ +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, +                                                    const uint8_t *payload, +                                                    size_t payloadlen); + +/* + * ngtcp2_pkt_decode_datagram_frame decodes DATAGRAM frame from + * |payload| of length |payloadlen|.  The result is stored in the + * object pointed by |dest|.  DATAGRAM frame must start at payload[0]. + * This function finishes when it decodes one DATAGRAM 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 DATAGRAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, +                                              const uint8_t *payload, +                                              size_t payloadlen); + +/* + * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function assigns <the serialized frame type> & + * ~NGTCP2_FRAME_STREAM to fr->flags. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + *     Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, +                                            ngtcp2_stream *fr); + +/* + * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer + * pointed by |out| of length |outlen|. + * + * This function 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, +                                         const 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_stream *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_encode_datagram_frame encodes DATAGRAM 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_datagram_frame(uint8_t *out, size_t outlen, +                                              const ngtcp2_datagram *fr); + +/* + * ngtcp2_pkt_adjust_pkt_num finds the full 62 bits packet number for + * |pkt_num|, which is encoded in |pkt_numlen| bytes.  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 pkt_numlen); + +/* + * ngtcp2_pkt_validate_ack verifies whether |fr| is malformed or not. + * |min_pkt_num| is the minimum packet number that a local endpoint + * sends.  It is an error to receive acknowledgements for a packet + * less than |min_pkt_num|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_ACK_FRAME + *     ACK frame is malformed + * NGTCP2_ERR_PROTO + *     |fr| contains a packet number less than |min_pkt_num|. + */ +int ngtcp2_pkt_validate_ack(const ngtcp2_ack *fr, int64_t min_pkt_num); + +/* + * 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 within the stream.  |len| is the estimated number of + * bytes to send.  |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, +                                     uint64_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 within + * the crypto stream.  |len| is the estimated number of bytes to send. + * |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_datagram_framelen returns the length of DATAGRAM frame + * to encode |len| bytes of data. + */ +size_t ngtcp2_pkt_datagram_framelen(size_t len); + +/* + * 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(uint32_t version, const ngtcp2_pkt_retry *retry, +                                const uint8_t *pkt, size_t pktlen, +                                ngtcp2_encrypt encrypt, +                                const ngtcp2_crypto_aead *aead, +                                const ngtcp2_crypto_aead_ctx *aead_ctx); + +/* + * ngtcp2_pkt_versioned_type returns versioned packet type for a + * version |version| that corresponds to the version-independent + * |pkt_type|. + */ +uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type); + +/** + * @function + * + * `ngtcp2_pkt_get_type_long` returns the version-independent long + * packet type.  |version| is the QUIC version.  |c| is the first byte + * of Long packet header.  If |version| is not supported by the + * library, it returns 0. + */ +uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c); + +#endif /* !defined(NGTCP2_PKT_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pktns_id.h b/contrib/libs/ngtcp2/lib/ngtcp2_pktns_id.h new file mode 100644 index 00000000000..9cf8444d32d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pktns_id.h @@ -0,0 +1,62 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_PKTNS_ID_H +#define NGTCP2_PKTNS_ID_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/** + * @enum + * + * :type:`ngtcp2_pktns_id` defines packet number space identifier. + */ +typedef enum ngtcp2_pktns_id { +  /** +   * :enum:`NGTCP2_PKTNS_ID_INITIAL` is the Initial packet number +   * space. +   */ +  NGTCP2_PKTNS_ID_INITIAL, +  /** +   * :enum:`NGTCP2_PKTNS_ID_HANDSHAKE` is the Handshake packet number +   * space. +   */ +  NGTCP2_PKTNS_ID_HANDSHAKE, +  /** +   * :enum:`NGTCP2_PKTNS_ID_APPLICATION` is the Application data +   * packet number space. +   */ +  NGTCP2_PKTNS_ID_APPLICATION, +  /** +   * :enum:`NGTCP2_PKTNS_ID_MAX` is defined to get the number of +   * packet number spaces. +   */ +  NGTCP2_PKTNS_ID_MAX +} ngtcp2_pktns_id; + +#endif /* !defined(NGTCP2_PKTNS_ID_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.c b/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.c new file mode 100644 index 00000000000..ebd113f6746 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.c @@ -0,0 +1,167 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_pmtud.h" + +#include <assert.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_macro.h" + +/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent +   for each probe. */ +#define NGTCP2_PMTUD_PROBE_NUM_MAX 3 + +static uint16_t pmtud_default_probes[] = { +  1454 - 48, /* The well known MTU used by a domestic optic fiber +                service in Japan. */ +  1390 - 48, /* Typical Tunneled MTU */ +  1280 - 48, /* IPv6 minimum MTU */ +  1492 - 48, /* PPPoE */ +}; + +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, +                     size_t hard_max_udp_payload_size, int64_t tx_pkt_num, +                     const uint16_t *probes, size_t probeslen, +                     const ngtcp2_mem *mem) { +  ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud)); + +  if (pmtud == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  pmtud->mem = mem; +  pmtud->mtu_idx = 0; +  pmtud->num_pkts_sent = 0; +  pmtud->expiry = UINT64_MAX; +  pmtud->tx_pkt_num = tx_pkt_num; +  pmtud->max_udp_payload_size = max_udp_payload_size; +  pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size; +  pmtud->min_fail_udp_payload_size = SIZE_MAX; + +  if (probeslen) { +    pmtud->probes = probes; +    pmtud->probeslen = probeslen; +  } else { +    pmtud->probes = pmtud_default_probes; +    pmtud->probeslen = ngtcp2_arraylen(pmtud_default_probes); +  } + +  for (; pmtud->mtu_idx < pmtud->probeslen; ++pmtud->mtu_idx) { +    if (pmtud->probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { +      continue; +    } +    if (pmtud->probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { +      break; +    } +  } + +  *ppmtud = pmtud; + +  return 0; +} + +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) { +  if (!pmtud) { +    return; +  } + +  ngtcp2_mem_free(pmtud->mem, pmtud); +} + +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) { +  assert(pmtud->mtu_idx < pmtud->probeslen); + +  return pmtud->probes[pmtud->mtu_idx]; +} + +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, +                             ngtcp2_tstamp ts) { +  ngtcp2_tstamp timeout; + +  if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { +    timeout = pto; +  } else { +    timeout = 3 * pto; +  } + +  pmtud->expiry = ts + timeout; +} + +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) { +  return pmtud->expiry == UINT64_MAX; +} + +static void pmtud_next_probe(ngtcp2_pmtud *pmtud) { +  assert(pmtud->mtu_idx < pmtud->probeslen); + +  ++pmtud->mtu_idx; +  pmtud->num_pkts_sent = 0; +  pmtud->expiry = UINT64_MAX; + +  for (; pmtud->mtu_idx < pmtud->probeslen; ++pmtud->mtu_idx) { +    if (pmtud->probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size || +        pmtud->probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) { +      continue; +    } + +    if (pmtud->probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) { +      break; +    } +  } +} + +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) { +  pmtud->max_udp_payload_size = +    ngtcp2_max_size(pmtud->max_udp_payload_size, payloadlen); + +  assert(pmtud->mtu_idx < pmtud->probeslen); + +  if (pmtud->probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) { +    return; +  } + +  pmtud_next_probe(pmtud); +} + +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) { +  if (ts < pmtud->expiry) { +    return; +  } + +  pmtud->expiry = UINT64_MAX; + +  if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) { +    return; +  } + +  pmtud->min_fail_udp_payload_size = ngtcp2_min_size( +    pmtud->min_fail_udp_payload_size, pmtud->probes[pmtud->mtu_idx]); + +  pmtud_next_probe(pmtud); +} + +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) { +  return pmtud->mtu_idx >= pmtud->probeslen; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.h b/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.h new file mode 100644 index 00000000000..53fc6a53829 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pmtud.h @@ -0,0 +1,132 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_PMTUD_H +#define NGTCP2_PMTUD_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_pmtud { +  const ngtcp2_mem *mem; +  /* mtu_idx is the index of UDP payload size candidates to try +     out. */ +  size_t mtu_idx; +  /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload +     sent */ +  size_t num_pkts_sent; +  /* expiry is the expired, if it is reached, send out the next UDP +     datagram.  UINT64_MAX means no expiry, or expiration is canceled. +     In either case, new probe packet should be sent unless we have +     done all attempts. */ +  ngtcp2_tstamp expiry; +  /* tx_pkt_num is the smallest outgoing packet number where the +     current discovery is performed.  In other words, acknowledging +     packet whose packet number lower than that does not indicate the +     success of Path MTU Discovery. */ +  int64_t tx_pkt_num; +  /* max_udp_payload_size is the maximum UDP payload size which is +     known to work. */ +  size_t max_udp_payload_size; +  /* hard_max_udp_payload_size is the maximum UDP payload size that is +     going to be probed. */ +  size_t hard_max_udp_payload_size; +  /* min_fail_udp_payload_size is the minimum UDP payload size that is +     known to fail. */ +  size_t min_fail_udp_payload_size; +  /* probes is the array of UDP datagram payload size to probe. */ +  const uint16_t *probes; +  /* probeslen is the number of probes pointed by probes. */ +  size_t probeslen; +} ngtcp2_pmtud; + +/* + * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its + * pointer to |*ppmtud|.  |max_udp_payload_size| is the maximum UDP + * payload size that is known to work for the current path. + * |tx_pkt_num| should be the next packet number to send, which is + * used to differentiate the PMTUD probe packet sent by the previous + * PMTUD.  PMTUD might finish immediately if |max_udp_payload_size| is + * larger than or equal to all UDP payload probe candidates. + * Therefore, call ngtcp2_pmtud_finished to check this situation. + * + * The array pointed by |pmtud_probes| of length |pmtud_probeslen| + * specifies UDP datagram payload size to probe.  If |pmtud_probeslen| + * is zero, the default probes are used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size, +                     size_t hard_max_udp_payload_size, int64_t tx_pkt_num, +                     const uint16_t *pmtud_probes, size_t pmtud_probeslen, +                     const ngtcp2_mem *mem); + +/* + * ngtcp2_pmtud_del deletes |pmtud|. + */ +void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probelen returns the length of UDP payload size for a + * PMTUD probe packet. + */ +size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is + * sent. + */ +void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto, +                             ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet + * should be sent. + */ +int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud); + +/* + * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe + * UDP datagram sized |payloadlen| is acknowledged. + */ +void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen); + +/* + * ngtcp2_pmtud_handle_expiry handles expiry. + */ +void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts); + +/* + * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished. + */ +int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud); + +#endif /* !defined(NGTCP2_PMTUD_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_ppe.c b/contrib/libs/ngtcp2/lib/ngtcp2_ppe.c new file mode 100644 index 00000000000..13ef2b24908 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ppe.c @@ -0,0 +1,243 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ppe.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_macro.h" + +void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, +                     size_t dgram_offset, ngtcp2_crypto_cc *cc) { +  ngtcp2_buf_init(&ppe->buf, out, outlen); + +  ppe->dgram_offset = dgram_offset; +  ppe->hdlen = 0; +  ppe->len_offset = 0; +  ppe->pkt_num_offset = 0; +  ppe->pkt_numlen = 0; +  ppe->pkt_num = 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.max_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_uvarintlen(hd->tokenlen) + hd->tokenlen; +    } + +    ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; + +    rv = ngtcp2_pkt_encode_hd_long( +      buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); +  } else { +    ppe->pkt_num_offset = 1 + hd->dcid.datalen; + +    rv = ngtcp2_pkt_encode_hd_short( +      buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); +  } + +  if (rv < 0) { +    return (int)rv; +  } + +  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.max_overhead) { +    return NGTCP2_ERR_NOBUF; +  } + +  rv = ngtcp2_pkt_encode_frame( +    buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr); +  if (rv < 0) { +    return (int)rv; +  } + +  buf->last += rv; + +  return 0; +} + +/* + * ppe_sample_offset returns the offset to sample for packet number + * encryption. + */ +static size_t ppe_sample_offset(ngtcp2_ppe *ppe) { +  return ppe->pkt_num_offset + 4; +} + +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_uvarint30( +      buf->begin + ppe->len_offset, +      (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead)); +  } + +  ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len, +                             ppe->pkt_num); + +  rv = cc->encrypt(payload, &cc->aead, &cc->ckm->aead_ctx, payload, payloadlen, +                   ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen); +  if (rv != 0) { +    return NGTCP2_ERR_CALLBACK_FAILURE; +  } + +  buf->last = payload + payloadlen + cc->aead.max_overhead; + +  /* Make sure that we have enough space to get sample */ +  assert(ppe_sample_offset(ppe) + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf)); + +  rv = cc->hp_mask(mask, &cc->hp, &cc->hp_ctx, +                   buf->begin + ppe_sample_offset(ppe)); +  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(const ngtcp2_ppe *ppe) { +  ngtcp2_crypto_cc *cc = ppe->cc; + +  if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) { +    return 0; +  } + +  return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead; +} + +size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe) { +  ngtcp2_crypto_cc *cc = ppe->cc; + +  return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead; +} + +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.max_overhead; +  size_t len = 0; +  size_t max_samplelen; + +  n = ngtcp2_min_size(n, ngtcp2_buf_cap(buf)); +  if (pktlen < n) { +    len = n - pktlen; +  } + +  /* Ensure header protection sample */ +  max_samplelen = +    ngtcp2_buf_len(buf) + cc->aead.max_overhead - ppe_sample_offset(ppe); + +  if (max_samplelen < NGTCP2_HP_SAMPLELEN) { +    len = ngtcp2_max_size(len, NGTCP2_HP_SAMPLELEN - max_samplelen); +  } + +  assert(ngtcp2_buf_left(buf) >= len + cc->aead.max_overhead); + +  buf->last = ngtcp2_setmem(buf->last, 0, len); + +  return len; +} + +size_t ngtcp2_ppe_dgram_padding(ngtcp2_ppe *ppe) { +  return ngtcp2_ppe_dgram_padding_size(ppe, NGTCP2_MAX_UDP_PAYLOAD_SIZE); +} + +size_t ngtcp2_ppe_dgram_padding_size(ngtcp2_ppe *ppe, size_t n) { +  ngtcp2_crypto_cc *cc = ppe->cc; +  ngtcp2_buf *buf = &ppe->buf; +  size_t dgramlen = +    ppe->dgram_offset + ngtcp2_buf_len(buf) + cc->aead.max_overhead; +  size_t len; + +  n = ngtcp2_min_size(n, ppe->dgram_offset + ngtcp2_buf_cap(buf)); + +  if (dgramlen >= n) { +    return 0; +  } + +  len = n - dgramlen; +  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/contrib/libs/ngtcp2/lib/ngtcp2_ppe.h b/contrib/libs/ngtcp2/lib/ngtcp2_ppe.h new file mode 100644 index 00000000000..660b1482b56 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ppe.h @@ -0,0 +1,163 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PPE_H +#define NGTCP2_PPE_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_crypto.h" + +/* + * ngtcp2_ppe is the QUIC Packet Encoder. + */ +typedef struct ngtcp2_ppe { +  /* buf is the buffer where a QUIC packet is written. */ +  ngtcp2_buf buf; +  /* cc is the encryption context that includes callback functions to +     encrypt a QUIC packet, and AEAD cipher, etc. */ +  ngtcp2_crypto_cc *cc; +  /* dgram_offset is the offset in UDP datagram payload that this QUIC +     packet is positioned at. */ +  size_t dgram_offset; +  /* 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; +  /* 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 the 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, +                     size_t dgram_offset, 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(const ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_pktlen returns the provisional packet length.  It + * includes AEAD overhead. + */ +size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_dgram_padding is equivalent to call + * ngtcp2_ppe_dgram_padding_size(ppe, NGTCP2_MAX_UDP_PAYLOAD_SIZE). + * This function should be called just before calling + * ngtcp2_ppe_final(). + * + * This function returns the number of bytes padded. + */ +size_t ngtcp2_ppe_dgram_padding(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_dgram_padding_size adds PADDING frame so that the size + * of a UDP datagram payload 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(). + * + * This function returns the number of bytes added as padding. + */ +size_t ngtcp2_ppe_dgram_padding_size(ngtcp2_ppe *ppe, size_t n); + +/* + * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC + * packet is at least |n| bytes long and the current payload has + * enough space for header protection sample.  If it is unable to add + * PADDING at least |n| bytes, this function still adds PADDING frames + * as much as possible.  This function also adds PADDING frames so + * that the minimum padding requirement of header protection is met. + * Those padding may be larger than |n| bytes.  It is recommended to + * make sure that ngtcp2_ppe_ensure_hp_sample succeeds after writing + * QUIC packet header.  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_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 /* !defined(NGTCP2_PPE_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pq.c b/contrib/libs/ngtcp2/lib/ngtcp2_pq.c new file mode 100644 index 00000000000..19e3e3e36aa --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pq.c @@ -0,0 +1,179 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pq.h" + +#include <assert.h> + +#include "ngtcp2_macro.h" + +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_pq_less less, const ngtcp2_mem *mem) { +  pq->q = NULL; +  pq->mem = mem; +  pq->length = 0; +  pq->capacity = 0; +  pq->less = less; +} + +void ngtcp2_pq_free(ngtcp2_pq *pq) { +  if (!pq) { +    return; +  } + +  ngtcp2_mem_free(pq->mem, pq->q); +} + +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) { +    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_size(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, item->index); + +  return 0; +} + +ngtcp2_pq_entry *ngtcp2_pq_top(const 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) { +  assert(pq->length); + +  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(const ngtcp2_pq *pq) { return pq->length == 0; } + +size_t ngtcp2_pq_size(const ngtcp2_pq *pq) { return pq->length; } + +int ngtcp2_pq_each(const 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/contrib/libs/ngtcp2/lib/ngtcp2_pq.h b/contrib/libs/ngtcp2/lib/ngtcp2_pq.h new file mode 100644 index 00000000000..84961c91439 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pq.h @@ -0,0 +1,129 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PQ_H +#define NGTCP2_PQ_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* Implementation of priority queue */ + +/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates +   that an entry is not queued.  Assigning this value to +   ngtcp2_pq_entry.index can check that the entry is queued or not. */ +#define NGTCP2_PQ_BAD_INDEX SIZE_MAX + +typedef struct ngtcp2_pq_entry { +  size_t index; +} ngtcp2_pq_entry; + +/* ngtcp2_pq_less is a "less" function, that returns nonzero if |lhs| +   is considered to be less than |rhs|. */ +typedef int (*ngtcp2_pq_less)(const ngtcp2_pq_entry *lhs, +                              const ngtcp2_pq_entry *rhs); + +typedef struct ngtcp2_pq { +  /* q is a pointer to an array that stores the items. */ +  ngtcp2_pq_entry **q; +  /* mem is a memory allocator. */ +  const ngtcp2_mem *mem; +  /* length is the number of items stored. */ +  size_t length; +  /* capacity is the maximum number of items this queue can store. +     This is automatically extended when length is reached to this +     limit. */ +  size_t capacity; +  /* less is the less function to compare items. */ +  ngtcp2_pq_less less; +} ngtcp2_pq; + +/* + * ngtcp2_pq_init initializes |pq| with compare function |cmp|. + */ +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_pq_less less, const ngtcp2_mem *mem); + +/* + * ngtcp2_pq_free deallocates any resources allocated for |pq|.  The + * stored items are not freed by this function. + */ +void ngtcp2_pq_free(ngtcp2_pq *pq); + +/* + * ngtcp2_pq_push adds |item| to |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); + +/* + * ngtcp2_pq_top returns item at the top of |pq|.  It is undefined if + * |pq| is empty. + */ +ngtcp2_pq_entry *ngtcp2_pq_top(const ngtcp2_pq *pq); + +/* + * ngtcp2_pq_pop pops item at the top of |pq|.  The popped item is not + * freed by this function.  It is undefined if |pq| is empty. + */ +void ngtcp2_pq_pop(ngtcp2_pq *pq); + +/* + * ngtcp2_pq_empty returns nonzero if |pq| is empty. + */ +int ngtcp2_pq_empty(const ngtcp2_pq *pq); + +/* + * ngtcp2_pq_size returns the number of items |pq| contains. + */ +size_t ngtcp2_pq_size(const ngtcp2_pq *pq); + +typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg); + +/* + * ngtcp2_pq_each applies |fun| to each item in |pq|.  The |arg| is + * passed as arg parameter to callback function.  This function must + * not change the ordering key.  If the return value from callback is + * nonzero, this function returns 1 immediately without iterating + * remaining items.  Otherwise this function returns 0. + */ +int ngtcp2_pq_each(const ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg); + +/* + * ngtcp2_pq_remove removes |item| from |pq|.  |pq| must contain + * |item| otherwise the behavior is undefined. + */ +void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item); + +#endif /* !defined(NGTCP2_PQ_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pv.c b/contrib/libs/ngtcp2/lib/ngtcp2_pv.c new file mode 100644 index 00000000000..e4fee94eb55 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pv.c @@ -0,0 +1,172 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pv.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_addr.h" + +void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, +                          ngtcp2_tstamp expiry, uint8_t flags) { +  memcpy(pvent->data, data, sizeof(pvent->data)); +  pvent->expiry = expiry; +  pvent->flags = flags; +} + +int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, +                  ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, +                  const ngtcp2_mem *mem) { +  (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); +  if (*ppv == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents); + +  ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); + +  (*ppv)->mem = mem; +  (*ppv)->log = log; +  (*ppv)->timeout = timeout; +  (*ppv)->fallback_pto = 0; +  (*ppv)->started_ts = UINT64_MAX; +  (*ppv)->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT; +  (*ppv)->round = 0; +  (*ppv)->flags = flags; + +  return 0; +} + +void ngtcp2_pv_del(ngtcp2_pv *pv) { +  if (pv == NULL) { +    return; +  } +  ngtcp2_mem_free(pv->mem, pv); +} + +void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, +                         ngtcp2_tstamp expiry, uint8_t flags, +                         ngtcp2_tstamp ts) { +  ngtcp2_pv_entry *ent; + +  assert(pv->probe_pkt_left); + +  if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { +    pv->started_ts = ts; +  } + +  ent = ngtcp2_ringbuf_push_back(&pv->ents.rb); +  ngtcp2_pv_entry_init(ent, data, expiry, flags); + +  pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER; +  --pv->probe_pkt_left; +} + +int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { +  size_t len = ngtcp2_ringbuf_len(&pv->ents.rb); +  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.rb, i); +    if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { +      *pflags = ent->flags; +      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.rb) == 0) { +    return; +  } + +  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); + +  if (ent->expiry > ts) { +    return; +  } + +  ++pv->round; +  pv->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT; +} + +int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv) { +  return pv->probe_pkt_left > 0; +} + +int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { +  ngtcp2_tstamp t; +  ngtcp2_pv_entry *ent; + +  if (pv->started_ts == UINT64_MAX) { +    return 0; +  } + +  assert(ngtcp2_ringbuf_len(&pv->ents.rb)); + +  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); + +  t = pv->started_ts + pv->timeout; +  t = ngtcp2_max_uint64(t, ent->expiry); + +  return t <= ts; +} + +ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { +  ngtcp2_pv_entry *ent; + +  if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) || +      ngtcp2_ringbuf_len(&pv->ents.rb) == 0) { +    return UINT64_MAX; +  } + +  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1); + +  return ent->expiry; +} + +void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts) { +  ngtcp2_tstamp expiry = ngtcp2_pv_next_expiry(pv); + +  if (expiry > ts) { +    return; +  } + +  pv->flags |= NGTCP2_PV_FLAG_CANCEL_TIMER; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_pv.h b/contrib/libs/ngtcp2/lib/ngtcp2_pv.h new file mode 100644 index 00000000000..7ffa623d17e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_pv.h @@ -0,0 +1,194 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PV_H +#define NGTCP2_PV_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_cid.h" +#include "ngtcp2_ringbuf.h" + +/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that +   ngtcp2_pv can contain.  It must be power of 2. */ +#define NGTCP2_PV_MAX_ENTRIES 8 +/* NGTCP2_PV_NUM_PROBE_PKT is the number of probe packets containing +   PATH_CHALLENGE sent at a time. */ +#define NGTCP2_PV_NUM_PROBE_PKT 2 + +typedef struct ngtcp2_log ngtcp2_log; + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u +/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which +   contains PATH_CHALLENGE is undersized (< 1200 bytes) */ +#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u + +typedef struct ngtcp2_pv_entry { +  /* expiry is the timestamp when this PATH_CHALLENGE expires. */ +  ngtcp2_tstamp expiry; +  /* flags is zero or more of NGTCP2_PV_ENTRY_FLAG_*. */ +  uint8_t flags; +  /* 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, uint8_t flags); + +/* NGTCP2_PV_FLAG_NONE indicates no flag is set. */ +#define NGTCP2_PV_FLAG_NONE 0x00u +/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path +   validation should be ignored entirely. */ +#define NGTCP2_PV_FLAG_DONT_CARE 0x01u +/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is +   cancelled. */ +#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u +/* 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. */ +#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u +/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to +   server's preferred address.  This flag is only used by client. */ +#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u + +typedef struct ngtcp2_pv ngtcp2_pv; + +ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES, +                          sizeof(ngtcp2_pv_entry)); +/* + * 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_static_ringbuf_pv_ents ents; +  /* timeout is the duration within which this path validation should +     succeed. */ +  ngtcp2_duration timeout; +  /* fallback_pto is PTO of fallback connection. */ +  ngtcp2_duration fallback_pto; +  /* started_ts is the timestamp this path validation starts. */ +  ngtcp2_tstamp started_ts; +  /* round is the number of times that probe_pkt_left is reset. */ +  size_t round; +  /* probe_pkt_left is the number of probe packets containing +     PATH_CHALLENGE which can be send without waiting for an +     expiration of a previous flight. */ +  size_t probe_pkt_left; +  /* 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_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, uint8_t flags, ngtcp2_tstamp ts); + +/* + * 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.  The flag of ngtcp2_pv_entry that + * matches |data| is assigned to |*pflags| if this function succeeds. + * + * 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, uint8_t *pflags, const uint8_t *data); + +/* + * ngtcp2_pv_handle_entry_expiry checks expiry of existing entries. + */ +void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_should_send_probe returns nonzero if new entry can be + * added by ngtcp2_pv_add_entry. + */ +int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv); + +/* + * 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); + +/* + * ngtcp2_pv_cancel_expired_timer cancels the expired timer. + */ +void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +#endif /* !defined(NGTCP2_PV_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_qlog.c b/contrib/libs/ngtcp2/lib/ngtcp2_qlog.c new file mode 100644 index 00000000000..c0f920746a4 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_qlog.c @@ -0,0 +1,1223 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_qlog.h" + +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" +#include "ngtcp2_unreachable.h" +#include "ngtcp2_conn_stat.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; +} + +#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), sizeof(S) - 1) + +static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data, +                                  size_t datalen) { +  *p++ = '"'; +  if (datalen) { +    p = ngtcp2_cpymem(p, data, datalen); +  } +  *p++ = '"'; +  return p; +} + +#define write_string(DEST, S)                                                  \ +  write_string_impl((DEST), (const uint8_t *)(S), sizeof(S) - 1) + +#define NGTCP2_LOWER_XDIGITS "0123456789abcdef" + +static uint8_t *write_hex(uint8_t *p, const uint8_t *data, size_t datalen) { +  const uint8_t *b = data, *end = data + datalen; +  *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) { +  return write_hex(p, 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_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_impl(uint8_t *p, const uint8_t *name, size_t namelen, +                                const ngtcp2_vec *value) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_string_impl(p, value->base, value->len); +} + +#define write_pair(DEST, NAME, VALUE)                                          \ +  write_pair_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, (VALUE)) + +static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name, +                                    size_t namelen, const uint8_t *value, +                                    size_t valuelen) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_hex(p, value, valuelen); +} + +#define write_pair_hex(DEST, NAME, VALUE, VALUELEN)                            \ +  write_pair_hex_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,       \ +                      (VALUE), (VALUELEN)) + +static uint8_t *write_pair_number_impl(uint8_t *p, const uint8_t *name, +                                       size_t namelen, uint64_t value) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_number(p, value); +} + +#define write_pair_number(DEST, NAME, VALUE)                                   \ +  write_pair_number_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,    \ +                         (VALUE)) + +static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name, +                                         size_t namelen, +                                         ngtcp2_duration duration) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_duration(p, duration); +} + +#define write_pair_duration(DEST, NAME, VALUE)                                 \ +  write_pair_duration_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,  \ +                           (VALUE)) + +static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name, +                                       size_t namelen, ngtcp2_tstamp ts) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_tstamp(p, ts); +} + +#define write_pair_tstamp(DEST, NAME, VALUE)                                   \ +  write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,    \ +                         (VALUE)) + +static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name, +                                     size_t namelen, int b) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_bool(p, b); +} + +#define write_pair_bool(DEST, NAME, VALUE)                                     \ +  write_pair_bool_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,      \ +                       (VALUE)) + +static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name, +                                    size_t namelen, const ngtcp2_cid *cid) { +  p = write_string_impl(p, name, namelen); +  *p++ = ':'; +  return write_cid(p, cid); +} + +#define write_pair_cid(DEST, NAME, VALUE)                                      \ +  write_pair_cid_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1,       \ +                      (VALUE)) + +#define ngtcp2_make_vec_lit(S) {(uint8_t *)(S), sizeof((S)) - 1} + +static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { +  p = write_verbatim( +    p, "\"common_fields\":{\"protocol_type\":[\"QUIC\"],\"time_format\":" +       "\"relative\",\"reference_time\":0,\"group_id\":"); +  p = write_cid(p, odcid); +  *p++ = '}'; +  return p; +} + +static uint8_t *write_trace(uint8_t *p, int server, const ngtcp2_cid *odcid) { +  p = write_verbatim( +    p, "\"trace\":{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":"); +  if (server) { +    p = write_string(p, "server"); +  } else { +    p = write_string(p, "client"); +  } +  p = write_verbatim(p, "},"); +  p = write_common_fields(p, odcid); +  *p++ = '}'; +  return p; +} + +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { +  uint8_t buf[1024]; +  uint8_t *p = buf; + +  if (!qlog->write) { +    return; +  } + +  p = write_verbatim( +    p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\","); +  p = write_trace(p, server, odcid); +  p = write_verbatim(p, "}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, +              (size_t)(p - buf)); +} + +void ngtcp2_qlog_end(ngtcp2_qlog *qlog) { +  uint8_t buf[1] = {0}; + +  if (!qlog->write) { +    return; +  } + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, &buf, 0); +} + +static ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial"); +static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake"); +static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT"); +static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT"); +static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry"); +static ngtcp2_vec vec_pkt_type_version_negotiation = +  ngtcp2_make_vec_lit("version_negotiation"); +static ngtcp2_vec vec_pkt_type_stateless_reset = +  ngtcp2_make_vec_lit("stateless_reset"); +static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown"); + +static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { +  if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { +    switch (hd->type) { +    case NGTCP2_PKT_INITIAL: +      return &vec_pkt_type_initial; +    case NGTCP2_PKT_HANDSHAKE: +      return &vec_pkt_type_handshake; +    case NGTCP2_PKT_0RTT: +      return &vec_pkt_type_0rtt; +    case NGTCP2_PKT_RETRY: +      return &vec_pkt_type_retry; +    default: +      return &vec_pkt_type_unknown; +    } +  } + +  switch (hd->type) { +  case NGTCP2_PKT_VERSION_NEGOTIATION: +    return &vec_pkt_type_version_negotiation; +  case NGTCP2_PKT_STATELESS_RESET: +    return &vec_pkt_type_stateless_reset; +  case NGTCP2_PKT_1RTT: +    return &vec_pkt_type_1rtt; +  default: +    return &vec_pkt_type_unknown; +  } +} + +static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) { +  /* +   * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}} +   */ +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95 + +  *p++ = '{'; +  p = write_pair(p, "packet_type", qlog_pkt_type(hd)); +  *p++ = ','; +  p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num); +  if (hd->type == NGTCP2_PKT_INITIAL && hd->tokenlen) { +    p = write_verbatim(p, ",\"token\":{"); +    p = write_pair_hex(p, "data", hd->token, hd->tokenlen); +    *p++ = '}'; +  } +  /* TODO Write DCIL and DCID */ +  /* TODO Write SCIL and SCID */ +  *p++ = '}'; +  return p; +} + +static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) { +  (void)fr; + +  /* {"frame_type":"padding"} */ +#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24 + +  return write_verbatim(p, "{\"frame_type\":\"padding\"}"); +} + +static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) { +  (void)fr; + +  /* {"frame_type":"ping"} */ +#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21 + +  return write_verbatim(p, "{\"frame_type\":\"ping\"}"); +} + +static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { +  int64_t largest_ack, min_ack; +  size_t i; +  const ngtcp2_ack_range *range; + +  /* +   * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]} +   * +   * each range: +   * [0000000000000000000,0000000000000000000], +   * +   * ecn: +   * ,"ect1":0000000000000000000,"ect0":0000000000000000000,"ce":0000000000000000000 +   */ +#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70 +#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 42 +#define NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD 79 + +  p = write_verbatim(p, "{\"frame_type\":\"ack\","); +  p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled); +  p = write_verbatim(p, ",\"acked_ranges\":["); + +  largest_ack = fr->largest_ack; +  min_ack = fr->largest_ack - (int64_t)fr->first_ack_range; + +  *p++ = '['; +  p = write_number(p, (uint64_t)min_ack); +  if (largest_ack != min_ack) { +    *p++ = ','; +    p = write_number(p, (uint64_t)largest_ack); +  } +  *p++ = ']'; + +  for (i = 0; i < fr->rangecnt; ++i) { +    range = &fr->ranges[i]; +    largest_ack = min_ack - (int64_t)range->gap - 2; +    min_ack = largest_ack - (int64_t)range->len; +    *p++ = ','; +    *p++ = '['; +    p = write_number(p, (uint64_t)min_ack); +    if (largest_ack != min_ack) { +      *p++ = ','; +      p = write_number(p, (uint64_t)largest_ack); +    } +    *p++ = ']'; +  } + +  *p++ = ']'; + +  if (fr->type == NGTCP2_FRAME_ACK_ECN) { +    *p++ = ','; +    p = write_pair_number(p, "ect1", fr->ecn.ect1); +    *p++ = ','; +    p = write_pair_number(p, "ect0", fr->ecn.ect0); +    *p++ = ','; +    p = write_pair_number(p, "ce", fr->ecn.ce); +  } + +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_reset_stream_frame(uint8_t *p, +                                         const ngtcp2_reset_stream *fr) { +  /* +   * {"frame_type":"reset_stream","stream_id":0000000000000000000,"error_code":0000000000000000000,"final_size":0000000000000000000} +   */ +#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 127 + +  p = write_verbatim(p, "{\"frame_type\":\"reset_stream\","); +  p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); +  *p++ = ','; +  p = write_pair_number(p, "error_code", fr->app_error_code); +  *p++ = ','; +  p = write_pair_number(p, "final_size", fr->final_size); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_stop_sending_frame(uint8_t *p, +                                         const ngtcp2_stop_sending *fr) { +  /* +   * {"frame_type":"stop_sending","stream_id":0000000000000000000,"error_code":0000000000000000000} +   */ +#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 94 + +  p = write_verbatim(p, "{\"frame_type\":\"stop_sending\","); +  p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); +  *p++ = ','; +  p = write_pair_number(p, "error_code", fr->app_error_code); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_stream *fr) { +  /* +   * {"frame_type":"crypto","offset":0000000000000000000,"length":0000000000000000000} +   */ +#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 81 + +  p = write_verbatim(p, "{\"frame_type\":\"crypto\","); +  p = write_pair_number(p, "offset", fr->offset); +  *p++ = ','; +  p = write_pair_number(p, "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) { +  /* +   * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}} +   */ +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75 + +  p = write_verbatim(p, "{\"frame_type\":\"new_token\","); +  p = write_pair_number(p, "length", fr->tokenlen); +  p = write_verbatim(p, ",\"token\":{"); +  p = write_pair_hex(p, "data", fr->token, fr->tokenlen); +  *p++ = '}'; +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) { +  /* +   * {"frame_type":"stream","stream_id":0000000000000000000,"offset":0000000000000000000,"length":0000000000000000000,"fin":true} +   */ +#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 124 + +  p = write_verbatim(p, "{\"frame_type\":\"stream\","); +  p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); +  *p++ = ','; +  p = write_pair_number(p, "offset", fr->offset); +  *p++ = ','; +  p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt)); +  if (fr->fin) { +    *p++ = ','; +    p = write_pair_bool(p, "fin", 1); +  } +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) { +  /* +   * {"frame_type":"max_data","maximum":0000000000000000000} +   */ +#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 55 + +  p = write_verbatim(p, "{\"frame_type\":\"max_data\","); +  p = write_pair_number(p, "maximum", fr->max_data); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_max_stream_data_frame(uint8_t *p, +                                            const ngtcp2_max_stream_data *fr) { +  /* +   * {"frame_type":"max_stream_data","stream_id":0000000000000000000,"maximum":0000000000000000000} +   */ +#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 94 + +  p = write_verbatim(p, "{\"frame_type\":\"max_stream_data\","); +  p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); +  *p++ = ','; +  p = write_pair_number(p, "maximum", fr->max_stream_data); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_max_streams_frame(uint8_t *p, +                                        const ngtcp2_max_streams *fr) { +  /* +   * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":0000000000000000000} +   */ +#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89 + +  p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":"); +  if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { +    p = write_string(p, "bidirectional"); +  } else { +    p = write_string(p, "unidirectional"); +  } +  *p++ = ','; +  p = write_pair_number(p, "maximum", fr->max_streams); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_data_blocked_frame(uint8_t *p, +                                         const ngtcp2_data_blocked *fr) { +  /* +   * {"frame_type":"data_blocked","limit":0000000000000000000} +   */ +#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 57 + +  p = write_verbatim(p, "{\"frame_type\":\"data_blocked\","); +  p = write_pair_number(p, "limit", fr->offset); +  *p++ = '}'; + +  return p; +} + +static uint8_t * +write_stream_data_blocked_frame(uint8_t *p, +                                const ngtcp2_stream_data_blocked *fr) { +  /* +   * {"frame_type":"stream_data_blocked","stream_id":0000000000000000000,"limit":0000000000000000000} +   */ +#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 96 + +  p = write_verbatim(p, "{\"frame_type\":\"stream_data_blocked\","); +  p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); +  *p++ = ','; +  p = write_pair_number(p, "limit", fr->offset); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_streams_blocked_frame(uint8_t *p, +                                            const ngtcp2_streams_blocked *fr) { +  /* +   * {"frame_type":"streams_blocked","stream_type":"unidirectional","limit":0000000000000000000} +   */ +#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 91 + +  p = write_verbatim(p, "{\"frame_type\":\"streams_blocked\",\"stream_type\":"); +  if (fr->type == NGTCP2_FRAME_STREAMS_BLOCKED_BIDI) { +    p = write_string(p, "bidirectional"); +  } else { +    p = write_string(p, "unidirectional"); +  } +  *p++ = ','; +  p = write_pair_number(p, "limit", fr->max_streams); +  *p++ = '}'; + +  return p; +} + +static uint8_t * +write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { +  /* +   * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}} +   */ +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280 + +  p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\","); +  p = write_pair_number(p, "sequence_number", fr->seq); +  *p++ = ','; +  p = write_pair_number(p, "retire_prior_to", fr->retire_prior_to); +  *p++ = ','; +  p = write_pair_number(p, "connection_id_length", fr->cid.datalen); +  *p++ = ','; +  p = write_pair_cid(p, "connection_id", &fr->cid); +  p = write_verbatim(p, ",\"stateless_reset_token\":{"); +  p = write_pair_hex(p, "data", fr->stateless_reset_token, +                     sizeof(fr->stateless_reset_token)); +  *p++ = '}'; +  *p++ = '}'; + +  return p; +} + +static uint8_t * +write_retire_connection_id_frame(uint8_t *p, +                                 const ngtcp2_retire_connection_id *fr) { +  /* +   * {"frame_type":"retire_connection_id","sequence_number":0000000000000000000} +   */ +#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 75 + +  p = write_verbatim(p, "{\"frame_type\":\"retire_connection_id\","); +  p = write_pair_number(p, "sequence_number", fr->seq); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_path_challenge_frame(uint8_t *p, +                                           const ngtcp2_path_challenge *fr) { +  /* +   * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"} +   */ +#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57 + +  p = write_verbatim(p, "{\"frame_type\":\"path_challenge\","); +  p = write_pair_hex(p, "data", fr->data, sizeof(fr->data)); +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_path_response_frame(uint8_t *p, +                                          const ngtcp2_path_response *fr) { +  /* +   * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"} +   */ +#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56 + +  p = write_verbatim(p, "{\"frame_type\":\"path_response\","); +  p = write_pair_hex(p, "data", fr->data, sizeof(fr->data)); +  *p++ = '}'; + +  return p; +} + +static uint8_t * +write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { +  /* +   * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000} +   */ +#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131 + +  p = +    write_verbatim(p, "{\"frame_type\":\"connection_close\",\"error_space\":"); +  if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { +    p = write_string(p, "transport"); +  } else { +    p = write_string(p, "application"); +  } +  *p++ = ','; +  p = write_pair_number(p, "error_code", fr->error_code); +  *p++ = ','; +  p = write_pair_number(p, "raw_error_code", fr->error_code); +  /* TODO Write reason by escaping non-printables */ +  /* TODO Write trigger_frame_type */ +  *p++ = '}'; + +  return p; +} + +static uint8_t *write_handshake_done_frame(uint8_t *p, +                                           const ngtcp2_handshake_done *fr) { +  (void)fr; + +  /* +   * {"frame_type":"handshake_done"} +   */ +#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31 + +  return write_verbatim(p, "{\"frame_type\":\"handshake_done\"}"); +} + +static uint8_t *write_datagram_frame(uint8_t *p, const ngtcp2_datagram *fr) { +  /* +   * {"frame_type":"datagram","length":0000000000000000000} +   */ +#define NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD 54 + +  p = write_verbatim(p, "{\"frame_type\":\"datagram\","); +  p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt)); +  *p++ = '}'; + +  return p; +} + +static uint8_t *qlog_write_time(ngtcp2_qlog *qlog, uint8_t *p) { +  return write_pair_tstamp(p, "time", qlog->last_ts - qlog->ts); +} + +static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) { +  uint8_t *p; + +  if (!qlog->write) { +    return; +  } + +  ngtcp2_buf_reset(&qlog->buf); +  p = qlog->buf.last; + +  *p++ = '\x1e'; +  *p++ = '{'; +  p = qlog_write_time(qlog, p); +  p = write_verbatim(p, ",\"name\":"); +  if (sent) { +    p = write_string(p, "transport:packet_sent"); +  } else { +    p = write_string(p, "transport:packet_received"); +  } +  p = write_verbatim(p, ",\"data\":{\"frames\":["); +  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; + +  if (!qlog->write) { +    return; +  } + +  /* +   * ],"header":,"raw":{"length":0000000000000000000}}} +   * +   * plus, terminating LF +   */ +#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD                                     \ +  (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + +  if (ngtcp2_buf_left(&qlog->buf) < +      NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->tokenlen * 2) { +    return; +  } + +  assert(ngtcp2_buf_len(&qlog->buf)); + +  /* Eat last ',' */ +  if (*(p - 1) == ',') { +    --p; +  } + +  p = write_verbatim(p, "],\"header\":"); +  p = write_pkt_hd(p, hd); +  p = write_verbatim(p, ",\"raw\":{\"length\":"); +  p = write_number(p, pktlen); +  p = write_verbatim(p, "}}}\n"); + +  qlog->buf.last = p; + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, qlog->buf.pos, +              ngtcp2_buf_len(&qlog->buf)); +} + +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { +  uint8_t *p = qlog->buf.last; + +  if (!qlog->write) { +    return; +  } + +  switch (fr->type) { +  case NGTCP2_FRAME_PADDING: +    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) { +      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) { +      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 + +          (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN +                     ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD +                     : 0) + +          NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.rangecnt) + 1) { +      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) { +      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) { +      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) { +      return; +    } +    p = write_crypto_frame(p, &fr->stream); +    break; +  case NGTCP2_FRAME_NEW_TOKEN: +    if (ngtcp2_buf_left(&qlog->buf) < +        NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + fr->new_token.tokenlen * 2 + 1) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      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) { +      return; +    } +    p = write_handshake_done_frame(p, &fr->handshake_done); +    break; +  case NGTCP2_FRAME_DATAGRAM: +  case NGTCP2_FRAME_DATAGRAM_LEN: +    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) { +      return; +    } +    p = write_datagram_frame(p, &fr->datagram); +    break; +  default: +    ngtcp2_unreachable(); +  } + +  *p++ = ','; + +  qlog->buf.last = p; +} + +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog) { +  qlog_pkt_write_start(qlog, /* 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) { +  qlog_pkt_write_start(qlog, /* sent = */ 1); +} + +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, +                              size_t pktlen) { +  qlog_pkt_write_end(qlog, hd, pktlen); +} + +void ngtcp2_qlog_parameters_set_transport_params( +  ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, +  ngtcp2_qlog_side side) { +  uint8_t buf[1024]; +  uint8_t *p = buf; +  const ngtcp2_preferred_addr *paddr; +  const ngtcp2_sockaddr_in *sa_in; +  const ngtcp2_sockaddr_in6 *sa_in6; + +  if (!qlog->write) { +    return; +  } + +  *p++ = '\x1e'; +  *p++ = '{'; +  p = qlog_write_time(qlog, p); +  p = write_verbatim( +    p, ",\"name\":\"transport:parameters_set\",\"data\":{\"owner\":"); + +  if (side == NGTCP2_QLOG_SIDE_LOCAL) { +    p = write_string(p, "local"); +  } else { +    p = write_string(p, "remote"); +  } + +  *p++ = ','; +  p = write_pair_cid(p, "initial_source_connection_id", ¶ms->initial_scid); +  *p++ = ','; +  if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) { +    p = write_pair_cid(p, "original_destination_connection_id", +                       ¶ms->original_dcid); +    *p++ = ','; +  } +  if (params->retry_scid_present) { +    p = write_pair_cid(p, "retry_source_connection_id", ¶ms->retry_scid); +    *p++ = ','; +  } +  if (params->stateless_reset_token_present) { +    p = write_verbatim(p, "\"stateless_reset_token\":{"); +    p = write_pair_hex(p, "data", params->stateless_reset_token, +                       sizeof(params->stateless_reset_token)); +    *p++ = '}'; +    *p++ = ','; +  } +  p = write_pair_bool(p, "disable_active_migration", +                      params->disable_active_migration); +  *p++ = ','; +  p = write_pair_duration(p, "max_idle_timeout", params->max_idle_timeout); +  *p++ = ','; +  p = +    write_pair_number(p, "max_udp_payload_size", params->max_udp_payload_size); +  *p++ = ','; +  p = write_pair_number(p, "ack_delay_exponent", params->ack_delay_exponent); +  *p++ = ','; +  p = write_pair_duration(p, "max_ack_delay", params->max_ack_delay); +  *p++ = ','; +  p = write_pair_number(p, "active_connection_id_limit", +                        params->active_connection_id_limit); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_data", params->initial_max_data); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_stream_data_bidi_local", +                        params->initial_max_stream_data_bidi_local); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_stream_data_bidi_remote", +                        params->initial_max_stream_data_bidi_remote); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_stream_data_uni", +                        params->initial_max_stream_data_uni); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_streams_bidi", +                        params->initial_max_streams_bidi); +  *p++ = ','; +  p = write_pair_number(p, "initial_max_streams_uni", +                        params->initial_max_streams_uni); +  if (params->preferred_addr_present) { +    *p++ = ','; +    paddr = ¶ms->preferred_addr; +    p = write_string(p, "preferred_address"); +    *p++ = ':'; +    *p++ = '{'; + +    if (paddr->ipv4_present) { +      sa_in = &paddr->ipv4; + +      p = write_pair_hex(p, "ip_v4", (const uint8_t *)&sa_in->sin_addr, +                         sizeof(sa_in->sin_addr)); +      *p++ = ','; +      p = write_pair_number(p, "port_v4", ngtcp2_ntohs(sa_in->sin_port)); +      *p++ = ','; +    } + +    if (paddr->ipv6_present) { +      sa_in6 = &paddr->ipv6; + +      p = write_pair_hex(p, "ip_v6", (const uint8_t *)&sa_in6->sin6_addr, +                         sizeof(sa_in6->sin6_addr)); +      *p++ = ','; +      p = write_pair_number(p, "port_v6", ngtcp2_ntohs(sa_in6->sin6_port)); +      *p++ = ','; +    } + +    p = write_pair_cid(p, "connection_id", &paddr->cid); +    p = write_verbatim(p, ",\"stateless_reset_token\":{"); +    p = write_pair_hex(p, "data", paddr->stateless_reset_token, +                       sizeof(paddr->stateless_reset_token)); +    *p++ = '}'; +    *p++ = '}'; +  } +  *p++ = ','; +  p = write_pair_number(p, "max_datagram_frame_size", +                        params->max_datagram_frame_size); +  *p++ = ','; +  p = write_pair_bool(p, "grease_quic_bit", params->grease_quic_bit); +  p = write_verbatim(p, "}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, +              (size_t)(p - buf)); +} + +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, +                                 const ngtcp2_conn_stat *cstat) { +  uint8_t buf[1024]; +  uint8_t *p = buf; + +  if (!qlog->write) { +    return; +  } + +  *p++ = '\x1e'; +  *p++ = '{'; +  p = qlog_write_time(qlog, p); +  p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{"); + +  if (cstat->min_rtt != UINT64_MAX) { +    p = write_pair_duration(p, "min_rtt", cstat->min_rtt); +    *p++ = ','; +  } +  p = write_pair_duration(p, "smoothed_rtt", cstat->smoothed_rtt); +  *p++ = ','; +  p = write_pair_duration(p, "latest_rtt", cstat->latest_rtt); +  *p++ = ','; +  p = write_pair_duration(p, "rtt_variance", cstat->rttvar); +  *p++ = ','; +  p = write_pair_number(p, "pto_count", cstat->pto_count); +  *p++ = ','; +  p = write_pair_number(p, "congestion_window", cstat->cwnd); +  *p++ = ','; +  p = write_pair_number(p, "bytes_in_flight", cstat->bytes_in_flight); +  if (cstat->ssthresh != UINT64_MAX) { +    *p++ = ','; +    p = write_pair_number(p, "ssthresh", cstat->ssthresh); +  } + +  p = write_verbatim(p, "}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, +              (size_t)(p - buf)); +} + +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { +  uint8_t buf[256]; +  uint8_t *p = buf; +  ngtcp2_pkt_hd hd = {0}; + +  if (!qlog->write) { +    return; +  } + +  *p++ = '\x1e'; +  *p++ = '{'; +  p = qlog_write_time(qlog, p); +  p = write_verbatim( +    p, ",\"name\":\"recovery:packet_lost\",\"data\":{\"header\":"); + +  hd.type = ent->hd.type; +  hd.flags = ent->hd.flags; +  hd.pkt_num = ent->hd.pkt_num; + +  p = write_pkt_hd(p, &hd); +  p = write_verbatim(p, "}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, +              (size_t)(p - buf)); +} + +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, +                                    const ngtcp2_pkt_retry *retry) { +  uint8_t rawbuf[1024]; +  ngtcp2_buf buf; + +  if (!qlog->write) { +    return; +  } + +  ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + +  *buf.last++ = '\x1e'; +  *buf.last++ = '{'; +  buf.last = qlog_write_time(qlog, buf.last); +  buf.last = write_verbatim( +    buf.last, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + +  if (ngtcp2_buf_left(&buf) < NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->tokenlen * 2 + +                                sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - +                                1 + retry->tokenlen * 2) { +    return; +  } + +  buf.last = write_pkt_hd(buf.last, hd); +  buf.last = write_verbatim(buf.last, ",\"retry_token\":{"); +  buf.last = write_pair_hex(buf.last, "data", retry->token, retry->tokenlen); +  buf.last = write_verbatim(buf.last, "}}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, +              ngtcp2_buf_len(&buf)); +} + +void ngtcp2_qlog_stateless_reset_pkt_received( +  ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) { +  uint8_t buf[256]; +  uint8_t *p = buf; +  ngtcp2_pkt_hd hd = {0}; + +  if (!qlog->write) { +    return; +  } + +  hd.type = NGTCP2_PKT_STATELESS_RESET; + +  *p++ = '\x1e'; +  *p++ = '{'; +  p = qlog_write_time(qlog, p); +  p = write_verbatim( +    p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); +  p = write_pkt_hd(p, &hd); +  *p++ = ','; +  p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token, +                     NGTCP2_STATELESS_RESET_TOKENLEN); +  p = write_verbatim(p, "}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, +              (size_t)(p - buf)); +} + +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, +                                                  const ngtcp2_pkt_hd *hd, +                                                  const uint32_t *sv, +                                                  size_t nsv) { +  uint8_t rawbuf[512]; +  ngtcp2_buf buf; +  size_t i; +  uint32_t v; + +  if (!qlog->write) { +    return; +  } + +  ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf)); + +  *buf.last++ = '\x1e'; +  *buf.last++ = '{'; +  buf.last = qlog_write_time(qlog, buf.last); +  buf.last = write_verbatim( +    buf.last, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); +  buf.last = write_pkt_hd(buf.last, hd); +  buf.last = write_verbatim(buf.last, ",\"supported_versions\":["); + +  if (nsv) { +    if (ngtcp2_buf_left(&buf) < +        (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) { +      return; +    } + +    v = ngtcp2_htonl(sv[0]); +    buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); + +    for (i = 1; i < nsv; ++i) { +      *buf.last++ = ','; +      v = ngtcp2_htonl(sv[i]); +      buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v)); +    } +  } + +  buf.last = write_verbatim(buf.last, "]}}\n"); + +  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos, +              ngtcp2_buf_len(&buf)); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_qlog.h b/contrib/libs/ngtcp2/lib/ngtcp2_qlog.h new file mode 100644 index 00000000000..d2a5f1038c0 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_qlog.h @@ -0,0 +1,161 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_QLOG_H +#define NGTCP2_QLOG_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_rtb.h" + +/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for +   qlog. */ +#define NGTCP2_QLOG_BUFLEN 4096 + +typedef enum ngtcp2_qlog_side { +  NGTCP2_QLOG_SIDE_LOCAL, +  NGTCP2_QLOG_SIDE_REMOTE, +} ngtcp2_qlog_side; + +typedef struct ngtcp2_qlog { +  /* write is a callback function to write qlog. */ +  ngtcp2_qlog_write write; +  /* ts is the initial timestamp */ +  ngtcp2_tstamp ts; +  /* last_ts is the timestamp observed last time. */ +  ngtcp2_tstamp last_ts; +  /* buf is a heap allocated buffer to write exclusively +     packet_received and packet_sent. */ +  ngtcp2_buf buf; +  /* user_data is an opaque pointer which is passed to write +     callback. */ +  void *user_data; +} ngtcp2_qlog; + +/* + * ngtcp2_qlog_init initializes |qlog|. + */ +void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, +                      ngtcp2_tstamp ts, void *user_data); + +/* + * ngtcp2_qlog_start writes qlog preamble. + */ +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server); + +/* + * ngtcp2_qlog_end writes closing part of qlog. + */ +void ngtcp2_qlog_end(ngtcp2_qlog *qlog); + +/* + * ngtcp2_qlog_write_frame writes |fr| to qlog->buf. + * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must + * be called before calling this function. + */ +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr); + +/* + * ngtcp2_qlog_pkt_received_start starts to write packet_received + * event.  It initializes qlog->buf.  It writes qlog to qlog->buf. + * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to + * write callback. + */ +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog); + +/* + * 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); + +/* + * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the + * content of qlog->buf to qlog->write callback. + */ +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, +                              size_t pktlen); + +/* + * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog + * as parameters_set event.  |server| is nonzero if the local endpoint + * is server.  If |local| is nonzero, it is "owner" field becomes + * "local", otherwise "remote". + */ +void ngtcp2_qlog_parameters_set_transport_params( +  ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, +  ngtcp2_qlog_side side); + +/* + * ngtcp2_qlog_metrics_updated writes metrics_updated event of + * recovery category. + */ +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, +                                 const ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_qlog_pkt_lost writes packet_lost event. + */ +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); + +/* + * ngtcp2_qlog_retry_pkt_received writes packet_received event for a + * received Retry packet. + */ +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, +                                    const ngtcp2_pkt_retry *retry); + +/* + * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received + * event for a received Stateless Reset packet. + */ +void ngtcp2_qlog_stateless_reset_pkt_received( +  ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr); + +/* + * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received + * event for a received Version Negotiation packet. + */ +void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog, +                                                  const ngtcp2_pkt_hd *hd, +                                                  const uint32_t *sv, +                                                  size_t nsv); + +#endif /* !defined(NGTCP2_QLOG_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_range.c b/contrib/libs/ngtcp2/lib/ngtcp2_range.c new file mode 100644 index 00000000000..7bbefc0175c --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_range.c @@ -0,0 +1,63 @@ +/* + * 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_uint64(a->begin, b->begin); +  uint64_t end = ngtcp2_min_uint64(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/contrib/libs/ngtcp2/lib/ngtcp2_range.h b/contrib/libs/ngtcp2/lib/ngtcp2_range.h new file mode 100644 index 00000000000..22cd2951859 --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_range represents half-closed range [begin, end). + */ +typedef struct ngtcp2_range { +  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 /* !defined(NGTCP2_RANGE_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rcvry.h b/contrib/libs/ngtcp2/lib/ngtcp2_rcvry.h new file mode 100644 index 00000000000..e6321061b59 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rcvry.h @@ -0,0 +1,40 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RCVRY_H +#define NGTCP2_RCVRY_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in RFC 9002. */ +#define NGTCP2_PKT_THRESHOLD 3 + +/* NGTCP2_GRANULARITY is kGranularity described in RFC 9002. */ +#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS + +#endif /* !defined(NGTCP2_RCVRY_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.c b/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.c new file mode 100644 index 00000000000..1dab296a80d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.c @@ -0,0 +1,123 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ringbuf.h" + +#include <assert.h> +#ifdef WIN32 +#  include <intrin.h> +#endif /* defined(WIN32) */ + +#include "ngtcp2_macro.h" + +static int ispow2(size_t n) { +#if defined(_MSC_VER) && !defined(__clang__) &&                                \ +  (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941)) +  return n && !(n & (n - 1)); +#elif defined(WIN32) +  return 1 == __popcnt((unsigned int)n); +#else  /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) ||   \ +          (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +  return 1 == __builtin_popcount((unsigned int)n); +#endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) ||   \ +          (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +} + +int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, +                        const ngtcp2_mem *mem) { +  uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size); + +  if (buf == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem); + +  return 0; +} + +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, +                             uint8_t *buf, const ngtcp2_mem *mem) { +  assert(ispow2(nmemb)); + +  rb->buf = buf; +  rb->mem = mem; +  rb->mask = nmemb - 1; +  rb->size = size; +  rb->first = 0; +  rb->len = 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->mask; +  if (rb->len < rb->mask + 1) { +    ++rb->len; +  } + +  return (void *)&rb->buf[rb->first * rb->size]; +} + +void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) { +  size_t offset = (rb->first + rb->len) & rb->mask; + +  if (rb->len == rb->mask + 1) { +    rb->first = (rb->first + 1) & rb->mask; +  } else { +    ++rb->len; +  } + +  return (void *)&rb->buf[offset * rb->size]; +} + +void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) { +  rb->first = (rb->first + 1) & rb->mask; +  --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->mask + 1); +  rb->len = len; +} + +void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset) { +  assert(offset < rb->len); +  offset = (rb->first + offset) & rb->mask; + +  return &rb->buf[offset * rb->size]; +} + +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->mask + 1; } diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.h b/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.h new file mode 100644 index 00000000000..6953ea6278f --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_ringbuf.h @@ -0,0 +1,132 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RINGBUF_H +#define NGTCP2_RINGBUF_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +typedef struct ngtcp2_ringbuf { +  /* buf points to the underlying buffer. */ +  uint8_t *buf; +  const ngtcp2_mem *mem; +  /* mask is the bit mask to cover all bits for the maximum number of +     elements.  The maximum number of elements is mask + 1. */ +  size_t mask; +  /* 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.  |nmemb| 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_buf_init initializes |rb| with given buffer and + * size.  Same restrictions are applied as ngtcp2_ringbuf_init. + */ +void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, +                             uint8_t *buf, 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, +   this function returns the pointer to the last element, 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, this function +   returns the pointer to the first element, 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(const ngtcp2_ringbuf *rb, size_t offset); + +/* ngtcp2_ringbuf_len returns the number of elements stored. */ +#define ngtcp2_ringbuf_len(RB) ((RB)->len) + +/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); + +/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper +   which uses a statically allocated buffer.  ngtcp2_ringbuf_free +   should never be called for rb field. */ +#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE)                           \ +  typedef struct ngtcp2_static_ringbuf_##NAME {                                \ +    ngtcp2_ringbuf rb;                                                         \ +    uint8_t buf[(NMEMB) * (SIZE)];                                             \ +  } ngtcp2_static_ringbuf_##NAME;                                              \ +                                                                               \ +  static inline void ngtcp2_static_ringbuf_##NAME##_init(                      \ +    ngtcp2_static_ringbuf_##NAME *srb) {                                       \ +    ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL);        \ +  } + +#endif /* !defined(NGTCP2_RINGBUF_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rob.c b/contrib/libs/ngtcp2/lib/ngtcp2_rob.c new file mode 100644 index 00000000000..12c7d40976c --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rob.c @@ -0,0 +1,332 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rob.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_macro.h" + +int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, +                       const ngtcp2_mem *mem) { +  *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap)); +  if (*pg == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  (*pg)->range.begin = begin; +  (*pg)->range.end = end; + +  return 0; +} + +void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) { +  ngtcp2_mem_free(mem, g); +} + +int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, +                        const ngtcp2_mem *mem) { +  *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk); +  if (*pd == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  (*pd)->range.begin = offset; +  (*pd)->range.end = offset + chunk; +  (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data); + +  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_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), +                  mem); + +  rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); +  if (rv != 0) { +    goto fail_rob_gap_new; +  } + +  rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, &g->range, g); +  if (rv != 0) { +    goto fail_gapksl_ksl_insert; +  } + +  ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), +                  mem); + +  rob->chunk = chunk; +  rob->mem = mem; + +  return 0; + +fail_gapksl_ksl_insert: +  ngtcp2_rob_gap_del(g, mem); +fail_rob_gap_new: +  ngtcp2_ksl_free(&rob->gapksl); +  return rv; +} + +void ngtcp2_rob_free(ngtcp2_rob *rob) { +  ngtcp2_ksl_it it; + +  if (rob == NULL) { +    return; +  } + +  for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it); +       ngtcp2_ksl_it_next(&it)) { +    ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem); +  } + +  for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it); +       ngtcp2_ksl_it_next(&it)) { +    ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem); +  } + +  ngtcp2_ksl_free(&rob->dataksl); +  ngtcp2_ksl_free(&rob->gapksl); +} + +static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, +                          size_t len) { +  size_t n; +  int rv; +  ngtcp2_rob_data *d; +  ngtcp2_range range = {offset, offset + len}; +  ngtcp2_ksl_it it; + +  for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, &range, +                                          ngtcp2_ksl_range_exclusive_compar); +       len; ngtcp2_ksl_it_next(&it)) { +    if (ngtcp2_ksl_it_end(&it)) { +      d = NULL; +    } else { +      d = ngtcp2_ksl_it_get(&it); +    } + +    if (d == NULL || offset < d->range.begin) { +      rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk, +                               rob->chunk, rob->mem); +      if (rv != 0) { +        return rv; +      } + +      rv = ngtcp2_ksl_insert(&rob->dataksl, &it, &d->range, d); +      if (rv != 0) { +        ngtcp2_rob_data_del(d, rob->mem); +        return rv; +      } +    } + +    n = (size_t)ngtcp2_min_uint64((uint64_t)len, +                                  d->range.begin + rob->chunk - offset); +    memcpy(d->begin + (offset - d->range.begin), data, n); +    offset += n; +    data += n; +    len -= n; +  } + +  return 0; +} + +int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, +                    size_t datalen) { +  int rv; +  ngtcp2_rob_gap *g; +  ngtcp2_range m, l, r, q = {offset, offset + datalen}; +  ngtcp2_ksl_it it; + +  it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, &q, +                                     ngtcp2_ksl_range_exclusive_compar); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    g = ngtcp2_ksl_it_get(&it); + +    m = ngtcp2_range_intersect(&q, &g->range); +    if (!ngtcp2_range_len(&m)) { +      break; +    } + +    if (ngtcp2_range_eq(&g->range, &m)) { +      ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); +      ngtcp2_rob_gap_del(g, rob->mem); + +      rv = rob_write_data(rob, m.begin, data + (m.begin - offset), +                          (size_t)ngtcp2_range_len(&m)); +      if (rv != 0) { +        return rv; +      } + +      continue; +    } + +    ngtcp2_range_cut(&l, &r, &g->range, &m); + +    if (ngtcp2_range_len(&l)) { +      ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &l); +      g->range = l; + +      if (ngtcp2_range_len(&r)) { +        ngtcp2_rob_gap *ng; + +        rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); +        if (rv != 0) { +          return rv; +        } + +        rv = ngtcp2_ksl_insert(&rob->gapksl, &it, &ng->range, ng); +        if (rv != 0) { +          ngtcp2_rob_gap_del(ng, rob->mem); +          return rv; +        } +      } +    } else if (ngtcp2_range_len(&r)) { +      ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); +      g->range = r; +    } + +    rv = rob_write_data(rob, m.begin, data + (m.begin - offset), +                        (size_t)ngtcp2_range_len(&m)); +    if (rv != 0) { +      return rv; +    } + +    ngtcp2_ksl_it_next(&it); +  } + +  return 0; +} + +void ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { +  ngtcp2_rob_gap *g; +  ngtcp2_rob_data *d; +  ngtcp2_ksl_it it; + +  it = ngtcp2_ksl_begin(&rob->gapksl); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    g = ngtcp2_ksl_it_get(&it); +    if (offset <= g->range.begin) { +      break; +    } + +    if (offset < g->range.end) { +      ngtcp2_range r = {offset, g->range.end}; + +      ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); +      g->range.begin = offset; + +      break; +    } + +    ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); +    ngtcp2_rob_gap_del(g, rob->mem); +  } + +  it = ngtcp2_ksl_begin(&rob->dataksl); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    d = ngtcp2_ksl_it_get(&it); +    if (offset < d->range.begin + rob->chunk) { +      return; +    } + +    ngtcp2_ksl_remove_hint(&rob->dataksl, &it, &it, &d->range); +    ngtcp2_rob_data_del(d, rob->mem); +  } +} + +size_t ngtcp2_rob_data_at(const ngtcp2_rob *rob, const uint8_t **pdest, +                          uint64_t offset) { +  ngtcp2_rob_gap *g; +  ngtcp2_rob_data *d; +  ngtcp2_ksl_it it; + +  it = ngtcp2_ksl_begin(&rob->gapksl); +  if (ngtcp2_ksl_it_end(&it)) { +    return 0; +  } + +  g = ngtcp2_ksl_it_get(&it); + +  if (g->range.begin <= offset) { +    return 0; +  } + +  it = ngtcp2_ksl_begin(&rob->dataksl); +  d = ngtcp2_ksl_it_get(&it); + +  assert(d); +  assert(d->range.begin <= offset); +  assert(offset < d->range.begin + rob->chunk); + +  *pdest = d->begin + (offset - d->range.begin); + +  return ( +    size_t)(ngtcp2_min_uint64(g->range.begin, d->range.begin + rob->chunk) - +            offset); +} + +void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { +  ngtcp2_ksl_it it; +  ngtcp2_rob_data *d; + +  it = ngtcp2_ksl_begin(&rob->dataksl); +  d = ngtcp2_ksl_it_get(&it); + +  assert(d); + +  if (offset + len < d->range.begin + rob->chunk) { +    return; +  } + +  ngtcp2_ksl_remove_hint(&rob->dataksl, NULL, &it, &d->range); +  ngtcp2_rob_data_del(d, rob->mem); +} + +uint64_t ngtcp2_rob_first_gap_offset(const 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(const ngtcp2_rob *rob) { +  return ngtcp2_ksl_len(&rob->dataksl) != 0; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rob.h b/contrib/libs/ngtcp2/lib/ngtcp2_rob.h new file mode 100644 index 00000000000..d53b5160b10 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rob.h @@ -0,0 +1,191 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ROB_H +#define NGTCP2_ROB_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" +#include "ngtcp2_ksl.h" + +/* + * ngtcp2_rob_gap represents the gap, which is the range of stream + * data that is not received yet. + */ +typedef struct ngtcp2_rob_gap { +  /* range is the range of this gap. */ +  ngtcp2_range range; +} ngtcp2_rob_gap; + +/* + * 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); + +/* + * ngtcp2_rob_data holds the buffered stream data. + */ +typedef struct ngtcp2_rob_data { +  /* range is the range of this data. */ +  ngtcp2_range range; +  /* begin points to the buffer. */ +  uint8_t *begin; +} ngtcp2_rob_data; + +/* + * 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 ngtcp2_rob { +  /* gapksl maintains the range of offset which is not received +     yet. Initially, its range is [0, UINT64_MAX). */ +  ngtcp2_ksl gapksl; +  /* dataksl maintains the buffers which store received out-of-order +     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 pointed by |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 buffered data if it is completely included in + * |offset|. + */ +void 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.  This function only returns the data before the first + * gap.  It returns 0 even if data is available after the first gap. + */ +size_t ngtcp2_rob_data_at(const 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(const ngtcp2_rob *rob); + +/* + * ngtcp2_rob_data_buffered returns nonzero if any data is buffered. + */ +int ngtcp2_rob_data_buffered(const ngtcp2_rob *rob); + +#endif /* !defined(NGTCP2_ROB_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rst.c b/contrib/libs/ngtcp2/lib/ngtcp2_rst.c new file mode 100644 index 00000000000..89c89acdc26 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rst.c @@ -0,0 +1,131 @@ +/* + * 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 <assert.h> + +#include "ngtcp2_rtb.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_conn_stat.h" + +void ngtcp2_rs_init(ngtcp2_rs *rs) { +  rs->interval = UINT64_MAX; +  rs->delivered = 0; +  rs->prior_delivered = 0; +  rs->prior_ts = UINT64_MAX; +  rs->tx_in_flight = 0; +  rs->lost = 0; +  rs->prior_lost = 0; +  rs->send_elapsed = 0; +  rs->ack_elapsed = 0; +  rs->last_end_seq = -1; +  rs->is_app_limited = 0; +} + +void ngtcp2_rst_init(ngtcp2_rst *rst) { +  ngtcp2_rs_init(&rst->rs); +  rst->delivered = 0; +  rst->delivered_ts = 0; +  rst->first_sent_ts = 0; +  rst->app_limited = 0; +  rst->is_cwnd_limited = 0; +  rst->lost = 0; +  rst->last_seq = -1; +} + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, +                            const ngtcp2_conn_stat *cstat) { +  if (cstat->bytes_in_flight == 0) { +    rst->first_sent_ts = rst->delivered_ts = ent->ts; +  } +  ent->rst.first_sent_ts = rst->first_sent_ts; +  ent->rst.delivered_ts = rst->delivered_ts; +  ent->rst.delivered = rst->delivered; +  ent->rst.is_app_limited = rst->app_limited != 0; +  ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen; +  ent->rst.lost = rst->lost; +  ent->rst.end_seq = ++rst->last_seq; +} + +void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { +  ngtcp2_rs *rs = &rst->rs; + +  if (rst->app_limited && rst->delivered > rst->app_limited) { +    rst->app_limited = 0; +  } + +  if (rs->prior_ts == UINT64_MAX) { +    return; +  } + +  rs->interval = ngtcp2_max_uint64(rs->send_elapsed, rs->ack_elapsed); + +  rs->delivered = rst->delivered - rs->prior_delivered; +  rs->lost = rst->lost - rs->prior_lost; + +  if (rs->interval < cstat->min_rtt) { +    rs->interval = UINT64_MAX; +    return; +  } + +  if (!rs->interval) { +    return; +  } + +  cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; +} + +static int rst_is_newest_pkt(const ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, +                             const ngtcp2_rs *rs) { +  return ent->ts > rst->first_sent_ts || +         (ent->ts == rst->first_sent_ts && ent->rst.end_seq > rs->last_end_seq); +} + +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 (rs->prior_ts == UINT64_MAX || rst_is_newest_pkt(rst, ent, rs)) { +    rs->prior_delivered = ent->rst.delivered; +    rs->prior_ts = ent->rst.delivered_ts; +    rs->is_app_limited = ent->rst.is_app_limited; +    rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; +    rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; +    rs->tx_in_flight = ent->rst.tx_in_flight; +    rs->prior_lost = ent->rst.lost; +    rs->last_end_seq = ent->rst.end_seq; +    rst->first_sent_ts = ent->ts; +  } +} + +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { +  (void)rst; +  (void)cstat; +  /* TODO Not implemented */ +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rst.h b/contrib/libs/ngtcp2/lib/ngtcp2_rst.h new file mode 100644 index 00000000000..95616eee97d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rst.h @@ -0,0 +1,88 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RST_H +#define NGTCP2_RST_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_window_filter.h" + +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; +typedef struct ngtcp2_conn_stat ngtcp2_conn_stat; + +/** + * @struct + * + * ngtcp2_rs contains connection state for delivery rate estimation. + */ +typedef struct ngtcp2_rs { +  ngtcp2_duration interval; +  uint64_t delivered; +  uint64_t prior_delivered; +  ngtcp2_tstamp prior_ts; +  uint64_t tx_in_flight; +  uint64_t lost; +  uint64_t prior_lost; +  ngtcp2_duration send_elapsed; +  ngtcp2_duration ack_elapsed; +  int64_t last_end_seq; +  int is_app_limited; +} ngtcp2_rs; + +void ngtcp2_rs_init(ngtcp2_rs *rs); + +/* + * ngtcp2_rst implements delivery rate estimation described in + * https://ietf-wg-ccwg.github.io/draft-cardwell-ccwg-bbr/draft-cardwell-ccwg-bbr.html + */ +typedef struct ngtcp2_rst { +  ngtcp2_rs rs; +  uint64_t delivered; +  ngtcp2_tstamp delivered_ts; +  ngtcp2_tstamp first_sent_ts; +  uint64_t app_limited; +  uint64_t lost; +  /* last_seq is the sequence number of packets across all packet +     number spaces.  If we would adopt single packet number sequence +     across all packet number spaces, we can replace this with a +     packet number. */ +  int64_t last_seq; +  int is_cwnd_limited; +} ngtcp2_rst; + +void ngtcp2_rst_init(ngtcp2_rst *rst); + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, +                            const ngtcp2_conn_stat *cstat); +void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); +void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, +                                   ngtcp2_tstamp ts); +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); + +#endif /* !defined(NGTCP2_RST_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c b/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c new file mode 100644 index 00000000000..008ba470c28 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c @@ -0,0 +1,1508 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rtb.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_conn.h" +#include "ngtcp2_log.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" +#include "ngtcp2_unreachable.h" +#include "ngtcp2_tstamp.h" +#include "ngtcp2_frame_chain.h" + +ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent); + +static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, +                           ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, +                           size_t pktlen, uint16_t flags) { +  memset(ent, 0, sizeof(*ent)); + +  ent->hd.pkt_num = hd->pkt_num; +  ent->hd.type = hd->type; +  ent->hd.flags = hd->flags; +  ent->frc = frc; +  ent->ts = ts; +  ent->lost_ts = UINT64_MAX; +  ent->pktlen = pktlen; +  ent->flags = flags; +} + +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, +                                  const ngtcp2_pkt_hd *hd, +                                  ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, +                                  size_t pktlen, uint16_t flags, +                                  ngtcp2_objalloc *objalloc) { +  *pent = ngtcp2_objalloc_rtb_entry_get(objalloc); +  if (*pent == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  rtb_entry_init(*pent, hd, frc, ts, pktlen, flags); + +  return 0; +} + +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, +                                   ngtcp2_objalloc *objalloc, +                                   ngtcp2_objalloc *frc_objalloc, +                                   const ngtcp2_mem *mem) { +  ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem); + +  ent->frc = NULL; + +  ngtcp2_objalloc_rtb_entry_release(objalloc, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { +  return *(int64_t *)lhs > *(int64_t *)rhs; +} + +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_rst *rst, ngtcp2_cc *cc, +                     int64_t cc_pkt_num, ngtcp2_log *log, ngtcp2_qlog *qlog, +                     ngtcp2_objalloc *rtb_entry_objalloc, +                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { +  rtb->rtb_entry_objalloc = rtb_entry_objalloc; +  rtb->frc_objalloc = frc_objalloc; +  ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); +  rtb->rst = rst; +  rtb->cc = cc; +  rtb->log = log; +  rtb->qlog = qlog; +  rtb->mem = mem; +  rtb->largest_acked_tx_pkt_num = -1; +  rtb->num_ack_eliciting = 0; +  rtb->num_retransmittable = 0; +  rtb->num_pto_eliciting = 0; +  rtb->probe_pkt_left = 0; +  rtb->cc_pkt_num = cc_pkt_num; +  rtb->cc_bytes_in_flight = 0; +  rtb->persistent_congestion_start_ts = UINT64_MAX; +  rtb->num_lost_pkts = 0; +  rtb->num_lost_pmtud_pkts = 0; +} + +void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { +  ngtcp2_ksl_it it; + +  if (rtb == NULL) { +    return; +  } + +  it = ngtcp2_ksl_begin(&rtb->ents); + +  for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { +    ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it), +                                  rtb->rtb_entry_objalloc, rtb->frc_objalloc, +                                  rtb->mem); +  } + +  ngtcp2_ksl_free(&rtb->ents); +} + +static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                       ngtcp2_conn_stat *cstat) { +  ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat); + +  assert(rtb->cc_pkt_num <= ent->hd.pkt_num); + +  cstat->bytes_in_flight += ent->pktlen; +  rtb->cc_bytes_in_flight += ent->pktlen; + +  ngtcp2_rst_update_app_limited(rtb->rst, cstat); + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +    ++rtb->num_ack_eliciting; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { +    ++rtb->num_retransmittable; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { +    ++rtb->num_pto_eliciting; +  } +} + +static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                            ngtcp2_conn_stat *cstat) { +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { +    assert(rtb->num_lost_pkts); +    --rtb->num_lost_pkts; + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +      assert(rtb->num_lost_pmtud_pkts); +      --rtb->num_lost_pmtud_pkts; +    } + +    return 0; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +    assert(rtb->num_ack_eliciting); +    --rtb->num_ack_eliciting; +  } + +  if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && +      !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) { +    assert(rtb->num_retransmittable); +    --rtb->num_retransmittable; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { +    assert(rtb->num_pto_eliciting); +    --rtb->num_pto_eliciting; +  } + +  if (rtb->cc_pkt_num <= ent->hd.pkt_num) { +    assert(cstat->bytes_in_flight >= ent->pktlen); +    cstat->bytes_in_flight -= ent->pktlen; + +    assert(rtb->cc_bytes_in_flight >= ent->pktlen); +    rtb->cc_bytes_in_flight -= ent->pktlen; + +    /* If PMTUD packet is lost, we do not report the lost bytes to the +       caller in order to ignore loss of PMTUD packet. */ +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +      return 0; +    } + +    return ent->pktlen; +  } + +  return 0; +} + +/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_RECLAIM_FLAG_NONE 0x00u +/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed +   because of the packet loss.*/ +#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u + +/* + * rtb_reclaim_frame copies and queues frames included in |ent| for + * retransmission.  The frames are not deleted from |ent|.  It returns + * the number of frames queued.  |flags| is bitwise OR of 0 or more of + * NGTCP2_RECLAIM_FLAG_*. + */ +static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags, +                                      ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                                      ngtcp2_rtb_entry *ent) { +  ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq; +  ngtcp2_frame *fr; +  ngtcp2_strm *strm; +  ngtcp2_range gap, range; +  size_t num_reclaimed = 0; +  int rv; + +  assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE); + +  /* TODO Reconsider the order of pfrc */ +  for (frc = ent->frc; frc; frc = frc->next) { +    fr = &frc->fr; + +    /* Check that a late ACK acknowledged this frame. */ +    if (frc->binder && +        (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { +      continue; +    } + +    switch (frc->fr.type) { +    case NGTCP2_FRAME_STREAM: +      strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id); +      if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM)) { +        continue; +      } + +      gap = ngtcp2_strm_get_unacked_range_after(strm, fr->stream.offset); + +      range.begin = fr->stream.offset; +      range.end = +        fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); +      range = ngtcp2_range_intersect(&range, &gap); + +      if (ngtcp2_range_len(&range) == 0) { +        if (!fr->stream.fin) { +          /* 0 length STREAM frame with offset == 0 must be +             retransmitted if no non-empty data are sent to this +             stream, and no data in this stream are acknowledged. */ +          if (fr->stream.offset != 0 || fr->stream.datacnt != 0 || +              strm->tx.offset || (strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { +            continue; +          } +        } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { +          continue; +        } +      } + +      if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) && +          ent->hd.pkt_num != strm->tx.last_lost_pkt_num) { +        strm->tx.last_lost_pkt_num = ent->hd.pkt_num; +        ++strm->tx.loss_count; +      } + +      rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +        &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem); +      if (rv != 0) { +        return rv; +      } + +      nfrc->fr = *fr; +      ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, +                      fr->stream.datacnt); + +      rv = ngtcp2_strm_streamfrq_push(strm, nfrc); +      if (rv != 0) { +        ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); +        return rv; +      } + +      if (!ngtcp2_strm_is_tx_queued(strm)) { +        strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); + +        rv = ngtcp2_conn_tx_strmq_push(conn, strm); +        if (rv != 0) { +          return rv; +        } +      } + +      ++num_reclaimed; + +      continue; +    case NGTCP2_FRAME_CRYPTO: +      /* Do not resend CRYPTO frame if the whole region it contains +         has been acknowledged */ +      gap = ngtcp2_strm_get_unacked_range_after(&pktns->crypto.strm, +                                                fr->stream.offset); + +      range.begin = fr->stream.offset; +      range.end = +        fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); +      range = ngtcp2_range_intersect(&range, &gap); + +      if (ngtcp2_range_len(&range) == 0) { +        continue; +      } + +      rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +        &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem); +      if (rv != 0) { +        return rv; +      } + +      nfrc->fr = *fr; +      ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, +                      fr->stream.datacnt); + +      rv = ngtcp2_strm_streamfrq_push(&pktns->crypto.strm, nfrc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem); +        return rv; +      } + +      ++num_reclaimed; + +      continue; +    case NGTCP2_FRAME_NEW_TOKEN: +      rv = ngtcp2_frame_chain_new_token_objalloc_new( +        &nfrc, fr->new_token.token, fr->new_token.tokenlen, rtb->frc_objalloc, +        rtb->mem); +      if (rv != 0) { +        return rv; +      } + +      rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); +      if (rv != 0) { +        return rv; +      } + +      ++num_reclaimed; + +      nfrc->next = *pfrc; +      *pfrc = nfrc; +      pfrc = &nfrc->next; + +      continue; +    case NGTCP2_FRAME_DATAGRAM: +    case NGTCP2_FRAME_DATAGRAM_LEN: +      continue; +    case NGTCP2_FRAME_RESET_STREAM: +      strm = ngtcp2_conn_find_stream(conn, fr->reset_stream.stream_id); +      if (strm == NULL || !ngtcp2_strm_require_retransmit_reset_stream(strm)) { +        continue; +      } + +      break; +    case NGTCP2_FRAME_STOP_SENDING: +      strm = ngtcp2_conn_find_stream(conn, fr->stop_sending.stream_id); +      if (strm == NULL || !ngtcp2_strm_require_retransmit_stop_sending(strm)) { +        continue; +      } + +      break; +    case NGTCP2_FRAME_MAX_STREAM_DATA: +      strm = ngtcp2_conn_find_stream(conn, fr->max_stream_data.stream_id); +      if (strm == NULL || !ngtcp2_strm_require_retransmit_max_stream_data( +                            strm, &fr->max_stream_data)) { +        continue; +      } + +      break; +    case NGTCP2_FRAME_STREAM_DATA_BLOCKED: +      strm = ngtcp2_conn_find_stream(conn, fr->stream_data_blocked.stream_id); +      if (strm == NULL || !ngtcp2_strm_require_retransmit_stream_data_blocked( +                            strm, &fr->stream_data_blocked)) { +        continue; +      } + +      break; +    } + +    rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc); +    if (rv != 0) { +      return rv; +    } + +    nfrc->fr = *fr; + +    rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); +    if (rv != 0) { +      return rv; +    } + +    ++num_reclaimed; + +    nfrc->next = *pfrc; +    *pfrc = nfrc; +    pfrc = &nfrc->next; +  } + +  return (ngtcp2_ssize)num_reclaimed; +} + +/* + * conn_process_lost_datagram calls ngtcp2_lost_datagram callback for + * lost DATAGRAM frames. + */ +static int conn_process_lost_datagram(ngtcp2_conn *conn, +                                      ngtcp2_rtb_entry *ent) { +  ngtcp2_frame_chain *frc; +  int rv; + +  for (frc = ent->frc; frc; frc = frc->next) { +    switch (frc->fr.type) { +    case NGTCP2_FRAME_DATAGRAM: +    case NGTCP2_FRAME_DATAGRAM_LEN: +      assert(conn->callbacks.lost_datagram); + +      rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, +                                         conn->user_data); +      if (rv != 0) { +        return NGTCP2_ERR_CALLBACK_FAILURE; +      } + +      break; +    } +  } + +  return 0; +} + +static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                           ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, +                           ngtcp2_pktns *pktns, ngtcp2_tstamp ts) { +  int rv; +  ngtcp2_ssize reclaimed; +  ngtcp2_cc *cc = rtb->cc; +  ngtcp2_cc_pkt pkt; + +  ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, +                      ent->ts); + +  if (rtb->qlog) { +    ngtcp2_qlog_pkt_lost(rtb->qlog, ent); +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +    ++rtb->num_lost_pmtud_pkts; +  } else if (rtb->cc->on_pkt_lost) { +    cc->on_pkt_lost(cc, cstat, +                    ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, +                                       pktns->id, ent->ts, ent->rst.lost, +                                       ent->rst.tx_in_flight, +                                       ent->rst.is_app_limited), +                    ts); +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "pkn=%" PRId64 " has already been reclaimed on PTO", +                    ent->hd.pkt_num); +    assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); +    assert(UINT64_MAX == ent->lost_ts); +  } else { +    if (conn->callbacks.lost_datagram && +        (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) { +      rv = conn_process_lost_datagram(conn, ent); +      if (rv != 0) { +        return rv; +      } +    } + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { +      assert(ent->frc); +      assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); +      assert(UINT64_MAX == ent->lost_ts); + +      reclaimed = +        rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent); +      if (reclaimed < 0) { +        return (int)reclaimed; +      } +    } +  } + +  ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; +  ent->lost_ts = ts; + +  ++rtb->num_lost_pkts; + +  return 0; +} + +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                   ngtcp2_conn_stat *cstat) { +  int rv; + +  rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent); +  if (rv != 0) { +    return rv; +  } + +  rtb_on_add(rtb, ent, cstat); + +  return 0; +} + +ngtcp2_ksl_it ngtcp2_rtb_head(const ngtcp2_rtb *rtb) { +  return ngtcp2_ksl_begin(&rtb->ents); +} + +static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, +                       ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent, +                       ngtcp2_conn_stat *cstat) { +  int rv; +  (void)rv; + +  rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num); +  assert(0 == rv); +  rtb_on_remove(rtb, ent, cstat); + +  assert(ent->next == NULL); + +  ngtcp2_list_insert(ent, pent); +} + +static void conn_ack_crypto_data(ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                                 uint64_t datalen) { +  ngtcp2_buf_chain **pbufchain, *bufchain; +  size_t left; + +  for (pbufchain = &pktns->crypto.tx.data; *pbufchain;) { +    left = ngtcp2_buf_len(&(*pbufchain)->buf); +    if (left > datalen) { +      (*pbufchain)->buf.pos += datalen; +      return; +    } + +    bufchain = *pbufchain; +    *pbufchain = bufchain->next; + +    ngtcp2_mem_free(conn->mem, bufchain); + +    datalen -= left; + +    if (datalen == 0) { +      return; +    } +  } + +  assert(datalen == 0); + +  return; +} + +static int process_acked_pkt(ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, +                             ngtcp2_pktns *pktns) { +  ngtcp2_frame_chain *frc; +  uint64_t prev_stream_offset, stream_offset; +  ngtcp2_strm *strm; +  int rv; +  uint64_t datalen; +  ngtcp2_strm *crypto = &pktns->crypto.strm; + +  if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud && +      conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) { +    ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen); + +    if (conn->dcid.current.max_udp_payload_size < ent->pktlen) { +      conn->dcid.current.max_udp_payload_size = ent->pktlen; +      conn->cstat.max_tx_udp_payload_size = +        ngtcp2_conn_get_path_max_tx_udp_payload_size(conn); +    } + +    if (ngtcp2_pmtud_finished(conn->pmtud)) { +      ngtcp2_conn_stop_pmtud(conn); +    } +  } + +  for (frc = ent->frc; frc; frc = frc->next) { +    if (frc->binder) { +      if (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK) { +        continue; +      } + +      frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK; +    } + +    switch (frc->fr.type) { +    case NGTCP2_FRAME_STREAM: +      strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id); +      if (strm == NULL) { +        break; +      } + +      strm->flags |= NGTCP2_STRM_FLAG_ANY_ACKED; + +      if (frc->fr.stream.fin) { +        strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; +      } + +      prev_stream_offset = ngtcp2_strm_get_acked_offset(strm); + +      rv = ngtcp2_strm_ack_data( +        strm, frc->fr.stream.offset, +        ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); +      if (rv != 0) { +        return rv; +      } + +      if (conn->callbacks.acked_stream_data_offset) { +        stream_offset = ngtcp2_strm_get_acked_offset(strm); + +        datalen = stream_offset - prev_stream_offset; +        if (datalen == 0 && !frc->fr.stream.fin) { +          break; +        } + +        rv = conn->callbacks.acked_stream_data_offset( +          conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data, +          strm->stream_user_data); +        if (rv != 0) { +          return NGTCP2_ERR_CALLBACK_FAILURE; +        } +      } + +      rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +      if (rv != 0) { +        return rv; +      } + +      break; +    case NGTCP2_FRAME_CRYPTO: +      prev_stream_offset = ngtcp2_strm_get_acked_offset(crypto); + +      rv = ngtcp2_strm_ack_data( +        crypto, frc->fr.stream.offset, +        ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); +      if (rv != 0) { +        return rv; +      } + +      stream_offset = ngtcp2_strm_get_acked_offset(crypto); + +      datalen = stream_offset - prev_stream_offset; +      if (datalen == 0) { +        break; +      } + +      conn_ack_crypto_data(conn, pktns, datalen); + +      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_RESET_STREAM_ACKED; + +      rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); +      if (rv != 0) { +        return rv; +      } + +      break; +    case NGTCP2_FRAME_RETIRE_CONNECTION_ID: +      ngtcp2_conn_untrack_retired_dcid_seq(conn, +                                           frc->fr.retire_connection_id.seq); +      break; +    case NGTCP2_FRAME_NEW_CONNECTION_ID: +      assert(conn->scid.num_in_flight); + +      --conn->scid.num_in_flight; + +      break; +    case NGTCP2_FRAME_DATAGRAM: +    case NGTCP2_FRAME_DATAGRAM_LEN: +      if (!conn->callbacks.ack_datagram) { +        break; +      } + +      rv = conn->callbacks.ack_datagram(conn, frc->fr.datagram.dgram_id, +                                        conn->user_data); +      if (rv != 0) { +        return NGTCP2_ERR_CALLBACK_FAILURE; +      } + +      break; +    } +  } + +  return 0; +} + +static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                             ngtcp2_conn_stat *cstat, const ngtcp2_pktns *pktns, +                             ngtcp2_tstamp ts) { +  ngtcp2_cc *cc = rtb->cc; +  ngtcp2_cc_pkt pkt; + +  ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); + +  if (cc->on_pkt_acked) { +    cc->on_pkt_acked(cc, cstat, +                     ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, +                                        pktns->id, ent->ts, ent->rst.lost, +                                        ent->rst.tx_in_flight, +                                        ent->rst.is_app_limited), +                     ts); +  } + +  if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) && +      (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { +    cstat->pto_count = 0; +  } +} + +static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                            ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, +                            const ngtcp2_ack *fr, size_t ecn_acked, +                            ngtcp2_tstamp largest_pkt_sent_ts, +                            ngtcp2_tstamp ts) { +  if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) { +    return; +  } + +  if ((ecn_acked && fr->type == NGTCP2_FRAME_ACK) || +      (fr->type == NGTCP2_FRAME_ACK_ECN && +       (pktns->rx.ecn.ack.ect0 > fr->ecn.ect0 || +        pktns->rx.ecn.ack.ect1 > fr->ecn.ect1 || +        pktns->rx.ecn.ack.ce > fr->ecn.ce || +        (fr->ecn.ect0 - pktns->rx.ecn.ack.ect0) + +            (fr->ecn.ce - pktns->rx.ecn.ack.ce) < +          ecn_acked || +        fr->ecn.ect0 > pktns->tx.ecn.ect0 || fr->ecn.ect1))) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, +                    "path is not ECN capable"); +    conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED; + +    return; +  } + +  if (conn->tx.ecn.state != NGTCP2_ECN_STATE_CAPABLE && ecn_acked) { +    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "path is ECN capable"); +    conn->tx.ecn.state = NGTCP2_ECN_STATE_CAPABLE; +  } + +  if (fr->type == NGTCP2_FRAME_ACK_ECN) { +    if (cc->congestion_event && largest_pkt_sent_ts != UINT64_MAX && +        fr->ecn.ce > pktns->rx.ecn.ack.ce) { +      cc->congestion_event(cc, cstat, largest_pkt_sent_ts, 0, ts); +    } + +    pktns->rx.ecn.ack.ect0 = fr->ecn.ect0; +    pktns->rx.ecn.ack.ect1 = fr->ecn.ect1; +    pktns->rx.ecn.ack.ce = fr->ecn.ce; +  } +} + +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, +                               ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                               ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, +                                 ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, +                                 ngtcp2_pktns *pktns, 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; +  size_t num_acked = 0; +  ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX; +  int64_t pkt_num; +  ngtcp2_cc *cc = rtb->cc; +  ngtcp2_rtb_entry *acked_ent = NULL; +  int ack_eliciting_pkt_acked = 0; +  size_t ecn_acked = 0; +  int verify_ecn = 0; +  ngtcp2_cc_ack cc_ack = {0}; +  size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts; + +  cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight; +  cc_ack.rtt = UINT64_MAX; + +  if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && +      (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) && +      largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { +    conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | +                                NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); +    conn->crypto.key_update.confirmed_ts = ts; + +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); +  } + +  if (rtb->largest_acked_tx_pkt_num < largest_ack) { +    rtb->largest_acked_tx_pkt_num = largest_ack; +    verify_ecn = 1; +  } + +  /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ +  it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); +  if (ngtcp2_ksl_it_end(&it)) { +    if (conn && verify_ecn) { +      conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, +                      largest_pkt_sent_ts, ts); +    } + +    return 0; +  } + +  min_ack = largest_ack - (int64_t)fr->first_ack_range; + +  for (; !ngtcp2_ksl_it_end(&it);) { +    pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); + +    assert(pkt_num <= largest_ack); + +    if (pkt_num < min_ack) { +      break; +    } + +    ent = ngtcp2_ksl_it_get(&it); + +    if (largest_ack == pkt_num) { +      largest_pkt_sent_ts = ent->ts; +    } + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +      ack_eliciting_pkt_acked = 1; +    } + +    rtb_remove(rtb, &it, &acked_ent, ent, cstat); +    ++num_acked; +  } + +  for (i = 0; i < fr->rangecnt; ++i) { +    largest_ack = min_ack - (int64_t)fr->ranges[i].gap - 2; +    min_ack = largest_ack - (int64_t)fr->ranges[i].len; + +    it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); +    if (ngtcp2_ksl_it_end(&it)) { +      break; +    } + +    for (; !ngtcp2_ksl_it_end(&it);) { +      pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); +      if (pkt_num < min_ack) { +        break; +      } + +      ent = ngtcp2_ksl_it_get(&it); + +      if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { +        ack_eliciting_pkt_acked = 1; +      } + +      rtb_remove(rtb, &it, &acked_ent, ent, cstat); +      ++num_acked; +    } +  } + +  if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { +    cc_ack.rtt = pkt_ts - largest_pkt_sent_ts; + +    rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); +    if (rv == 0 && cc->new_rtt_sample) { +      cc->new_rtt_sample(cc, cstat, ts); +    } +  } + +  if (conn) { +    for (ent = acked_ent; ent; ent = acked_ent) { +      if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && +          (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) { +        ++ecn_acked; +      } + +      rv = process_acked_pkt(ent, conn, pktns); +      if (rv != 0) { +        goto fail; +      } + +      if (ent->hd.pkt_num >= rtb->cc_pkt_num) { +        assert(cc_ack.pkt_delivered <= ent->rst.delivered); + +        cc_ack.bytes_delivered += ent->pktlen; +        cc_ack.pkt_delivered = ent->rst.delivered; +      } + +      rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); +      acked_ent = ent->next; +      ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                    rtb->frc_objalloc, rtb->mem); +    } + +    if (verify_ecn) { +      conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, +                      largest_pkt_sent_ts, ts); +    } +  } else { +    /* For unit tests */ +    for (ent = acked_ent; ent; ent = acked_ent) { +      rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); +      acked_ent = ent->next; +      ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                    rtb->frc_objalloc, rtb->mem); +    } +  } + +  if (rtb->cc->on_spurious_congestion && num_lost_pkts && +      rtb->num_lost_pkts == rtb->num_lost_pmtud_pkts) { +    rtb->cc->on_spurious_congestion(cc, cstat, ts); +  } + +  if (num_acked) { +    ngtcp2_rst_on_ack_recv(rtb->rst, cstat); + +    if (conn) { +      rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts); +      if (rv != 0) { +        return rv; +      } +    } +  } + +  rtb->rst->lost += cc_ack.bytes_lost; + +  cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts; +  if (num_acked && cc->on_ack_recv) { +    cc->on_ack_recv(cc, cstat, &cc_ack, ts); +  } + +  return (ngtcp2_ssize)num_acked; + +fail: +  for (ent = acked_ent; ent; ent = acked_ent) { +    acked_ent = ent->next; +    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                  rtb->frc_objalloc, rtb->mem); +  } + +  return rv; +} + +static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, +                        const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay, +                        size_t pkt_thres, const ngtcp2_pktns *pktns, +                        ngtcp2_tstamp ts) { +  ngtcp2_tstamp loss_time; + +  if (ngtcp2_tstamp_elapsed(ent->ts, loss_delay, ts) || +      rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) { +    return 1; +  } + +  loss_time = cstat->loss_time[pktns->id]; + +  if (loss_time == UINT64_MAX) { +    loss_time = ent->ts + loss_delay; +  } else { +    loss_time = ngtcp2_min_uint64(loss_time, ent->ts + loss_delay); +  } + +  cstat->loss_time[pktns->id] = loss_time; + +  return 0; +} + +/* + * compute_pkt_loss_delay computes loss delay. + */ +static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_conn_stat *cstat) { +  /* 9/8 is kTimeThreshold */ +  ngtcp2_duration loss_delay = +    ngtcp2_max_uint64(cstat->latest_rtt, cstat->smoothed_rtt) * 9 / 8; +  return ngtcp2_max_uint64(loss_delay, NGTCP2_GRANULARITY); +} + +/* + * conn_all_ecn_pkt_lost returns nonzero if all ECN QUIC packets are + * lost during validation period. + */ +static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) { +  ngtcp2_pktns *in_pktns = conn->in_pktns; +  ngtcp2_pktns *hs_pktns = conn->hs_pktns; +  ngtcp2_pktns *pktns = &conn->pktns; + +  return (!in_pktns || in_pktns->tx.ecn.validation_pkt_sent == +                         in_pktns->tx.ecn.validation_pkt_lost) && +         (!hs_pktns || hs_pktns->tx.ecn.validation_pkt_sent == +                         hs_pktns->tx.ecn.validation_pkt_lost) && +         pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost; +} + +static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, +                               ngtcp2_conn *conn, ngtcp2_pktns *pktns, +                               ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { +  ngtcp2_rtb_entry *ent; +  ngtcp2_duration loss_delay; +  ngtcp2_ksl_it it; +  ngtcp2_tstamp latest_ts, oldest_ts; +  int64_t last_lost_pkt_num; +  ngtcp2_duration loss_window, congestion_period; +  ngtcp2_cc *cc = rtb->cc; +  int rv; +  uint64_t pkt_thres = +    rtb->cc_bytes_in_flight / cstat->max_tx_udp_payload_size / 2; +  size_t ecn_pkt_lost = 0; +  ngtcp2_tstamp start_ts; +  ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns); +  uint64_t bytes_lost = 0; +  ngtcp2_duration max_ack_delay; + +  pkt_thres = ngtcp2_max_uint64(pkt_thres, NGTCP2_PKT_THRESHOLD); +  pkt_thres = ngtcp2_min_uint64(pkt_thres, 256); +  cstat->loss_time[pktns->id] = UINT64_MAX; +  loss_delay = compute_pkt_loss_delay(cstat); + +  it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num); +  for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { +    ent = ngtcp2_ksl_it_get(&it); + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { +      break; +    } + +    if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, pktns, +                     ts)) { +      /* All entries from ent are considered to be lost. */ +      latest_ts = oldest_ts = ent->ts; +      /* +1 to pick this packet for persistent congestion in the +         following loop. */ +      last_lost_pkt_num = ent->hd.pkt_num + 1; +      max_ack_delay = conn->remote.transport_params +                        ? conn->remote.transport_params->max_ack_delay +                        : 0; + +      congestion_period = +        (cstat->smoothed_rtt + +         ngtcp2_max_uint64(4 * cstat->rttvar, NGTCP2_GRANULARITY) + +         max_ack_delay) * +        NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + +      start_ts = ngtcp2_max_uint64(rtb->persistent_congestion_start_ts, +                                   cstat->first_rtt_sample_ts); + +      for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { +        ent = ngtcp2_ksl_it_get(&it); + +        if (last_lost_pkt_num == ent->hd.pkt_num + 1 && ent->ts >= start_ts) { +          last_lost_pkt_num = ent->hd.pkt_num; +          oldest_ts = ent->ts; +        } else { +          last_lost_pkt_num = -1; +        } + +        if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) { +          if (pktns->id != NGTCP2_PKTNS_ID_APPLICATION || +              last_lost_pkt_num == -1 || +              latest_ts - oldest_ts >= congestion_period) { +            break; +          } + +          continue; +        } + +        if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && +            (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) { +          ++ecn_pkt_lost; +        } + +        bytes_lost += rtb_on_remove(rtb, ent, cstat); +        rv = rtb_on_pkt_lost(rtb, ent, cstat, conn, pktns, ts); +        if (rv != 0) { +          return rv; +        } +      } + +      /* If only PMTUD packets are lost, do not trigger congestion +         event. */ +      if (bytes_lost == 0) { +        break; +      } + +      switch (conn->tx.ecn.state) { +      case NGTCP2_ECN_STATE_TESTING: +        if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { +          break; +        } + +        if (ts - conn->tx.ecn.validation_start_ts < 3 * pto) { +          pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost; +          assert(pktns->tx.ecn.validation_pkt_sent >= +                 pktns->tx.ecn.validation_pkt_lost); +          break; +        } + +        conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; + +        /* fall through */ +      case NGTCP2_ECN_STATE_UNKNOWN: +        pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost; +        assert(pktns->tx.ecn.validation_pkt_sent >= +               pktns->tx.ecn.validation_pkt_lost); +        if (conn_all_ecn_pkt_lost(conn)) { +          conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED; +        } +        break; +      default: +        break; +      } + +      if (cc->congestion_event) { +        cc->congestion_event(cc, cstat, latest_ts, bytes_lost, ts); +      } + +      loss_window = latest_ts - oldest_ts; +      /* Persistent congestion situation is only evaluated for app +       * packet number space and for the packets sent after handshake +       * is confirmed.  During handshake, there is not much packets +       * sent and also people seem to do lots of effort not to trigger +       * persistent congestion there, then it is a lot easier to just +       * not enable it during handshake. +       */ +      if (pktns->id == NGTCP2_PKTNS_ID_APPLICATION && loss_window && +          loss_window >= congestion_period) { +        ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                        "persistent congestion loss_window=%" PRIu64 +                        " congestion_period=%" PRIu64, +                        loss_window, congestion_period); + +        /* Reset min_rtt, srtt, and rttvar here.  Next new RTT +           sample will be used to recalculate these values. */ +        cstat->min_rtt = UINT64_MAX; +        cstat->smoothed_rtt = conn->local.settings.initial_rtt; +        cstat->rttvar = conn->local.settings.initial_rtt / 2; +        cstat->first_rtt_sample_ts = UINT64_MAX; + +        if (cc->on_persistent_congestion) { +          cc->on_persistent_congestion(cc, cstat, ts); +        } +      } + +      break; +    } +  } + +  ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres); + +  if (ppkt_lost) { +    *ppkt_lost = bytes_lost; +  } + +  return 0; +} + +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                               ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, +                               ngtcp2_tstamp ts) { +  return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat, +                             ts); +} + +void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { +  ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents); +  ngtcp2_rtb_entry *ent; +  int rv; +  (void)rv; + +  for (; rtb->num_lost_pkts > n;) { +    assert(ngtcp2_ksl_it_end(&it)); +    ngtcp2_ksl_it_prev(&it); +    ent = ngtcp2_ksl_it_get(&it); + +    assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED); + +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); + +    --rtb->num_lost_pkts; + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +      --rtb->num_lost_pmtud_pkts; +    } + +    rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); +    assert(0 == rv); +    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                  rtb->frc_objalloc, rtb->mem); +  } +} + +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +                                        ngtcp2_tstamp ts) { +  ngtcp2_ksl_it it; +  ngtcp2_rtb_entry *ent; +  int rv; +  (void)rv; + +  if (ngtcp2_ksl_len(&rtb->ents) == 0) { +    return; +  } + +  it = ngtcp2_ksl_end(&rtb->ents); + +  for (;;) { +    assert(ngtcp2_ksl_it_end(&it)); + +    ngtcp2_ksl_it_prev(&it); +    ent = ngtcp2_ksl_it_get(&it); + +    if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) || +        ts - ent->lost_ts < pto) { +      return; +    } + +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); + +    --rtb->num_lost_pkts; + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +      --rtb->num_lost_pmtud_pkts; +    } + +    rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); +    assert(0 == rv); +    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                  rtb->frc_objalloc, rtb->mem); + +    if (ngtcp2_ksl_len(&rtb->ents) == 0) { +      return; +    } +  } +} + +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb) { +  ngtcp2_ksl_it it; +  ngtcp2_rtb_entry *ent; + +  if (ngtcp2_ksl_len(&rtb->ents) == 0) { +    return UINT64_MAX; +  } + +  it = ngtcp2_ksl_end(&rtb->ents); +  ngtcp2_ksl_it_prev(&it); +  ent = ngtcp2_ksl_it_get(&it); + +  if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) { +    return UINT64_MAX; +  } + +  return ent->lost_ts; +} + +static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                                        ngtcp2_pktns *pktns, +                                        ngtcp2_rtb_entry *ent) { +  ngtcp2_frame_chain **pfrc, *frc; +  ngtcp2_stream *sfr; +  ngtcp2_strm *strm; +  int rv; + +  ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, +                      ent->ts); + +  if (rtb->qlog) { +    ngtcp2_qlog_pkt_lost(rtb->qlog, ent); +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) { +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "pkn=%" PRId64 +                    " is a probe packet, no retransmission is necessary", +                    ent->hd.pkt_num); +    return 0; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "pkn=%" PRId64 +                    " is a PMTUD probe packet, no retransmission is necessary", +                    ent->hd.pkt_num); +    return 0; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { +    --rtb->num_lost_pkts; + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { +      --rtb->num_lost_pmtud_pkts; +    } + +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "pkn=%" PRId64 +                    " was declared lost and has already been retransmitted", +                    ent->hd.pkt_num); +    return 0; +  } + +  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { +    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, +                    "pkn=%" PRId64 " has already been reclaimed on PTO", +                    ent->hd.pkt_num); +    return 0; +  } + +  if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && +      (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || +       !conn->callbacks.lost_datagram)) { +    /* PADDING only (or PADDING + ACK ) packets will have NULL +       ent->frc. */ +    return 0; +  } + +  pfrc = &ent->frc; + +  for (; *pfrc;) { +    switch ((*pfrc)->fr.type) { +    case NGTCP2_FRAME_STREAM: +      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_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); +        break; +      } + +      rv = ngtcp2_strm_streamfrq_push(strm, frc); +      if (rv != 0) { +        ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); +        return rv; +      } + +      if (!ngtcp2_strm_is_tx_queued(strm)) { +        strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); +        rv = ngtcp2_conn_tx_strmq_push(conn, strm); +        if (rv != 0) { +          return rv; +        } +      } + +      break; +    case NGTCP2_FRAME_CRYPTO: +      frc = *pfrc; + +      *pfrc = frc->next; +      frc->next = NULL; + +      rv = ngtcp2_strm_streamfrq_push(&pktns->crypto.strm, frc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); +        return rv; +      } + +      break; +    case NGTCP2_FRAME_DATAGRAM: +    case NGTCP2_FRAME_DATAGRAM_LEN: +      frc = *pfrc; + +      if (conn->callbacks.lost_datagram) { +        rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id, +                                           conn->user_data); +        if (rv != 0) { +          return NGTCP2_ERR_CALLBACK_FAILURE; +        } +      } + +      *pfrc = (*pfrc)->next; + +      ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem); + +      break; +    default: +      pfrc = &(*pfrc)->next; +    } +  } + +  *pfrc = pktns->tx.frq; +  pktns->tx.frq = ent->frc; +  ent->frc = NULL; + +  return 0; +} + +int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                          ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { +  ngtcp2_rtb_entry *ent; +  ngtcp2_ksl_it it; +  int rv; + +  it = ngtcp2_ksl_begin(&rtb->ents); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    ent = ngtcp2_ksl_it_get(&it); + +    rtb_on_remove(rtb, ent, cstat); +    rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); +    assert(0 == rv); + +    rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); + +    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                  rtb->frc_objalloc, rtb->mem); + +    if (rv != 0) { +      return rv; +    } +  } + +  return 0; +} + +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) { +  ngtcp2_rtb_entry *ent; +  ngtcp2_ksl_it it; +  int rv; +  (void)rv; + +  it = ngtcp2_ksl_begin(&rtb->ents); + +  for (; !ngtcp2_ksl_it_end(&it);) { +    ent = ngtcp2_ksl_it_get(&it); + +    if (ent->hd.type != NGTCP2_PKT_0RTT) { +      ngtcp2_ksl_it_next(&it); +      continue; +    } + +    rtb_on_remove(rtb, ent, cstat); +    rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); +    assert(0 == rv); + +    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, +                                  rtb->frc_objalloc, rtb->mem); +  } +} + +int ngtcp2_rtb_empty(const ngtcp2_rtb *rtb) { +  return ngtcp2_ksl_len(&rtb->ents) == 0; +} + +void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num) { +  rtb->cc_pkt_num = cc_pkt_num; +  rtb->cc_bytes_in_flight = 0; +} + +ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                                       ngtcp2_pktns *pktns, size_t num_pkts) { +  ngtcp2_ksl_it it; +  ngtcp2_rtb_entry *ent; +  ngtcp2_ssize reclaimed; +  size_t atmost = num_pkts; + +  it = ngtcp2_ksl_end(&rtb->ents); +  for (; !ngtcp2_ksl_it_begin(&it) && num_pkts;) { +    ngtcp2_ksl_it_prev(&it); +    ent = ngtcp2_ksl_it_get(&it); + +    if ((ent->flags & (NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED | +                       NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) || +        !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE)) { +      continue; +    } + +    assert(ent->frc); + +    reclaimed = +      rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent); +    if (reclaimed < 0) { +      return reclaimed; +    } + +    /* Mark ent reclaimed even if reclaimed == 0 so that we can skip +       it in the next run. */ +    ent->flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED; + +    assert(rtb->num_retransmittable); +    --rtb->num_retransmittable; + +    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) { +      ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING; +      assert(rtb->num_pto_eliciting); +      --rtb->num_pto_eliciting; +    } + +    if (reclaimed) { +      --num_pkts; +    } +  } + +  return (ngtcp2_ssize)(atmost - num_pkts); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_rtb.h b/contrib/libs/ngtcp2/lib/ngtcp2_rtb.h new file mode 100644 index 00000000000..768f4abc42a --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_rtb.h @@ -0,0 +1,320 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RTB_H +#define NGTCP2_RTB_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" +#include "ngtcp2_objalloc.h" +#include "ngtcp2_pktns_id.h" + +typedef struct ngtcp2_conn ngtcp2_conn; +typedef struct ngtcp2_pktns ngtcp2_pktns; +typedef struct ngtcp2_log ngtcp2_log; +typedef struct ngtcp2_qlog ngtcp2_qlog; +typedef struct ngtcp2_strm ngtcp2_strm; +typedef struct ngtcp2_rst ngtcp2_rst; +typedef struct ngtcp2_cc ngtcp2_cc; +typedef struct ngtcp2_conn_stat ngtcp2_conn_stat; +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u +/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a +   probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u +/* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry +   includes a frame which must be retransmitted until it is +   acknowledged.  In most cases, this flag is used along with +   NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and +   NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */ +#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u +/* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry +   elicits acknowledgement. */ +#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u +/* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has +   been reclaimed on PTO.  It is not marked lost yet and still +   consumes congestion window. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u +/* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry +   has been marked lost and, optionally, scheduled to retransmit. */ +#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u +/* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a +   UDP datagram with ECN marking. */ +#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u +/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes +   DATAGRAM frame. */ +#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u +/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes +   a PMTUD probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u +/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry +   includes a packet which elicits PTO probe packets. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u + +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 acknowledgement. + */ +struct ngtcp2_rtb_entry { +  union { +    struct { +      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 remote endpoint. */ +      ngtcp2_tstamp ts; +      /* lost_ts is the time when this entry is declared to be +         lost. */ +      ngtcp2_tstamp lost_ts; +      /* pktlen is the length of QUIC packet */ +      size_t pktlen; +      struct { +        uint64_t delivered; +        ngtcp2_tstamp delivered_ts; +        ngtcp2_tstamp first_sent_ts; +        uint64_t tx_in_flight; +        uint64_t lost; +        int64_t end_seq; +        int is_app_limited; +      } rst; +      /* flags is bitwise-OR of zero or more of +         NGTCP2_RTB_ENTRY_FLAG_*. */ +      uint16_t flags; +    }; + +    ngtcp2_opl_entry oplent; +  }; +}; + +ngtcp2_objalloc_decl(rtb_entry, ngtcp2_rtb_entry, oplent); + +/* + * ngtcp2_rtb_entry_objalloc_new allocates ngtcp2_rtb_entry object via + * |objalloc|, and assigns its pointer to |*pent|. + */ +int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, +                                  const ngtcp2_pkt_hd *hd, +                                  ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, +                                  size_t pktlen, uint16_t flags, +                                  ngtcp2_objalloc *objalloc); + +/* + * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse. + * ngtcp2_frame_chain linked from ent->frc are also added to + * |frc_objalloc| depending on their frame type and size. + */ +void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent, +                                   ngtcp2_objalloc *objalloc, +                                   ngtcp2_objalloc *frc_objalloc, +                                   const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb tracks sent packets, and its acknowledgement timeout for + * retransmission. + */ +typedef struct ngtcp2_rtb { +  ngtcp2_objalloc *frc_objalloc; +  ngtcp2_objalloc *rtb_entry_objalloc; +  /* ents includes ngtcp2_rtb_entry sorted by decreasing order of +     packet number. */ +  ngtcp2_ksl ents; +  ngtcp2_rst *rst; +  ngtcp2_cc *cc; +  ngtcp2_log *log; +  ngtcp2_qlog *qlog; +  const ngtcp2_mem *mem; +  /* largest_acked_tx_pkt_num is the largest packet number +     acknowledged by a remote endpoint. */ +  int64_t largest_acked_tx_pkt_num; +  /* num_ack_eliciting is the number of ACK eliciting entries in +     ents. */ +  size_t num_ack_eliciting; +  /* num_retransmittable is the number of packets which contain frames +     that must be retransmitted on loss in ents. */ +  size_t num_retransmittable; +  /* num_pto_eliciting is the number of packets that elicit PTO probe +     packets in ents. */ +  size_t num_pto_eliciting; +  /* probe_pkt_left is the number of probe packet to send */ +  size_t probe_pkt_left; +  /* cc_pkt_num is the smallest packet number that is contributed to +     ngtcp2_conn_stat.bytes_in_flight. */ +  int64_t cc_pkt_num; +  /* cc_bytes_in_flight is the number of in-flight bytes that is +     contributed to ngtcp2_conn_stat.bytes_in_flight.  It only +     includes the bytes after congestion state is reset, that is only +     count a packet whose packet number is greater than or equals to +     cc_pkt_num. */ +  uint64_t cc_bytes_in_flight; +  /* persistent_congestion_start_ts is the time when persistent +     congestion evaluation is started.  It happens roughly after +     handshake is confirmed. */ +  ngtcp2_tstamp persistent_congestion_start_ts; +  /* num_lost_pkts is the number entries in ents which has +     NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ +  size_t num_lost_pkts; +  /* num_lost_pmtud_pkts is the number of entries in ents which have +     both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and +     NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */ +  size_t num_lost_pmtud_pkts; +} ngtcp2_rtb; + +/* + * ngtcp2_rtb_init initializes |rtb|. + */ +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_rst *rst, ngtcp2_cc *cc, +                     int64_t cc_pkt_num, ngtcp2_log *log, ngtcp2_qlog *qlog, +                     ngtcp2_objalloc *rtb_entry_objalloc, +                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb_free deallocates resources allocated for |rtb|. + */ +void ngtcp2_rtb_free(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_add adds |ent| to |rtb|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, +                   ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_rtb_head returns the iterator which points to the entry + * which has the largest packet number.  If there is no entry, + * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. + */ +ngtcp2_ksl_it ngtcp2_rtb_head(const ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_recv_ack removes an acknowledged ngtcp2_rtb_entry from + * |rtb|.  |pkt_num| is a packet number which includes |fr|.  |pkt_ts| + * is the timestamp when packet is received.  |ts| should be the + * current time.  Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of newly acknowledged packets if + * it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + *     User callback failed + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, +                                 ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, +                                 ngtcp2_pktns *pktns, 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. + */ +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                               ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, +                               ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. + */ +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +                                        ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_lost_pkt_ts returns the timestamp when an oldest lost + * packet tracked by |rtb| was declared lost.  It returns UINT64_MAX + * if no such packet exists. + */ +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_remove_all removes all packets from |rtb|, and prepends + * all frames to |*pfrc|.  Even when this function fails, some frames + * might be prepended to |*pfrc|, and the caller should handle them. + */ +int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                          ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets. + */ +void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_rtb_empty returns nonzero if |rtb| has no entry. + */ +int ngtcp2_rtb_empty(const ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_reset_cc_state resets congestion state in |rtb|. + * |cc_pkt_num| is the next outbound packet number which is sent under + * new congestion state. + */ +void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num); + +/* + * ngtcp2_rtb_remove_excessive_lost_pkt ensures that the number of + * lost packets is at most |n|. + */ +void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n); + +/* + * ngtcp2_rtb_reclaim_on_pto reclaims up to |num_pkts| packets which + * are in-flight and not marked lost.  The reclaimed frames may be + * sent in a PTO probe packet. + * + * This function returns the number of packets reclaimed if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, +                                       ngtcp2_pktns *pktns, size_t num_pkts); + +#endif /* !defined(NGTCP2_RTB_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_settings.c b/contrib/libs/ngtcp2/lib/ngtcp2_settings.c new file mode 100644 index 00000000000..77a68bd112e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_settings.c @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2024 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_settings.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_unreachable.h" + +void ngtcp2_settings_default_versioned(int settings_version, +                                       ngtcp2_settings *settings) { +  size_t len = ngtcp2_settingslen_version(settings_version); + +  memset(settings, 0, len); + +  switch (settings_version) { +  case NGTCP2_SETTINGS_VERSION: +  case NGTCP2_SETTINGS_V1: +    settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; +    settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; +    settings->ack_thresh = 2; +    settings->max_tx_udp_payload_size = 1500 - 48; +    settings->handshake_timeout = UINT64_MAX; + +    break; +  } +} + +static void settings_copy(ngtcp2_settings *dest, const ngtcp2_settings *src, +                          int settings_version) { +  assert(settings_version != NGTCP2_SETTINGS_VERSION); + +  memcpy(dest, src, ngtcp2_settingslen_version(settings_version)); +} + +const ngtcp2_settings * +ngtcp2_settings_convert_to_latest(ngtcp2_settings *dest, int settings_version, +                                  const ngtcp2_settings *src) { +  if (settings_version == NGTCP2_SETTINGS_VERSION) { +    return src; +  } + +  ngtcp2_settings_default(dest); + +  settings_copy(dest, src, settings_version); + +  return dest; +} + +void ngtcp2_settings_convert_to_old(int settings_version, ngtcp2_settings *dest, +                                    const ngtcp2_settings *src) { +  assert(settings_version != NGTCP2_SETTINGS_VERSION); + +  settings_copy(dest, src, settings_version); +} + +size_t ngtcp2_settingslen_version(int settings_version) { +  ngtcp2_settings settings; + +  switch (settings_version) { +  case NGTCP2_SETTINGS_VERSION: +    return sizeof(settings); +  case NGTCP2_SETTINGS_V1: +    return offsetof(ngtcp2_settings, initial_pkt_num) + +           sizeof(settings.initial_pkt_num); +  default: +    ngtcp2_unreachable(); +  } +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_settings.h b/contrib/libs/ngtcp2/lib/ngtcp2_settings.h new file mode 100644 index 00000000000..80466d43e47 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_settings.h @@ -0,0 +1,73 @@ +/* + * ngtcp2 + * + * Copyright (c) 2024 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_SETTINGS_H +#define NGTCP2_SETTINGS_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_settings_convert_to_latest converts |src| of version + * |settings_version| to the latest version NGTCP2_SETTINGS_VERSION. + * + * |dest| must point to the latest version.  |src| may be the older + * version, and if so, it may have fewer fields.  Accessing those + * fields causes undefined behavior. + * + * If |settings_version| == NGTCP2_SETTINGS_VERSION, no conversion is + * made, and |src| is returned.  Otherwise, first |dest| is + * initialized via ngtcp2_settings_default, and then all valid fields + * in |src| are copied into |dest|.  Finally, |dest| is returned. + */ +const ngtcp2_settings * +ngtcp2_settings_convert_to_latest(ngtcp2_settings *dest, int settings_version, +                                  const ngtcp2_settings *src); + +/* + * ngtcp2_settings_convert_to_old converts |src| of the latest version + * to |dest| of version |settings_version|. + * + * |settings_version| must not be the latest version + *  NGTCP2_SETTINGS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version |settings_version| + * from |src| to |dest|. + */ +void ngtcp2_settings_convert_to_old(int settings_version, ngtcp2_settings *dest, +                                    const ngtcp2_settings *src); + +/* + * ngtcp2_settingslen_version returns the effective length of + * ngtcp2_settings at the version |settings_version|. + */ +size_t ngtcp2_settingslen_version(int settings_version); + +#endif /* !defined(NGTCP2_SETTINGS_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_str.c b/contrib/libs/ngtcp2/lib/ngtcp2_str.c new file mode 100644 index 00000000000..a61636d188f --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_str.c @@ -0,0 +1,233 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_str.h" + +#include <string.h> + +#include "ngtcp2_macro.h" + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n) { +  memcpy(dest, src, n); +  return (uint8_t *)dest + n; +} + +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) { +  memset(dest, b, n); +  return dest + n; +} + +const void *ngtcp2_get_bytes(void *dest, const void *src, size_t n) { +  memcpy(dest, src, n); +  return (uint8_t *)src + 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; +} + +/* + * write_uint writes |n| to the buffer pointed by |p| in decimal + * representation.  It returns |p| plus the number of bytes written. + * The function assumes that the buffer has enough capacity to contain + * a string. + */ +static uint8_t *write_uint(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; +} + +uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) { +  size_t i; +  uint8_t *p = dest; + +  p = write_uint(p, addr[0]); + +  for (i = 1; i < 4; ++i) { +    *p++ = '.'; +    p = write_uint(p, addr[i]); +  } + +  *p = '\0'; + +  return dest; +} + +/* + * write_hex_zsup writes the content of buffer pointed by |data| of + * length |len| to |dest| in hex string.  Any leading zeros are + * suppressed.  It returns |dest| plus the number of bytes written. + */ +static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) { +  size_t i; +  uint8_t *p = dest; +  uint8_t d; + +  for (i = 0; i < len; ++i) { +    d = data[i]; +    if (d >> 4) { +      break; +    } + +    d &= 0xf; + +    if (d) { +      *p++ = (uint8_t)LOWER_XDIGITS[d]; +      ++i; +      break; +    } +  } + +  if (p == dest && i == len) { +    *p++ = '0'; +    return p; +  } + +  for (; i < len; ++i) { +    d = data[i]; +    *p++ = (uint8_t)LOWER_XDIGITS[d >> 4]; +    *p++ = (uint8_t)LOWER_XDIGITS[d & 0xf]; +  } + +  return p; +} + +uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) { +  uint16_t blks[8]; +  size_t i; +  size_t zlen, zoff; +  size_t max_zlen = 0, max_zoff = 8; +  uint8_t *p = dest; + +  for (i = 0; i < 16; i += sizeof(uint16_t)) { +    /* Copy in network byte order. */ +    memcpy(&blks[i / sizeof(uint16_t)], addr + i, sizeof(uint16_t)); +  } + +  for (i = 0; i < 8;) { +    if (blks[i]) { +      ++i; +      continue; +    } + +    zlen = 1; +    zoff = i; + +    ++i; +    for (; i < 8 && blks[i] == 0; ++i, ++zlen) +      ; +    if (zlen > max_zlen) { +      max_zlen = zlen; +      max_zoff = zoff; +    } +  } + +  /* Do not suppress a single '0' block */ +  if (max_zlen == 1) { +    max_zoff = 8; +  } + +  if (max_zoff != 0) { +    p = write_hex_zsup(p, (const uint8_t *)blks, sizeof(uint16_t)); + +    for (i = 1; i < max_zoff; ++i) { +      *p++ = ':'; +      p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t)); +    } +  } + +  if (max_zoff != 8) { +    *p++ = ':'; + +    if (max_zoff + max_zlen == 8) { +      *p++ = ':'; +    } else { +      for (i = max_zoff + max_zlen; i < 8; ++i) { +        *p++ = ':'; +        p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t)); +      } +    } +  } + +  *p = '\0'; + +  return dest; +} + +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/contrib/libs/ngtcp2/lib/ngtcp2_str.h b/contrib/libs/ngtcp2/lib/ngtcp2_str.h new file mode 100644 index 00000000000..f970c153e80 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_str.h @@ -0,0 +1,94 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STR_H +#define NGTCP2_STR_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n); + +/* + * ngtcp2_setmem writes a string of length |n| consisting only |b| to + * the buffer pointed by |dest|.  It returns dest + n; + */ +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n); + +/* + * ngtcp2_get_bytes copies |n| bytes from |src| to |dest|, and returns + * |src| + |n|. + */ +const void *ngtcp2_get_bytes(void *dest, const void *src, 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_ipv4 encodes binary form IPv4 address stored in + * |addr| to human readable text form in the buffer pointed by |dest|. + * The capacity of buffer must have enough length to store a text form + * plus a terminating NULL byte.  The resulting text form ends with + * NULL byte.  The function returns |dest|. + */ +uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr); + +/* + * ngtcp2_encode_ipv6 encodes binary form IPv6 address stored in + * |addr| to human readable text form in the buffer pointed by |dest|. + * The capacity of buffer must have enough length to store a text form + * plus a terminating NULL byte.  The resulting text form ends with + * NULL byte.  The function produces the canonical form of IPv6 text + * representation described in + * https://tools.ietf.org/html/rfc5952#section-4.  The function + * returns |dest|. + */ +uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr); + +/* + * 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_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 /* !defined(NGTCP2_STR_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_strm.c b/contrib/libs/ngtcp2/lib/ngtcp2_strm.c new file mode 100644 index 00000000000..a30b052483d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_strm.c @@ -0,0 +1,778 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_strm.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_rtb.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_frame_chain.h" + +static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { +  return *(int64_t *)lhs < *(int64_t *)rhs; +} + +void 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, ngtcp2_objalloc *frc_objalloc, +                      const ngtcp2_mem *mem) { +  strm->pe.index = NGTCP2_PQ_BAD_INDEX; +  strm->cycle = 0; +  strm->frc_objalloc = frc_objalloc; +  strm->tx.acked_offset = NULL; +  strm->tx.cont_acked_offset = 0; +  strm->tx.streamfrq = NULL; +  strm->tx.offset = 0; +  strm->tx.max_offset = max_tx_offset; +  strm->tx.last_blocked_offset = UINT64_MAX; +  strm->tx.last_max_stream_data_ts = UINT64_MAX; +  strm->tx.loss_count = 0; +  strm->tx.last_lost_pkt_num = -1; +  strm->tx.stop_sending_app_error_code = 0; +  strm->tx.reset_stream_app_error_code = 0; +  strm->rx.rob = NULL; +  strm->rx.cont_offset = 0; +  strm->rx.last_offset = 0; +  strm->rx.max_offset = strm->rx.unsent_max_offset = strm->rx.window = +    max_rx_offset; +  strm->mem = mem; +  strm->stream_id = stream_id; +  strm->stream_user_data = stream_user_data; +  strm->flags = flags; +  strm->app_error_code = 0; +} + +void ngtcp2_strm_free(ngtcp2_strm *strm) { +  ngtcp2_ksl_it it; + +  if (strm == NULL) { +    return; +  } + +  if (strm->tx.streamfrq) { +    for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); +         ngtcp2_ksl_it_next(&it)) { +      ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it), +                                      strm->frc_objalloc, strm->mem); +    } + +    ngtcp2_ksl_free(strm->tx.streamfrq); +    ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); +  } + +  if (strm->rx.rob) { +    ngtcp2_rob_free(strm->rx.rob); +    ngtcp2_mem_free(strm->mem, strm->rx.rob); +  } + +  if (strm->tx.acked_offset) { +    ngtcp2_gaptr_free(strm->tx.acked_offset); +    ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); +  } +} + +static int strm_rob_init(ngtcp2_strm *strm) { +  int rv; +  ngtcp2_rob *rob = ngtcp2_mem_malloc(strm->mem, sizeof(*rob)); + +  if (rob == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  rv = ngtcp2_rob_init(rob, 8 * 1024, strm->mem); +  if (rv != 0) { +    ngtcp2_mem_free(strm->mem, rob); +    return rv; +  } + +  strm->rx.rob = rob; + +  return 0; +} + +uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm) { +  if (strm->rx.rob == NULL) { +    return strm->rx.cont_offset; +  } +  return ngtcp2_rob_first_gap_offset(strm->rx.rob); +} + +/* strm_rob_heavily_fragmented returns nonzero if the number of gaps +   in |rob| exceeds the limit. */ +static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) { +  return ngtcp2_ksl_len(&rob->gapksl) >= 5000; +} + +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, +                                size_t datalen, uint64_t offset) { +  int rv; + +  if (strm->rx.rob == NULL) { +    rv = strm_rob_init(strm); +    if (rv != 0) { +      return rv; +    } + +    if (strm->rx.cont_offset) { +      ngtcp2_rob_remove_prefix(strm->rx.rob, strm->rx.cont_offset); +    } +  } + +  if (strm_rob_heavily_fragmented(strm->rx.rob)) { +    return NGTCP2_ERR_INTERNAL; +  } + +  return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); +} + +void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { +  if (strm->rx.rob == NULL) { +    strm->rx.cont_offset = offset; +    return; +  } + +  ngtcp2_rob_remove_prefix(strm->rx.rob, offset); +} + +void ngtcp2_strm_discard_reordered_data(ngtcp2_strm *strm) { +  if (strm->rx.rob == NULL) { +    return; +  } + +  strm->rx.cont_offset = ngtcp2_strm_rx_offset(strm); + +  ngtcp2_rob_free(strm->rx.rob); +  ngtcp2_mem_free(strm->mem, strm->rx.rob); +  strm->rx.rob = NULL; +} + +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { +  strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR; +} + +static int strm_streamfrq_init(ngtcp2_strm *strm) { +  ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); +  if (streamfrq == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); + +  strm->tx.streamfrq = streamfrq; + +  return 0; +} + +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { +  int rv; + +  assert(frc->fr.type == NGTCP2_FRAME_STREAM || +         frc->fr.type == NGTCP2_FRAME_CRYPTO); +  assert(frc->next == NULL); + +  if (strm->tx.streamfrq == NULL) { +    rv = strm_streamfrq_init(strm); +    if (rv != 0) { +      return rv; +    } +  } + +  return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset, +                           frc); +} + +static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, +                                      ngtcp2_frame_chain **pfrc) { +  ngtcp2_frame_chain *frc, *nfrc; +  ngtcp2_stream *fr, *nfr; +  uint64_t offset, end_offset; +  size_t idx, end_idx; +  uint64_t base_offset, end_base_offset; +  ngtcp2_range gap; +  ngtcp2_vec *v; +  int rv; +  ngtcp2_ksl_it it; + +  *pfrc = NULL; + +  assert(strm->tx.streamfrq); +  assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + +  for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);) { +    frc = ngtcp2_ksl_it_get(&it); +    fr = &frc->fr.stream; + +    ngtcp2_ksl_remove_hint(strm->tx.streamfrq, &it, &it, &fr->offset); + +    idx = 0; +    offset = fr->offset; +    base_offset = 0; + +    gap = ngtcp2_strm_get_unacked_range_after(strm, offset); +    if (gap.begin < offset) { +      gap.begin = offset; +    } + +    for (; idx < fr->datacnt && offset < gap.begin; ++idx) { +      v = &fr->data[idx]; +      if (offset + v->len > gap.begin) { +        base_offset = gap.begin - offset; +        break; +      } + +      offset += v->len; +    } + +    if (idx == fr->datacnt) { +      if (fr->fin) { +        if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { +          ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +          assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); +          return 0; +        } + +        fr->offset += ngtcp2_vec_len(fr->data, fr->datacnt); +        fr->datacnt = 0; + +        *pfrc = frc; + +        return 0; +      } + +      if (fr->offset == 0 && fr->datacnt == 0 && strm->tx.offset == 0 && +          !(strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) { +        *pfrc = frc; + +        return 0; +      } + +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +      continue; +    } + +    assert(gap.begin == offset + base_offset); + +    end_idx = idx; +    end_offset = offset; +    end_base_offset = 0; + +    for (; end_idx < fr->datacnt; ++end_idx) { +      v = &fr->data[end_idx]; +      if (end_offset + v->len > gap.end) { +        end_base_offset = gap.end - end_offset; +        break; +      } + +      end_offset += v->len; +    } + +    if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { +      *pfrc = frc; +      return 0; +    } + +    if (fr->datacnt == end_idx) { +      memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + +      assert(fr->data[0].len > base_offset); + +      fr->offset = offset + base_offset; +      fr->datacnt = end_idx - idx; +      fr->data[0].base += base_offset; +      fr->data[0].len -= (size_t)base_offset; + +      *pfrc = frc; + +      return 0; +    } + +    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +      &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem); +    if (rv != 0) { +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +      return rv; +    } + +    nfr = &nfrc->fr.stream; +    memcpy(nfr->data, fr->data + end_idx, +           sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); + +    assert(nfr->data[0].len > end_base_offset); + +    nfr->type = fr->type; +    nfr->flags = 0; +    nfr->fin = fr->fin; +    nfr->stream_id = fr->stream_id; +    nfr->offset = end_offset + end_base_offset; +    nfr->datacnt = fr->datacnt - end_idx; +    nfr->data[0].base += end_base_offset; +    nfr->data[0].len -= (size_t)end_base_offset; + +    rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +      return rv; +    } + +    if (end_base_offset) { +      ++end_idx; +    } + +    memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + +    assert(fr->data[0].len > base_offset); + +    fr->fin = 0; +    fr->offset = offset + base_offset; +    fr->datacnt = end_idx - idx; + +    if (end_base_offset) { +      assert(fr->data[fr->datacnt - 1].len > end_base_offset); +      fr->data[fr->datacnt - 1].len = (size_t)end_base_offset; +    } + +    fr->data[0].base += base_offset; +    fr->data[0].len -= (size_t)base_offset; + +    *pfrc = frc; + +    return 0; +  } + +  return 0; +} + +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, +                              size_t left) { +  ngtcp2_stream *fr, *nfr; +  ngtcp2_frame_chain *frc, *nfrc, *sfrc; +  int rv; +  size_t nmerged; +  uint64_t datalen; +  ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; +  ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; +  size_t acnt, bcnt; +  uint64_t unacked_offset; + +  if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) { +    *pfrc = NULL; +    return 0; +  } + +  rv = strm_streamfrq_unacked_pop(strm, &frc); +  if (rv != 0) { +    return rv; +  } + +  if (frc == NULL) { +    *pfrc = NULL; +    return 0; +  } + +  fr = &frc->fr.stream; +  datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + +  /* datalen could be zero if 0 length STREAM has been sent */ +  if (left == 0 && datalen) { +    rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +      return rv; +    } + +    *pfrc = NULL; + +    return 0; +  } + +  if (datalen > left) { +    ngtcp2_vec_copy(a, fr->data, fr->datacnt); +    acnt = fr->datacnt; + +    bcnt = 0; +    ngtcp2_vec_split(b, &bcnt, a, &acnt, left, NGTCP2_MAX_STREAM_DATACNT); + +    assert(acnt > 0); +    assert(bcnt > 0); + +    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +      &nfrc, bcnt, strm->frc_objalloc, strm->mem); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +      return rv; +    } + +    nfr = &nfrc->fr.stream; +    nfr->type = fr->type; +    nfr->flags = 0; +    nfr->fin = fr->fin; +    nfr->stream_id = fr->stream_id; +    nfr->offset = fr->offset + left; +    nfr->datacnt = bcnt; +    ngtcp2_vec_copy(nfr->data, b, bcnt); + +    rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +      return rv; +    } + +    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +      &nfrc, acnt, strm->frc_objalloc, strm->mem); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, 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_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +    *pfrc = nfrc; + +    return 0; +  } + +  left -= (size_t)datalen; + +  ngtcp2_vec_copy(a, fr->data, fr->datacnt); +  acnt = fr->datacnt; + +  for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) { +    unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); +    if (unacked_offset != fr->offset + datalen) { +      assert(fr->offset + datalen < unacked_offset); +      break; +    } + +    rv = strm_streamfrq_unacked_pop(strm, &nfrc); +    if (rv != 0) { +      assert(ngtcp2_err_is_fatal(rv)); +      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +      return rv; +    } +    if (nfrc == NULL) { +      break; +    } + +    nfr = &nfrc->fr.stream; + +    if (nfr->fin && nfr->datacnt == 0) { +      fr->fin = 1; +      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +      break; +    } + +    bcnt = nfr->datacnt; + +    nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &bcnt, left, +                               NGTCP2_MAX_STREAM_DATACNT); +    if (nmerged == 0) { +      rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); +      if (rv != 0) { +        assert(ngtcp2_err_is_fatal(rv)); +        ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +        return rv; +      } + +      break; +    } + +    datalen += nmerged; +    left -= nmerged; + +    if (bcnt == 0) { +      fr->fin = nfr->fin; +      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +      continue; +    } + +    if (nfr->datacnt <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES || +        bcnt > NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES) { +      nfr->offset += nmerged; +      nfr->datacnt = bcnt; + +      rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); +      if (rv != 0) { +        ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +        return rv; +      } +    } else { +      rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +        &sfrc, bcnt, strm->frc_objalloc, strm->mem); +      if (rv != 0) { +        ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); +        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +        return rv; +      } + +      sfrc->fr.stream = nfrc->fr.stream; +      sfrc->fr.stream.offset += nmerged; +      sfrc->fr.stream.datacnt = bcnt; +      ngtcp2_vec_copy(sfrc->fr.stream.data, nfrc->fr.stream.data, bcnt); + +      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem); + +      rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &sfrc->fr.stream.offset, +                             sfrc); +      if (rv != 0) { +        ngtcp2_frame_chain_objalloc_del(sfrc, strm->frc_objalloc, strm->mem); +        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +        return rv; +      } +    } + +    break; +  } + +  if (acnt == fr->datacnt) { +    if (acnt > 0) { +      fr->data[acnt - 1] = a[acnt - 1]; +    } + +    *pfrc = frc; + +    return 0; +  } + +  assert(acnt > fr->datacnt); + +  rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( +    &nfrc, acnt, strm->frc_objalloc, strm->mem); +  if (rv != 0) { +    ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +    return rv; +  } + +  nfr = &nfrc->fr.stream; +  *nfr = *fr; +  nfr->datacnt = acnt; +  ngtcp2_vec_copy(nfr->data, a, acnt); + +  ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); + +  *pfrc = nfrc; + +  return 0; +} + +uint64_t ngtcp2_strm_streamfrq_unacked_offset(const ngtcp2_strm *strm) { +  ngtcp2_frame_chain *frc; +  ngtcp2_stream *fr; +  ngtcp2_range gap; +  ngtcp2_ksl_it it; +  uint64_t datalen; + +  assert(strm->tx.streamfrq); +  assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + +  for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); +       ngtcp2_ksl_it_next(&it)) { +    frc = ngtcp2_ksl_it_get(&it); +    fr = &frc->fr.stream; + +    gap = ngtcp2_strm_get_unacked_range_after(strm, fr->offset); + +    datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + +    if (gap.begin <= fr->offset) { +      return fr->offset; +    } + +    if (gap.begin < fr->offset + datalen) { +      return gap.begin; +    } + +    if (fr->offset + datalen == gap.begin && fr->fin && +        !(strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED)) { +      return fr->offset + datalen; +    } +  } + +  return (uint64_t)-1; +} + +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(const ngtcp2_strm *strm) { +  ngtcp2_ksl_it it; + +  assert(strm->tx.streamfrq); +  assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + +  it = ngtcp2_ksl_begin(strm->tx.streamfrq); + +  return ngtcp2_ksl_it_get(&it); +} + +int ngtcp2_strm_streamfrq_empty(const ngtcp2_strm *strm) { +  return strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0; +} + +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { +  ngtcp2_frame_chain *frc; +  ngtcp2_ksl_it it; + +  if (strm->tx.streamfrq == NULL) { +    return; +  } + +  for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); +       ngtcp2_ksl_it_next(&it)) { +    frc = ngtcp2_ksl_it_get(&it); +    ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem); +  } + +  ngtcp2_ksl_clear(strm->tx.streamfrq); +} + +int ngtcp2_strm_is_tx_queued(const ngtcp2_strm *strm) { +  return strm->pe.index != NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_strm_is_all_tx_data_acked(const ngtcp2_strm *strm) { +  if (strm->tx.acked_offset == NULL) { +    return strm->tx.cont_acked_offset == strm->tx.offset; +  } + +  return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset) == +         strm->tx.offset; +} + +int ngtcp2_strm_is_all_tx_data_fin_acked(const ngtcp2_strm *strm) { +  return (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) && +         ngtcp2_strm_is_all_tx_data_acked(strm); +} + +ngtcp2_range ngtcp2_strm_get_unacked_range_after(const ngtcp2_strm *strm, +                                                 uint64_t offset) { +  ngtcp2_range gap; + +  if (strm->tx.acked_offset == NULL) { +    gap.begin = strm->tx.cont_acked_offset; +    gap.end = UINT64_MAX; +    return gap; +  } + +  return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); +} + +uint64_t ngtcp2_strm_get_acked_offset(const ngtcp2_strm *strm) { +  if (strm->tx.acked_offset == NULL) { +    return strm->tx.cont_acked_offset; +  } + +  return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset); +} + +static int strm_acked_offset_init(ngtcp2_strm *strm) { +  ngtcp2_gaptr *acked_offset = +    ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); + +  if (acked_offset == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  ngtcp2_gaptr_init(acked_offset, strm->mem); + +  strm->tx.acked_offset = acked_offset; + +  return 0; +} + +int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { +  int rv; + +  if (strm->tx.acked_offset == NULL) { +    if (strm->tx.cont_acked_offset == offset) { +      strm->tx.cont_acked_offset += len; +      return 0; +    } + +    rv = strm_acked_offset_init(strm); +    if (rv != 0) { +      return rv; +    } + +    rv = +      ngtcp2_gaptr_push(strm->tx.acked_offset, 0, strm->tx.cont_acked_offset); +    if (rv != 0) { +      return rv; +    } +  } + +  return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); +} + +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, +                                    uint64_t app_error_code) { +  if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) { +    return; +  } + +  assert(0 == strm->app_error_code); + +  strm->flags |= NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET; +  strm->app_error_code = app_error_code; +} + +int ngtcp2_strm_require_retransmit_reset_stream(const ngtcp2_strm *strm) { +  return !ngtcp2_strm_is_all_tx_data_fin_acked(strm); +} + +int ngtcp2_strm_require_retransmit_stop_sending(const ngtcp2_strm *strm) { +  return !(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || +         ngtcp2_strm_rx_offset(strm) != strm->rx.last_offset; +} + +int ngtcp2_strm_require_retransmit_max_stream_data( +  const ngtcp2_strm *strm, const ngtcp2_max_stream_data *fr) { +  return fr->max_stream_data == strm->rx.max_offset && +         !(strm->flags & +           (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)); +} + +int ngtcp2_strm_require_retransmit_stream_data_blocked( +  const ngtcp2_strm *strm, const ngtcp2_stream_data_blocked *fr) { +  return fr->offset == strm->tx.max_offset && +         !(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_strm.h b/contrib/libs/ngtcp2/lib/ngtcp2_strm.h new file mode 100644 index 00000000000..c72f8b9dc89 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_strm.h @@ -0,0 +1,361 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STRM_H +#define NGTCP2_STRM_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_rob.h" +#include "ngtcp2_map.h" +#include "ngtcp2_gaptr.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" +#include "ngtcp2_pkt.h" + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_STRM_FLAG_NONE 0x00u +/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream +   data is not allowed. */ +#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u +/* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of +   stream data is not allowed. */ +#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u +#define NGTCP2_STRM_FLAG_SHUT_RDWR                                             \ +  (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR) +/* NGTCP2_STRM_FLAG_RESET_STREAM indicates that RESET_STREAM is sent +   from the local endpoint.  In this case, NGTCP2_STRM_FLAG_SHUT_WR is +   also set. */ +#define NGTCP2_STRM_FLAG_RESET_STREAM 0x04u +/* NGTCP2_STRM_FLAG_RESET_STREAM_RECVED indicates that RESET_STREAM is +   received from the remote endpoint.  In this case, +   NGTCP2_STRM_FLAG_SHUT_RD is also set. */ +#define NGTCP2_STRM_FLAG_RESET_STREAM_RECVED 0x08u +/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent +   from the local endpoint. */ +#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u +/* NGTCP2_STRM_FLAG_RESET_STREAM_ACKED indicates that the outgoing +   RESET_STREAM is acknowledged by peer. */ +#define NGTCP2_STRM_FLAG_RESET_STREAM_ACKED 0x20u +/* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set +   is acknowledged by a remote endpoint. */ +#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u +/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream +   data, including 0 length segment, is acknowledged. */ +#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u +/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code +   field is set.  This resolves the ambiguity that the initial +   app_error_code value 0 might be a proper application error code. +   In this case, without this flag, we are unable to distinguish +   assigned value from unassigned one.  */ +#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u +/* NGTCP2_STRM_FLAG_SEND_STOP_SENDING is set when STOP_SENDING frame +   should be sent. */ +#define NGTCP2_STRM_FLAG_SEND_STOP_SENDING 0x200u +/* NGTCP2_STRM_FLAG_SEND_RESET_STREAM is set when RESET_STREAM frame +   should be sent. */ +#define NGTCP2_STRM_FLAG_SEND_RESET_STREAM 0x400u +/* NGTCP2_STRM_FLAG_STOP_SENDING_RECVED indicates that STOP_SENDING is +   received from the remote endpoint.  In this case, +   NGTCP2_STRM_FLAG_SHUT_WR is also set. */ +#define NGTCP2_STRM_FLAG_STOP_SENDING_RECVED 0x800u +/* NGTCP2_STRM_FLAG_ANY_SENT indicates that any STREAM frame, +   including empty one, has been sent. */ +#define NGTCP2_STRM_FLAG_ANY_SENT 0x1000u + +typedef struct ngtcp2_strm ngtcp2_strm; + +struct ngtcp2_strm { +  union { +    struct { +      ngtcp2_pq_entry pe; +      uint64_t cycle; +      ngtcp2_objalloc *frc_objalloc; + +      struct { +        /* acked_offset tracks acknowledged outgoing data. */ +        ngtcp2_gaptr *acked_offset; +        /* cont_acked_offset is the offset that all data up to this offset +           is acknowledged by a remote endpoint.  It is used until the +           remote endpoint acknowledges data in out-of-order.  After that, +           acked_offset is used instead. */ +        uint64_t cont_acked_offset; +        /* streamfrq contains STREAM or CRYPTO frame for +           retransmission.  The flow control credits have already 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 new 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; +        /* last_blocked_offset is the largest offset where the +           transmission of stream data is blocked. */ +        uint64_t last_blocked_offset; +        /* last_max_stream_data_ts is the timestamp when last +           MAX_STREAM_DATA frame is sent. */ +        ngtcp2_tstamp last_max_stream_data_ts; +        /* loss_count is the number of packets that contain STREAM +           frame for this stream and are declared to be lost.  It may +           include the spurious losses.  It does not include a packet +           whose contents have been reclaimed for PTO and which is +           later declared to be lost.  Those data are not blocked by +           the flow control and will be sent immediately if no other +           restrictions are applied. */ +        size_t loss_count; +        /* last_lost_pkt_num is the packet number of the packet that +           is counted to loss_count.  It is used to avoid to count +           multiple STREAM frames in one lost packet. */ +        int64_t last_lost_pkt_num; +        /* stop_sending_app_error_code is the application specific +           error code that is sent along with STOP_SENDING. */ +        uint64_t stop_sending_app_error_code; +        /* reset_stream_app_error_code is the application specific +           error code that is sent along with RESET_STREAM. */ +        uint64_t reset_stream_app_error_code; +      } tx; + +      struct { +        /* rob is the reorder buffer for incoming stream data.  The data +           received in out of order is buffered and sorted by its offset +           in this object. */ +        ngtcp2_rob *rob; +        /* cont_offset is the largest offset of consecutive data.  It is +           used until the endpoint receives out-of-order data.  After +           that, rob is used to track the offset and data. */ +        uint64_t cont_offset; +        /* last_offset is the largest offset of stream data received for +           this stream. */ +        uint64_t last_offset; +        /* max_offset is the maximum offset that remote endpoint can send +           to this stream. */ +        uint64_t max_offset; +        /* unsent_max_offset is the maximum offset that remote endpoint +           can send to this stream, and it is not notified to the remote +           endpoint.  unsent_max_offset >= max_offset must be hold. */ +        uint64_t unsent_max_offset; +        /* window is the stream-level flow control window size. */ +        uint64_t window; +      } 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_FLAG_*. */ +      uint32_t flags; +      /* app_error_code is an error code the local endpoint sent in +         RESET_STREAM or STOP_SENDING, or received from a remote endpoint +         in RESET_STREAM or STOP_SENDING.  First application error code is +         chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is +         set in flags field. */ +      uint64_t app_error_code; +    }; + +    ngtcp2_opl_entry oplent; +  }; +}; + +/* + * ngtcp2_strm_init initializes |strm|. + */ +void 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, ngtcp2_objalloc *frc_objalloc, +                      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(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_recv_reordering handles reordered data. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, +                                size_t datalen, uint64_t offset); + +/* + * ngtcp2_strm_update_rx_offset tells that data up to |offset| bytes + * are received in order. + */ +void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset); + +/* + * ngtcp2_strm_discard_reordered_data discards all buffered reordered + * data. + */ +void ngtcp2_strm_discard_reordered_data(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_shutdown shutdowns |strm|.  |flags| should be one of + * NGTCP2_STRM_FLAG_SHUT_RD, NGTCP2_STRM_FLAG_SHUT_WR, and + * NGTCP2_STRM_FLAG_SHUT_RDWR. + */ +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags); + +/* + * ngtcp2_strm_streamfrq_push pushes |frc| to strm->tx.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 assigns a ngtcp2_frame_chain that only + * contains unacknowledged stream data with smallest offset to |*pfrc| + * for retransmission.  The assigned ngtcp2_frame_chain has stream + * data at most |left| bytes.  strm->tx.streamfrq is adjusted to + * exclude the portion of data included in it.  If there is no stream + * data to send, this function returns 0 and |*pfrc| is NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory + */ +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, +                              size_t left); + +/* + * ngtcp2_strm_streamfrq_unacked_offset returns the smallest offset of + * unacknowledged stream data held in strm->tx.streamfrq. + */ +uint64_t ngtcp2_strm_streamfrq_unacked_offset(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain. + * The queue must not be empty. + */ +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty. + */ +int ngtcp2_strm_streamfrq_empty(const 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(const 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(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_is_all_tx_data_fin_acked behaves like + * ngtcp2_strm_is_all_tx_data_acked, but it also requires that STREAM + * frame with fin bit set is acknowledged. + */ +int ngtcp2_strm_is_all_tx_data_fin_acked(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_get_unacked_range_after returns the range that is not + * acknowledged yet and includes or comes after |offset|. + */ +ngtcp2_range ngtcp2_strm_get_unacked_range_after(const ngtcp2_strm *strm, +                                                 uint64_t offset); + +/* + * ngtcp2_strm_get_acked_offset returns offset, that is the data up to + * this offset have been acknowledged by a remote endpoint.  It + * returns 0 if no data is acknowledged. + */ +uint64_t ngtcp2_strm_get_acked_offset(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_ack_data tells |strm| that the data [|offset|, |offset| + * + |len|) is acknowledged by a remote endpoint. + */ +int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len); + +/* + * ngtcp2_strm_set_app_error_code sets |app_error_code| to |strm| and + * set NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag.  If the flag is + * already set, this function does nothing. + */ +void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, uint64_t app_error_code); + +/* + * ngtcp2_strm_require_retransmit_reset_stream returns nonzero if + * RESET_STREAM frame should be retransmitted. + */ +int ngtcp2_strm_require_retransmit_reset_stream(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_require_retransmit_stop_sending returns nonzero if + * STOP_SENDING frame should be retransmitted. + */ +int ngtcp2_strm_require_retransmit_stop_sending(const ngtcp2_strm *strm); + +/* + * ngtcp2_strm_require_retransmit_max_stream_data returns nonzero if + * MAX_STREAM_DATA frame should be retransmitted. + */ +int ngtcp2_strm_require_retransmit_max_stream_data( +  const ngtcp2_strm *strm, const ngtcp2_max_stream_data *fr); + +/* + * ngtcp2_strm_require_retransmit_stream_data_blocked returns nonzero + * if STREAM_DATA_BLOCKED frame frame should be retransmitted. + */ +int ngtcp2_strm_require_retransmit_stream_data_blocked( +  const ngtcp2_strm *strm, const ngtcp2_stream_data_blocked *fr); + +#endif /* !defined(NGTCP2_STRM_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.c b/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.c new file mode 100644 index 00000000000..dda59c48858 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.c @@ -0,0 +1,886 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_transport_params.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_conv.h" +#include "ngtcp2_str.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_unreachable.h" + +void ngtcp2_transport_params_default_versioned( +  int transport_params_version, ngtcp2_transport_params *params) { +  size_t len; + +  switch (transport_params_version) { +  case NGTCP2_TRANSPORT_PARAMS_VERSION: +    len = sizeof(*params); + +    break; +  default: +    ngtcp2_unreachable(); +  } + +  memset(params, 0, len); + +  switch (transport_params_version) { +  case NGTCP2_TRANSPORT_PARAMS_VERSION: +    params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; +    params->active_connection_id_limit = +      NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; +    params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; +    params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + +    break; +  } +} + +/* + * 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_uvarintlen(param); +  return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(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_uvarint(p, id); +  p = ngtcp2_put_uvarint(p, ngtcp2_put_uvarintlen(value)); +  return ngtcp2_put_uvarint(p, value); +} + +/* + * zero_paramlen returns the length of a single transport parameter + * which has zero length value in its parameter. + */ +static size_t zero_paramlen(ngtcp2_transport_param_id id) { +  return ngtcp2_put_uvarintlen(id) + 1; +} + +/* + * write_zero_param writes parameter |id| that has zero length value. + * It returns p + the number of bytes written. + */ +static uint8_t *write_zero_param(uint8_t *p, ngtcp2_transport_param_id id) { +  p = ngtcp2_put_uvarint(p, id); +  *p++ = 0; + +  return p; +} + +/* + * cid_paramlen returns the length of a single transport parameter + * which has |cid| as value. + */ +static size_t cid_paramlen(ngtcp2_transport_param_id id, +                           const ngtcp2_cid *cid) { +  return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(cid->datalen) + +         cid->datalen; +} + +/* + * write_cid_param writes parameter |id| of the given |cid|.  It + * returns p + the number of bytes written. + */ +static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, +                                const ngtcp2_cid *cid) { +  assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN); +  assert(cid->datalen <= NGTCP2_MAX_CIDLEN); + +  p = ngtcp2_put_uvarint(p, id); +  p = ngtcp2_put_uvarint(p, cid->datalen); +  if (cid->datalen) { +    p = ngtcp2_cpymem(p, cid->data, cid->datalen); +  } +  return p; +} + +static const uint8_t empty_address[16]; + +ngtcp2_ssize ngtcp2_transport_params_encode_versioned( +  uint8_t *dest, size_t destlen, int transport_params_version, +  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; +  size_t version_infolen = 0; +  const ngtcp2_sockaddr_in *sa_in; +  const ngtcp2_sockaddr_in6 *sa_in6; +  ngtcp2_transport_params paramsbuf; + +  params = ngtcp2_transport_params_convert_to_latest( +    ¶msbuf, transport_params_version, params); + +  if (params->original_dcid_present) { +    len += +      cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, +                   ¶ms->original_dcid); +  } + +  if (params->stateless_reset_token_present) { +    len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + +           ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) + +           NGTCP2_STATELESS_RESET_TOKENLEN; +  } + +  if (params->preferred_addr_present) { +    assert(params->preferred_addr.cid.datalen >= NGTCP2_MIN_CIDLEN); +    assert(params->preferred_addr.cid.datalen <= NGTCP2_MAX_CIDLEN); +    preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ + +                        16 /* ipv6Address */ + 2 /* ipv6Port */ +                        + 1 + params->preferred_addr.cid.datalen /* CID */ + +                        NGTCP2_STATELESS_RESET_TOKENLEN; +    len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + +           ngtcp2_put_uvarintlen(preferred_addrlen) + preferred_addrlen; +  } +  if (params->retry_scid_present) { +    len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, +                        ¶ms->retry_scid); +  } + +  if (params->initial_scid_present) { +    len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, +                        ¶ms->initial_scid); +  } + +  if (params->initial_max_stream_data_bidi_local) { +    len += +      varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, +                      params->initial_max_stream_data_bidi_local); +  } +  if (params->initial_max_stream_data_bidi_remote) { +    len += varint_paramlen( +      NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, +      params->initial_max_stream_data_bidi_remote); +  } +  if (params->initial_max_stream_data_uni) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, +                           params->initial_max_stream_data_uni); +  } +  if (params->initial_max_data) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, +                           params->initial_max_data); +  } +  if (params->initial_max_streams_bidi) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, +                           params->initial_max_streams_bidi); +  } +  if (params->initial_max_streams_uni) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, +                           params->initial_max_streams_uni); +  } +  if (params->max_udp_payload_size != +      NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, +                           params->max_udp_payload_size); +  } +  if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, +                           params->ack_delay_exponent); +  } +  if (params->disable_active_migration) { +    len += zero_paramlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); +  } +  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 (params->max_datagram_frame_size) { +    len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, +                           params->max_datagram_frame_size); +  } +  if (params->grease_quic_bit) { +    len += zero_paramlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); +  } +  if (params->version_info_present) { +    version_infolen = +      sizeof(uint32_t) + params->version_info.available_versionslen; +    len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION) + +           ngtcp2_put_uvarintlen(version_infolen) + version_infolen; +  } + +  if (dest == NULL && destlen == 0) { +    return (ngtcp2_ssize)len; +  } + +  if (destlen < len) { +    return NGTCP2_ERR_NOBUF; +  } + +  p = dest; + +  if (params->original_dcid_present) { +    p = write_cid_param( +      p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, +      ¶ms->original_dcid); +  } + +  if (params->stateless_reset_token_present) { +    p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN); +    p = ngtcp2_put_uvarint(p, sizeof(params->stateless_reset_token)); +    p = ngtcp2_cpymem(p, params->stateless_reset_token, +                      sizeof(params->stateless_reset_token)); +  } + +  if (params->preferred_addr_present) { +    p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); +    p = ngtcp2_put_uvarint(p, preferred_addrlen); + +    if (params->preferred_addr.ipv4_present) { +      sa_in = ¶ms->preferred_addr.ipv4; +      p = ngtcp2_cpymem(p, &sa_in->sin_addr, sizeof(sa_in->sin_addr)); +      p = ngtcp2_put_uint16(p, sa_in->sin_port); +    } else { +      p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in->sin_addr)); +      p = ngtcp2_put_uint16(p, 0); +    } + +    if (params->preferred_addr.ipv6_present) { +      sa_in6 = ¶ms->preferred_addr.ipv6; +      p = ngtcp2_cpymem(p, &sa_in6->sin6_addr, sizeof(sa_in6->sin6_addr)); +      p = ngtcp2_put_uint16(p, sa_in6->sin6_port); +    } else { +      p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in6->sin6_addr)); +      p = ngtcp2_put_uint16(p, 0); +    } + +    *p++ = (uint8_t)params->preferred_addr.cid.datalen; +    if (params->preferred_addr.cid.datalen) { +      p = ngtcp2_cpymem(p, params->preferred_addr.cid.data, +                        params->preferred_addr.cid.datalen); +    } +    p = ngtcp2_cpymem(p, params->preferred_addr.stateless_reset_token, +                      sizeof(params->preferred_addr.stateless_reset_token)); +  } + +  if (params->retry_scid_present) { +    p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, +                        ¶ms->retry_scid); +  } + +  if (params->initial_scid_present) { +    p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, +                        ¶ms->initial_scid); +  } + +  if (params->initial_max_stream_data_bidi_local) { +    p = write_varint_param( +      p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, +      params->initial_max_stream_data_bidi_local); +  } + +  if (params->initial_max_stream_data_bidi_remote) { +    p = write_varint_param( +      p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, +      params->initial_max_stream_data_bidi_remote); +  } + +  if (params->initial_max_stream_data_uni) { +    p = +      write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, +                         params->initial_max_stream_data_uni); +  } + +  if (params->initial_max_data) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, +                           params->initial_max_data); +  } + +  if (params->initial_max_streams_bidi) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, +                           params->initial_max_streams_bidi); +  } + +  if (params->initial_max_streams_uni) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, +                           params->initial_max_streams_uni); +  } + +  if (params->max_udp_payload_size != +      NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, +                           params->max_udp_payload_size); +  } + +  if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, +                           params->ack_delay_exponent); +  } + +  if (params->disable_active_migration) { +    p = write_zero_param(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); +  } + +  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); +  } + +  if (params->max_datagram_frame_size) { +    p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, +                           params->max_datagram_frame_size); +  } + +  if (params->grease_quic_bit) { +    p = write_zero_param(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT); +  } + +  if (params->version_info_present) { +    p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION); +    p = ngtcp2_put_uvarint(p, version_infolen); +    p = ngtcp2_put_uint32be(p, params->version_info.chosen_version); +    if (params->version_info.available_versionslen) { +      p = ngtcp2_cpymem(p, params->version_info.available_versions, +                        params->version_info.available_versionslen); +    } +  } + +  assert((size_t)(p - dest) == len); + +  return (ngtcp2_ssize)len; +} + +/* + * decode_varint decodes a single varint from the buffer pointed by + * |*pp| of length |end - *pp|.  If it decodes an integer + * successfully, it stores the integer in |*pdest|, increment |*pp| by + * the number of bytes read from |*pp|, and returns 0.  Otherwise it + * returns -1. + */ +static int decode_varint(uint64_t *pdest, const uint8_t **pp, +                         const uint8_t *end) { +  const uint8_t *p = *pp; +  size_t len; + +  if (p == end) { +    return -1; +  } + +  len = ngtcp2_get_uvarintlen(p); +  if ((uint64_t)(end - p) < len) { +    return -1; +  } + +  *pp = ngtcp2_get_uvarint(pdest, p); + +  return 0; +} + +/* + * decode_varint_param decodes length prefixed value from the buffer + * pointed by |*pp| of length |end - *pp|.  The length and value are + * encoded in varint form.  If it decodes a value successfully, it + * stores the value in |*pdest|, increment |*pp| by the number of + * bytes read from |*pp|, and returns 0.  Otherwise it returns -1. + */ +static int decode_varint_param(uint64_t *pdest, const uint8_t **pp, +                               const uint8_t *end) { +  const uint8_t *p = *pp; +  uint64_t valuelen; + +  if (decode_varint(&valuelen, &p, end) != 0) { +    return -1; +  } + +  if (p == end) { +    return -1; +  } + +  if ((uint64_t)(end - p) < valuelen) { +    return -1; +  } + +  if (ngtcp2_get_uvarintlen(p) != valuelen) { +    return -1; +  } + +  *pp = ngtcp2_get_uvarint(pdest, p); + +  return 0; +} + +/* + * decode_zero_param decodes zero length value from the buffer pointed + * by |*pp| of length |end - *pp|.  The length is encoded in varint + * form.  If it decodes zero length value successfully, it increments + * |*pp| by 1, and returns 0.  Otherwise it returns -1. + */ +static int decode_zero_param(const uint8_t **pp, const uint8_t *end) { +  if (*pp == end || **pp != 0) { +    return -1; +  } + +  ++*pp; + +  return 0; +} + +/* + * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer + * pointed by |*pp| of length |end - *pp|.  The length is encoded in + * varint form.  If it decodes a value successfully, it stores the + * value in |*pdest|, increment |*pp| by the number of read from + * |*pp|, and returns the number of bytes read.  Otherwise it returns + * the one of the negative error code: + * + * NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM + *     Could not decode Connection ID. + */ +static int decode_cid_param(ngtcp2_cid *pdest, const uint8_t **pp, +                            const uint8_t *end) { +  const uint8_t *p = *pp; +  uint64_t valuelen; + +  if (decode_varint(&valuelen, &p, end) != 0) { +    return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +  } + +  if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) || +      valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) { +    return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +  } + +  ngtcp2_cid_init(pdest, p, (size_t)valuelen); + +  p += valuelen; + +  *pp = p; + +  return 0; +} + +int ngtcp2_transport_params_decode_versioned(int transport_params_version, +                                             ngtcp2_transport_params *dest, +                                             const uint8_t *data, +                                             size_t datalen) { +  const uint8_t *p, *end, *lend; +  size_t len; +  uint64_t param_type; +  uint64_t valuelen; +  int rv; +  ngtcp2_sockaddr_in *sa_in; +  ngtcp2_sockaddr_in6 *sa_in6; +  uint32_t version; +  ngtcp2_transport_params *params, paramsbuf; + +  if (transport_params_version == NGTCP2_TRANSPORT_PARAMS_VERSION) { +    params = dest; +  } else { +    params = ¶msbuf; +  } + +  /* Set default values */ +  memset(params, 0, sizeof(*params)); +  params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; +  params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; +  params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; +  params->active_connection_id_limit = +    NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + +  p = data; +  end = data + datalen; + +  for (; (size_t)(end - p) >= 2;) { +    if (decode_varint(¶m_type, &p, end) != 0) { +      return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +    } + +    switch (param_type) { +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: +      if (decode_varint_param(¶ms->initial_max_stream_data_bidi_local, &p, +                              end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: +      if (decode_varint_param(¶ms->initial_max_stream_data_bidi_remote, &p, +                              end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI: +      if (decode_varint_param(¶ms->initial_max_stream_data_uni, &p, end) != +          0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA: +      if (decode_varint_param(¶ms->initial_max_data, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI: +      if (decode_varint_param(¶ms->initial_max_streams_bidi, &p, end) != +          0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI: +      if (decode_varint_param(¶ms->initial_max_streams_uni, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: +      if (decode_varint_param(¶ms->max_idle_timeout, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      params->max_idle_timeout *= NGTCP2_MILLISECONDS; +      break; +    case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE: +      if (decode_varint_param(¶ms->max_udp_payload_size, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN: +      if (decode_varint(&valuelen, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      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; +      } + +      p = ngtcp2_get_bytes(params->stateless_reset_token, p, +                           sizeof(params->stateless_reset_token)); +      params->stateless_reset_token_present = 1; + +      break; +    case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT: +      if (decode_varint_param(¶ms->ack_delay_exponent, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (params->ack_delay_exponent > 20) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS: +      if (decode_varint(&valuelen, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      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; +      } + +      sa_in = ¶ms->preferred_addr.ipv4; + +      p = ngtcp2_get_bytes(&sa_in->sin_addr, p, sizeof(sa_in->sin_addr)); +      p = ngtcp2_get_uint16(&sa_in->sin_port, p); + +      if (sa_in->sin_port || memcmp(empty_address, &sa_in->sin_addr, +                                    sizeof(sa_in->sin_addr)) != 0) { +        sa_in->sin_family = NGTCP2_AF_INET; +        params->preferred_addr.ipv4_present = 1; +      } + +      sa_in6 = ¶ms->preferred_addr.ipv6; + +      p = ngtcp2_get_bytes(&sa_in6->sin6_addr, p, sizeof(sa_in6->sin6_addr)); +      p = ngtcp2_get_uint16(&sa_in6->sin6_port, p); + +      if (sa_in6->sin6_port || memcmp(empty_address, &sa_in6->sin6_addr, +                                      sizeof(sa_in6->sin6_addr)) != 0) { +        sa_in6->sin6_family = NGTCP2_AF_INET6; +        params->preferred_addr.ipv6_present = 1; +      } + +      /* cid */ +      params->preferred_addr.cid.datalen = *p++; +      len += params->preferred_addr.cid.datalen; +      if (valuelen != len || +          params->preferred_addr.cid.datalen > NGTCP2_MAX_CIDLEN || +          params->preferred_addr.cid.datalen < NGTCP2_MIN_CIDLEN) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (params->preferred_addr.cid.datalen) { +        p = ngtcp2_get_bytes(params->preferred_addr.cid.data, p, +                             params->preferred_addr.cid.datalen); +      } + +      /* stateless reset token */ +      p = +        ngtcp2_get_bytes(params->preferred_addr.stateless_reset_token, p, +                         sizeof(params->preferred_addr.stateless_reset_token)); +      params->preferred_addr_present = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION: +      if (decode_zero_param(&p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      params->disable_active_migration = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID: +      rv = decode_cid_param(¶ms->original_dcid, &p, end); +      if (rv != 0) { +        return rv; +      } +      params->original_dcid_present = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID: +      rv = decode_cid_param(¶ms->retry_scid, &p, end); +      if (rv != 0) { +        return rv; +      } +      params->retry_scid_present = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID: +      rv = decode_cid_param(¶ms->initial_scid, &p, end); +      if (rv != 0) { +        return rv; +      } +      params->initial_scid_present = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY: +      if (decode_varint_param(¶ms->max_ack_delay, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (params->max_ack_delay >= 16384) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      params->max_ack_delay *= NGTCP2_MILLISECONDS; +      break; +    case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT: +      if (decode_varint_param(¶ms->active_connection_id_limit, &p, end) != +          0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: +      if (decode_varint_param(¶ms->max_datagram_frame_size, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      break; +    case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT: +      if (decode_zero_param(&p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      params->grease_quic_bit = 1; +      break; +    case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION: +      if (decode_varint(&valuelen, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if ((size_t)(end - p) < valuelen) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      p = ngtcp2_get_uint32be(¶ms->version_info.chosen_version, p); +      if (params->version_info.chosen_version == 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if (valuelen > sizeof(uint32_t)) { +        params->version_info.available_versions = (uint8_t *)p; +        params->version_info.available_versionslen = +          (size_t)valuelen - sizeof(uint32_t); + +        for (lend = p + (valuelen - sizeof(uint32_t)); p != lend;) { +          p = ngtcp2_get_uint32be(&version, p); +          if (version == 0) { +            return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +          } +        } +      } +      params->version_info_present = 1; +      break; +    default: +      /* Ignore unknown parameter */ +      if (decode_varint(&valuelen, &p, end) != 0) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      if ((size_t)(end - p) < valuelen) { +        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +      } +      p += valuelen; +      break; +    } +  } + +  if (end - p != 0) { +    return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; +  } + +  if (transport_params_version != NGTCP2_TRANSPORT_PARAMS_VERSION) { +    ngtcp2_transport_params_convert_to_old(transport_params_version, dest, +                                           params); +  } + +  return 0; +} + +static int transport_params_copy_new(ngtcp2_transport_params **pdest, +                                     const ngtcp2_transport_params *src, +                                     const ngtcp2_mem *mem) { +  size_t len = sizeof(**pdest); +  ngtcp2_transport_params *dest; +  uint8_t *p; + +  if (src->version_info_present) { +    len += src->version_info.available_versionslen; +  } + +  dest = ngtcp2_mem_malloc(mem, len); +  if (dest == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  *dest = *src; + +  if (src->version_info_present && src->version_info.available_versionslen) { +    p = (uint8_t *)dest + sizeof(*dest); +    memcpy(p, src->version_info.available_versions, +           src->version_info.available_versionslen); +    dest->version_info.available_versions = p; +  } + +  *pdest = dest; + +  return 0; +} + +int ngtcp2_transport_params_decode_new(ngtcp2_transport_params **pparams, +                                       const uint8_t *data, size_t datalen, +                                       const ngtcp2_mem *mem) { +  int rv; +  ngtcp2_transport_params params; + +  rv = ngtcp2_transport_params_decode(¶ms, data, datalen); +  if (rv < 0) { +    return rv; +  } + +  if (mem == NULL) { +    mem = ngtcp2_mem_default(); +  } + +  return transport_params_copy_new(pparams, ¶ms, mem); +} + +void ngtcp2_transport_params_del(ngtcp2_transport_params *params, +                                 const ngtcp2_mem *mem) { +  if (params == NULL) { +    return; +  } + +  if (mem == NULL) { +    mem = ngtcp2_mem_default(); +  } + +  ngtcp2_mem_free(mem, params); +} + +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, +                                     const ngtcp2_transport_params *src, +                                     const ngtcp2_mem *mem) { +  if (src == NULL) { +    *pdest = NULL; +    return 0; +  } + +  return transport_params_copy_new(pdest, src, mem); +} + +static void transport_params_copy(ngtcp2_transport_params *dest, +                                  const ngtcp2_transport_params *src, +                                  int transport_params_version) { +  assert(transport_params_version != NGTCP2_TRANSPORT_PARAMS_VERSION); + +  switch (transport_params_version) { +  case NGTCP2_TRANSPORT_PARAMS_V1: +    memcpy(dest, src, +           offsetof(ngtcp2_transport_params, version_info_present) + +             sizeof(src->version_info_present)); + +    break; +  } +} + +const ngtcp2_transport_params * +ngtcp2_transport_params_convert_to_latest(ngtcp2_transport_params *dest, +                                          int transport_params_version, +                                          const ngtcp2_transport_params *src) { +  if (transport_params_version == NGTCP2_TRANSPORT_PARAMS_VERSION) { +    return src; +  } + +  ngtcp2_transport_params_default(dest); + +  transport_params_copy(dest, src, transport_params_version); + +  return dest; +} + +void ngtcp2_transport_params_convert_to_old( +  int transport_params_version, ngtcp2_transport_params *dest, +  const ngtcp2_transport_params *src) { +  assert(transport_params_version != NGTCP2_TRANSPORT_PARAMS_VERSION); + +  transport_params_copy(dest, src, transport_params_version); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.h b/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.h new file mode 100644 index 00000000000..c077f06a9dd --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_transport_params.h @@ -0,0 +1,118 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_TRANSPORT_PARAMS_H +#define NGTCP2_TRANSPORT_PARAMS_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* ngtcp2_transport_param_id is the registry of QUIC transport +   parameter ID. */ +typedef uint64_t ngtcp2_transport_param_id; + +#define NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID 0x00 +#define NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT 0x01 +#define NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN 0x02 +#define NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE 0x03 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA 0x04 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 0x05 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 0x06 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI 0x07 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI 0x08 +#define NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI 0x09 +#define NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT 0x0a +#define NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY 0x0b +#define NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION 0x0c +#define NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS 0x0d +#define NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT 0x0e +#define NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID 0x0f +#define NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID 0x10 +/* https://datatracker.ietf.org/doc/html/rfc9221 */ +#define NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE 0x20 +#define NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT 0x2ab2 +/* https://datatracker.ietf.org/doc/html/rfc9368 */ +#define NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION 0x11 + +/* NGTCP2_MAX_STREAMS is the maximum number of streams. */ +#define NGTCP2_MAX_STREAMS (1LL << 60) + +/* + * ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns + * it to |*pdest|.  If |src| is NULL, NULL is assigned to |*pdest|. + * + * Caller is responsible to call ngtcp2_transport_params_del to free + * the memory assigned to |*pdest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + *     Out of memory. + */ +int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest, +                                     const ngtcp2_transport_params *src, +                                     const ngtcp2_mem *mem); + +/* + * ngtcp2_transport_params_convert_to_latest converts |src| of version + * |transport_params_version| to the latest version + * NGTCP2_TRANSPORT_PARAMS_VERSION. + * + * |dest| must point to the latest version.  |src| may be the older + * version, and if so, it may have fewer fields.  Accessing those + * fields causes undefined behavior. + * + * If |transport_params_version| == NGTCP2_TRANSPORT_PARAMS_VERSION, + * no conversion is made, and |src| is returned.  Otherwise, first + * |dest| is initialized via ngtcp2_transport_params_default, and then + * all valid fields in |src| are copied into |dest|.  Finally, |dest| + * is returned. + */ +const ngtcp2_transport_params * +ngtcp2_transport_params_convert_to_latest(ngtcp2_transport_params *dest, +                                          int transport_params_version, +                                          const ngtcp2_transport_params *src); + +/* + * ngtcp2_transport_params_convert_to_old converts |src| of the latest + * version to |dest| of version |transport_params_version|. + * + * |transport_params_version| must not be the latest version + *  NGTCP2_TRANSPORT_PARAMS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version + * |transport_params_version| from |src| to |dest|. + */ +void ngtcp2_transport_params_convert_to_old(int transport_params_version, +                                            ngtcp2_transport_params *dest, +                                            const ngtcp2_transport_params *src); + +#endif /* !defined(NGTCP2_TRANSPORT_PARAMS_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_tstamp.h b/contrib/libs/ngtcp2/lib/ngtcp2_tstamp.h new file mode 100644 index 00000000000..2b1bb51d87e --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_tstamp.h @@ -0,0 +1,68 @@ +/* + * ngtcp2 + * + * Copyright (c) 2023 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_TSTAMP_H +#define NGTCP2_TSTAMP_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_tstamp_elapsed returns nonzero if at least |d| has passed + * since |base|.  |ts| expresses a current time, and must not be + * UINT64_MAX. + * + * If |base| is UINT64_MAX, this function returns 0 because UINT64_MAX + * is an invalid timestamp.  Otherwise, if |base| + |d| >= UINT64_MAX, + * this function returns 0. + * + * !ngtcp2_tstamp_elapsed() == ngtcp2_tstamp_not_elapsed() does not + * hold when |base| is UINT64_MAX.  If you need nonzero if |base| is + * UINT64_MAX, use !ngtcp2_tstamp_elapsed.  Otherwise, use + * ngtcp2_tstamp_not_elapsed. + */ +static inline int ngtcp2_tstamp_elapsed(ngtcp2_tstamp base, ngtcp2_duration d, +                                        ngtcp2_tstamp ts) { +  return base != UINT64_MAX && base < UINT64_MAX - d && base + d <= ts; +} + +/* + * ngtcp2_tstamp_not_elapsed returns nonzero if |d| has not passed + * since |base|.  |ts| expresses a current time, and must not be + * UINT64_MAX. + * + * If |base| is UINT64_MAX, this function returns 0 because UINT64_MAX + * is an invalid timestamp.  Otherwise, if |base| + |d| >= UINT64_MAX, + * this function returns nonzero. + */ +static inline int ngtcp2_tstamp_not_elapsed(ngtcp2_tstamp base, +                                            ngtcp2_duration d, +                                            ngtcp2_tstamp ts) { +  return base != UINT64_MAX && (base >= UINT64_MAX - d || base + d > ts); +} + +#endif /* !defined(NGTCP2_TSTAMP_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.c b/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.c new file mode 100644 index 00000000000..5ab1db72cc5 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.c @@ -0,0 +1,74 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_unreachable.h" + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#ifdef HAVE_UNISTD_H +#  define NGTCP2_UNREACHABLE_LOG +#  include <unistd.h> +#elif defined(WIN32) +#  define NGTCP2_UNREACHABLE_LOG +#  include <io.h> +#endif /* defined(WIN32) */ + +void ngtcp2_unreachable_fail(const char *file, int line, const char *func) { +#ifdef NGTCP2_UNREACHABLE_LOG +  char *buf; +  size_t buflen; +  int rv; + +#  define NGTCP2_UNREACHABLE_TEMPLATE "%s:%d %s: Unreachable.\n" + +  rv = snprintf(NULL, 0, NGTCP2_UNREACHABLE_TEMPLATE, file, line, func); +  if (rv < 0) { +    abort(); +  } + +  /* here we explicitly use system malloc */ +  buflen = (size_t)rv + 1; +  buf = malloc(buflen); +  if (buf == NULL) { +    abort(); +  } + +  rv = snprintf(buf, buflen, NGTCP2_UNREACHABLE_TEMPLATE, file, line, func); +  if (rv < 0) { +    abort(); +  } + +#  ifndef WIN32 +  while (write(STDERR_FILENO, buf, (size_t)rv) == -1 && errno == EINTR) +    ; +#  else  /* defined(WIN32) */ +  _write(_fileno(stderr), buf, (unsigned int)rv); +#  endif /* defined(WIN32) */ + +  free(buf); +#endif /* defined(NGTCP2_UNREACHABLE_LOG) */ + +  abort(); +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.h b/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.h new file mode 100644 index 00000000000..ca712386527 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_unreachable.h @@ -0,0 +1,52 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 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_UNREACHABLE_H +#define NGTCP2_UNREACHABLE_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#ifdef __FILE_NAME__ +#  define NGTCP2_FILE_NAME __FILE_NAME__ +#else /* !defined(__FILE_NAME__) */ +#  define NGTCP2_FILE_NAME "(file)" +#endif /* !defined(__FILE_NAME__) */ + +#define ngtcp2_unreachable()                                                   \ +  ngtcp2_unreachable_fail(NGTCP2_FILE_NAME, __LINE__, __func__) + +#ifdef _MSC_VER +__declspec(noreturn) +#endif /* defined(_MSC_VER) */ +    void ngtcp2_unreachable_fail(const char *file, int line, const char *func) +#ifndef _MSC_VER +        __attribute__((noreturn)) +#endif /* !defined(_MSC_VER) */ +        ; + +#endif /* !defined(NGTCP2_UNREACHABLE_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_vec.c b/contrib/libs/ngtcp2/lib/ngtcp2_vec.c new file mode 100644 index 00000000000..0b9c92d47d7 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_vec.c @@ -0,0 +1,245 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_vec.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" + +ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) { +  vec->base = (uint8_t *)base; +  vec->len = len; +  return vec; +} + +int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, +                   const ngtcp2_mem *mem) { +  size_t len; +  uint8_t *p; + +  len = sizeof(ngtcp2_vec) + datalen; + +  *pvec = ngtcp2_mem_malloc(mem, len); +  if (*pvec == NULL) { +    return NGTCP2_ERR_NOMEM; +  } + +  p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec); +  (*pvec)->base = p; +  (*pvec)->len = datalen; + +  if (datalen) { +    /* p = */ ngtcp2_cpymem(p, data, datalen); +  } + +  return 0; +} + +void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { +  ngtcp2_mem_free(mem, vec); +} + +uint64_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; +} + +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n) { +  uint64_t res = 0; +  size_t len; +  size_t i; + +  for (i = 0; i < n; ++i) { +    len = vec[i].len; +    if (len > NGTCP2_MAX_VARINT - res) { +      return -1; +    } + +    res += len; +  } + +  return (int64_t)res; +} + +ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, +                              size_t *psrccnt, 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 = 0; +  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->len = left; +      a->base = b->base; + +      b->len -= left; +      b->base += left; + +      return left; +    } + +    *a = *b; +    ++*pdstcnt; +    left -= b->len; +    i = 1; +  } + +  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 += 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; +    } + +    if (a->base + a->len == b->base) { +      a->len += b->len; +    } else if (*pdstcnt == maxcnt) { +      break; +    } else { +      dst[(*pdstcnt)++] = *b; +    } + +    left -= b->len; +  } + +  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 dstcnt, +                               const ngtcp2_vec *src, size_t srccnt, +                               size_t left) { +  size_t i, j; + +  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; +      return j + 1; +    } + +    left -= dst[j].len; +    ++i; +    ++j; +  } + +  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/contrib/libs/ngtcp2/lib/ngtcp2_vec.h b/contrib/libs/ngtcp2/lib/ngtcp2_vec.h new file mode 100644 index 00000000000..f7611efcb7d --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_vec.h @@ -0,0 +1,122 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_VEC_H +#define NGTCP2_VEC_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* + * ngtcp2_vec_lit is a convenient macro to fill the object pointed by + * |DEST| with the literal string |LIT|. + */ +#define ngtcp2_vec_lit(DEST, LIT)                                              \ +  ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST)) + +/* + * ngtcp2_vec_init initializes |vec| with the given parameters.  It + * returns |vec|. + */ +ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len); + +/* + * ngtcp2_vec_new allocates and initializes |*pvec| with given |data| + * of length |datalen|.  This function allocates memory for |*pvec| + * and the given data with a single allocation, and the contents + * pointed by |data| is copied into the allocated memory space.  To + * free the allocated memory, call ngtcp2_vec_del. + */ +int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, +                   const ngtcp2_mem *mem); + +/* + * ngtcp2_vec_del frees the memory allocated by |vec| which is + * allocated and initialized by ngtcp2_vec_new. + */ +void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); + +/* + * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. + */ +uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_len_varint is similar to ngtcp2_vec_len, but it returns + * -1 if the sum of the length exceeds NGTCP2_MAX_VARINT. + */ +int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_split splits |src| at the data position where + * ngtcp2_vec_len(|src|) does not exceed |left| bytes.  The removed + * vectors are moved to |dst|.  The existing elements in |dst| are + * moved forward to make up a space.  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 *dst, size_t *pdstcnt, ngtcp2_vec *src, +                              size_t *psrccnt, 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|.  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 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 /* !defined(NGTCP2_VEC_H) */ diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_version.c b/contrib/libs/ngtcp2/lib/ngtcp2_version.c new file mode 100644 index 00000000000..d2557e9ef8a --- /dev/null +++ b/contrib/libs/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 <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, +                              NGTCP2_VERSION}; + +const ngtcp2_info *ngtcp2_version(int least_version) { +  if (least_version > NGTCP2_VERSION_NUM) { +    return NULL; +  } +  return &version; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.c b/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.c new file mode 100644 index 00000000000..39f3d408a74 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.c @@ -0,0 +1,116 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#include "ngtcp2_window_filter.h" + +#include <string.h> + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, +                               uint64_t window_length) { +  wf->window_length = window_length; +  memset(wf->estimates, 0xff, sizeof(wf->estimates)); +} + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, +                                 uint64_t new_time) { +  /* Reset all estimates if they have not yet been initialized, if new +     sample is a new best, or if the newest recorded estimate is too +     old. */ +  if (wf->estimates[0].sample == UINT64_MAX || +      new_sample > wf->estimates[0].sample || +      new_time - wf->estimates[2].time > wf->window_length) { +    ngtcp2_window_filter_reset(wf, new_sample, new_time); +    return; +  } + +  if (new_sample > wf->estimates[1].sample) { +    wf->estimates[1].sample = new_sample; +    wf->estimates[1].time = new_time; +    wf->estimates[2] = wf->estimates[1]; +  } else if (new_sample > wf->estimates[2].sample) { +    wf->estimates[2].sample = new_sample; +    wf->estimates[2].time = new_time; +  } + +  /* Expire and update estimates as necessary. */ +  if (new_time - wf->estimates[0].time > wf->window_length) { +    /* The best estimate hasn't been updated for an entire window, so +       promote second and third best estimates. */ +    wf->estimates[0] = wf->estimates[1]; +    wf->estimates[1] = wf->estimates[2]; +    wf->estimates[2].sample = new_sample; +    wf->estimates[2].time = new_time; + +    /* Need to iterate one more time.  Check if the new best estimate +       is outside the window as well, since it may also have been +       recorded a long time ago.  Don't need to iterate once more +       since we cover that case at the beginning of the method. */ +    if (new_time - wf->estimates[0].time > wf->window_length) { +      wf->estimates[0] = wf->estimates[1]; +      wf->estimates[1] = wf->estimates[2]; +    } +    return; +  } + +  if (wf->estimates[1].sample == wf->estimates[0].sample && +      new_time - wf->estimates[1].time > wf->window_length >> 2) { +    /* A quarter of the window has passed without a better sample, so +       the second-best estimate is taken from the second quarter of +       the window. */ +    wf->estimates[2].sample = new_sample; +    wf->estimates[2].time = new_time; +    wf->estimates[1] = wf->estimates[2]; +    return; +  } + +  if (wf->estimates[2].sample == wf->estimates[1].sample && +      new_time - wf->estimates[2].time > wf->window_length >> 1) { +    /* We've passed a half of the window without a better estimate, so +       take a third-best estimate from the second half of the +       window. */ +    wf->estimates[2].sample = new_sample; +    wf->estimates[2].time = new_time; +  } +} + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, +                                uint64_t new_time) { +  wf->estimates[0].sample = new_sample; +  wf->estimates[0].time = new_time; +  wf->estimates[1] = wf->estimates[2] = wf->estimates[0]; +} + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) { +  return wf->estimates[0].sample; +} diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.h b/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.h new file mode 100644 index 00000000000..c90a9fdb907 --- /dev/null +++ b/contrib/libs/ngtcp2/lib/ngtcp2_window_filter.h @@ -0,0 +1,65 @@ +/* + * ngtcp2 + * + * Copyright (c) 2021 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. + */ + +/* + * Translated to C from the original C++ code + * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h + * with the following license: + * + * // Copyright (c) 2016 The Chromium Authors. All rights reserved. + * // Use of this source code is governed by a BSD-style license that can be + * // found in the LICENSE file. + */ +#ifndef NGTCP2_WINDOW_FILTER_H +#define NGTCP2_WINDOW_FILTER_H + +#ifdef HAVE_CONFIG_H +#  include <config.h> +#endif /* defined(HAVE_CONFIG_H) */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_window_filter_sample { +  uint64_t sample; +  uint64_t time; +} ngtcp2_window_filter_sample; + +typedef struct ngtcp2_window_filter { +  uint64_t window_length; +  ngtcp2_window_filter_sample estimates[3]; +} ngtcp2_window_filter; + +void ngtcp2_window_filter_init(ngtcp2_window_filter *wf, +                               uint64_t window_length); + +void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample, +                                 uint64_t new_time); + +void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample, +                                uint64_t new_time); + +uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf); + +#endif /* !defined(NGTCP2_WINDOW_FILTER_H) */ diff --git a/contrib/libs/ngtcp2/patches/staticlib.patch b/contrib/libs/ngtcp2/patches/staticlib.patch new file mode 100644 index 00000000000..5c90b20e1db --- /dev/null +++ b/contrib/libs/ngtcp2/patches/staticlib.patch @@ -0,0 +1,11 @@ +--- a/lib/includes/ngtcp2/ngtcp2.h ++++ b/lib/includes/ngtcp2/ngtcp2.h +@@ -64,7 +64,7 @@ +  + #include <ngtcp2/version.h> +  +-#ifdef NGTCP2_STATICLIB ++#if 1 + #  define NGTCP2_EXTERN + #elif defined(WIN32) + #  ifdef BUILDING_NGTCP2 diff --git a/contrib/libs/ngtcp2/ya.make b/contrib/libs/ngtcp2/ya.make new file mode 100644 index 00000000000..c1c7b88d783 --- /dev/null +++ b/contrib/libs/ngtcp2/ya.make @@ -0,0 +1,80 @@ +# Generated by devtools/yamaker from nixpkgs 22.11. + +LIBRARY() + +VERSION(1.8.1) + +ORIGINAL_SOURCE(https://github.com/ngtcp2/ngtcp2/releases/download/v1.8.1/ngtcp2-1.8.1.tar.xz) + +LICENSE( +    BSD-3-Clause AND +    FSFAP AND +    MIT +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( +    contrib/libs/openssl +) + +ADDINCL( +    GLOBAL contrib/libs/ngtcp2/lib/includes +    contrib/libs/ngtcp2 +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( +    -DBUILDING_NGTCP2 +    -DHAVE_CONFIG_H +) + +SRCS( +    lib/ngtcp2_acktr.c +    lib/ngtcp2_addr.c +    lib/ngtcp2_balloc.c +    lib/ngtcp2_bbr.c +    lib/ngtcp2_buf.c +    lib/ngtcp2_cc.c +    lib/ngtcp2_cid.c +    lib/ngtcp2_conn.c +    lib/ngtcp2_conv.c +    lib/ngtcp2_crypto.c +    lib/ngtcp2_crypto_quictls.c +    lib/ngtcp2_crypto_shared.c +    lib/ngtcp2_err.c +    lib/ngtcp2_frame_chain.c +    lib/ngtcp2_gaptr.c +    lib/ngtcp2_idtr.c +    lib/ngtcp2_ksl.c +    lib/ngtcp2_log.c +    lib/ngtcp2_map.c +    lib/ngtcp2_mem.c +    lib/ngtcp2_objalloc.c +    lib/ngtcp2_opl.c +    lib/ngtcp2_path.c +    lib/ngtcp2_pkt.c +    lib/ngtcp2_pmtud.c +    lib/ngtcp2_ppe.c +    lib/ngtcp2_pq.c +    lib/ngtcp2_pv.c +    lib/ngtcp2_qlog.c +    lib/ngtcp2_range.c +    lib/ngtcp2_ringbuf.c +    lib/ngtcp2_rob.c +    lib/ngtcp2_rst.c +    lib/ngtcp2_rtb.c +    lib/ngtcp2_settings.c +    lib/ngtcp2_str.c +    lib/ngtcp2_strm.c +    lib/ngtcp2_transport_params.c +    lib/ngtcp2_unreachable.c +    lib/ngtcp2_vec.c +    lib/ngtcp2_version.c +    lib/ngtcp2_window_filter.c +) + +END() diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA index 419722ab407..8ad26257c5b 100644 --- a/contrib/python/fonttools/.dist-info/METADATA +++ b/contrib/python/fonttools/.dist-info/METADATA @@ -1,6 +1,6 @@  Metadata-Version: 2.1  Name: fonttools -Version: 4.54.1 +Version: 4.55.0  Summary: Tools to manipulate font files  Home-page: http://github.com/fonttools/fonttools  Author: Just van Rossum @@ -23,6 +23,7 @@ Classifier: Programming Language :: Python :: 3.9  Classifier: Programming Language :: Python :: 3.10  Classifier: Programming Language :: Python :: 3.11  Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13  Classifier: Programming Language :: Python :: 3  Classifier: Topic :: Text Processing :: Fonts  Classifier: Topic :: Multimedia :: Graphics @@ -376,6 +377,24 @@ Have fun!  Changelog  ~~~~~~~~~ +4.55.0 (released 2024-11-14) +---------------------------- + + +- [cffLib.specializer] Adjust stack use calculation (#3689) +- [varLib] Lets not add mac names if the rest of name doesn't have them (#3688) +- [ttLib.reorderGlyphs] Update CFF table charstrings and charset (#3682) +- [cffLib.specializer] Add cmdline to specialize a CFF2 font (#3675, #3679) +- [CFF2] Lift uint16 VariationStore.length limitation (#3674) +- [subset] consider variation selectors subsetting cmap14 (#3672) +- [varLib.interpolatable] Support CFF2 fonts (#3670) +- Set isfinal to true in XML parser for proper resource cleanup (#3669) +- [removeOverlaps] Fix CFF CharString width (#3659) +- [glyf] Add optimizeSize option (#3657) +- Python 3.13 support (#3656) +- [TupleVariation] Optimize for loading speed, not size (#3650, #3653) + +  4.54.1 (released 2024-09-24)  ---------------------------- diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py index 0ec8b4ecfd1..502ca5cec65 100644 --- a/contrib/python/fonttools/fontTools/__init__.py +++ b/contrib/python/fonttools/fontTools/__init__.py @@ -3,6 +3,6 @@ from fontTools.misc.loggingTools import configLogger  log = logging.getLogger(__name__) -version = __version__ = "4.54.1" +version = __version__ = "4.55.0"  __all__ = ["version", "log", "configLogger"] diff --git a/contrib/python/fonttools/fontTools/cffLib/CFF2ToCFF.py b/contrib/python/fonttools/fontTools/cffLib/CFF2ToCFF.py index 689412ce2ba..f929cc96869 100644 --- a/contrib/python/fonttools/fontTools/cffLib/CFF2ToCFF.py +++ b/contrib/python/fonttools/fontTools/cffLib/CFF2ToCFF.py @@ -32,9 +32,10 @@ def _convertCFF2ToCFF(cff, otFont):      cff.major = 1 -    topDictData = TopDictIndex(None, isCFF2=True) +    topDictData = TopDictIndex(None)      for item in cff.topDictIndex:          # Iterate over, such that all are decompiled +        item.cff2GetGlyphOrder = None          topDictData.append(item)      cff.topDictIndex = topDictData      topDict = topDictData[0] @@ -99,6 +100,21 @@ def _convertCFF2ToCFF(cff, otFont):          if width != private.defaultWidthX:              cs.program.insert(0, width - private.nominalWidthX) +    mapping = { +        name: ("cid" + str(n) if n else ".notdef") +        for n, name in enumerate(topDict.charset) +    } +    topDict.charset = [ +        "cid" + str(n) if n else ".notdef" for n in range(len(topDict.charset)) +    ] +    charStrings.charStrings = { +        mapping[name]: v for name, v in charStrings.charStrings.items() +    } + +    # I'm not sure why the following is *not* necessary. And it breaks +    # the output if I add it. +    # topDict.ROS = ("Adobe", "Identity", 0) +  def convertCFF2ToCFF(font, *, updatePostTable=True):      cff = font["CFF2"].cff diff --git a/contrib/python/fonttools/fontTools/cffLib/CFFToCFF2.py b/contrib/python/fonttools/fontTools/cffLib/CFFToCFF2.py index 37463a5b9ba..2555f0b2425 100644 --- a/contrib/python/fonttools/fontTools/cffLib/CFFToCFF2.py +++ b/contrib/python/fonttools/fontTools/cffLib/CFFToCFF2.py @@ -81,7 +81,7 @@ def _convertCFFToCFF2(cff, otFont):          thisLocalSubrs = (              localSubrs[fdIndex] -            if fdIndex +            if fdIndex is not None              else (                  getattr(topDict.Private, "Subrs", [])                  if hasattr(topDict, "Private") @@ -104,9 +104,10 @@ def _convertCFFToCFF2(cff, otFont):              # just pop the first number since it may be a subroutine call.              # Instead, when seeing that, we embed the subroutine and recurse.              # If this ever happened, we later prune unused subroutines. -            while program[1] in ["callsubr", "callgsubr"]: +            while len(program) >= 2 and program[1] in ["callsubr", "callgsubr"]:                  removeUnusedSubrs = True                  subrNumber = program.pop(0) +                assert isinstance(subrNumber, int), subrNumber                  op = program.pop(0)                  bias = extractor.localBias if op == "callsubr" else extractor.globalBias                  subrNumber += bias @@ -114,6 +115,7 @@ def _convertCFFToCFF2(cff, otFont):                  subrProgram = subrSet[subrNumber].program                  program[:0] = subrProgram              # Now pop the actual width +            assert len(program) >= 1, program              program.pop(0)          if program and program[-1] == "endchar": diff --git a/contrib/python/fonttools/fontTools/cffLib/__init__.py b/contrib/python/fonttools/fontTools/cffLib/__init__.py index c192ec77afb..d75e23b750e 100644 --- a/contrib/python/fonttools/fontTools/cffLib/__init__.py +++ b/contrib/python/fonttools/fontTools/cffLib/__init__.py @@ -1,7 +1,7 @@  """cffLib: read/write Adobe CFF fonts -OpenType fonts with PostScript outlines contain a completely independent -font file, Adobe's *Compact Font Format*. So dealing with OpenType fonts +OpenType fonts with PostScript outlines embed a completely independent +font file in Adobe's *Compact Font Format*. So dealing with OpenType fonts  requires also dealing with CFF. This module allows you to read and write  fonts written in the CFF format. @@ -867,7 +867,11 @@ class VarStoreData(object):          if self.file:              # read data in from file. Assume position is correct.              length = readCard16(self.file) -            self.data = self.file.read(length) +            # https://github.com/fonttools/fonttools/issues/3673 +            if length == 65535: +                self.data = self.file.read() +            else: +                self.data = self.file.read(length)              globalState = {}              reader = OTTableReader(self.data, globalState)              self.otVarStore = ot.VarStore() @@ -1956,7 +1960,8 @@ class VarStoreCompiler(object):          self.parent = parent          if not varStoreData.data:              varStoreData.compile() -        data = [packCard16(len(varStoreData.data)), varStoreData.data] +        varStoreDataLen = min(0xFFFF, len(varStoreData.data)) +        data = [packCard16(varStoreDataLen), varStoreData.data]          self.data = bytesjoin(data)      def setPos(self, pos, endPos): @@ -2281,7 +2286,7 @@ class DictCompiler(object):                  # For PrivateDict BlueValues, the default font                  # values are absolute, not relative.                  # Must convert these back to relative coordinates -                # befor writing to CFF2. +                # before writing to CFF2.                  defaultValue = value[i][0]                  firstList[i] = defaultValue - prevVal                  prevVal = defaultValue diff --git a/contrib/python/fonttools/fontTools/cffLib/specializer.py b/contrib/python/fonttools/fontTools/cffLib/specializer.py index bb7f89e4ff0..5fddcb67dd3 100644 --- a/contrib/python/fonttools/fontTools/cffLib/specializer.py +++ b/contrib/python/fonttools/fontTools/cffLib/specializer.py @@ -80,8 +80,9 @@ def programToCommands(program, getNumRegions=None):              numBlendArgs = numBlends * numSourceFonts + 1              # replace first blend op by a list of the blend ops.              stack[-numBlendArgs:] = [stack[-numBlendArgs:]] -            lenBlendStack += numBlends + len(stack) - 1 -            lastBlendIndex = len(stack) +            lenStack = len(stack) +            lenBlendStack += numBlends + lenStack - 1 +            lastBlendIndex = lenStack              # if a blend op exists, this is or will be a CFF2 charstring.              continue @@ -153,9 +154,10 @@ def commandsToProgram(commands):  def _everyN(el, n):      """Group the list el into groups of size n""" -    if len(el) % n != 0: +    l = len(el) +    if l % n != 0:          raise ValueError(el) -    for i in range(0, len(el), n): +    for i in range(0, l, n):          yield el[i : i + n] @@ -218,9 +220,10 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def hhcurveto(args): -        if len(args) < 4 or len(args) % 4 > 1: +        l = len(args) +        if l < 4 or l % 4 > 1:              raise ValueError(args) -        if len(args) % 2 == 1: +        if l % 2 == 1:              yield ("rrcurveto", [args[1], args[0], args[2], args[3], args[4], 0])              args = args[5:]          for args in _everyN(args, 4): @@ -228,9 +231,10 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def vvcurveto(args): -        if len(args) < 4 or len(args) % 4 > 1: +        l = len(args) +        if l < 4 or l % 4 > 1:              raise ValueError(args) -        if len(args) % 2 == 1: +        if l % 2 == 1:              yield ("rrcurveto", [args[0], args[1], args[2], args[3], 0, args[4]])              args = args[5:]          for args in _everyN(args, 4): @@ -238,11 +242,12 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def hvcurveto(args): -        if len(args) < 4 or len(args) % 8 not in {0, 1, 4, 5}: +        l = len(args) +        if l < 4 or l % 8 not in {0, 1, 4, 5}:              raise ValueError(args)          last_args = None -        if len(args) % 2 == 1: -            lastStraight = len(args) % 8 == 5 +        if l % 2 == 1: +            lastStraight = l % 8 == 5              args, last_args = args[:-5], args[-5:]          it = _everyN(args, 4)          try: @@ -262,11 +267,12 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def vhcurveto(args): -        if len(args) < 4 or len(args) % 8 not in {0, 1, 4, 5}: +        l = len(args) +        if l < 4 or l % 8 not in {0, 1, 4, 5}:              raise ValueError(args)          last_args = None -        if len(args) % 2 == 1: -            lastStraight = len(args) % 8 == 5 +        if l % 2 == 1: +            lastStraight = l % 8 == 5              args, last_args = args[:-5], args[-5:]          it = _everyN(args, 4)          try: @@ -286,7 +292,8 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def rcurveline(args): -        if len(args) < 8 or len(args) % 6 != 2: +        l = len(args) +        if l < 8 or l % 6 != 2:              raise ValueError(args)          args, last_args = args[:-2], args[-2:]          for args in _everyN(args, 6): @@ -295,7 +302,8 @@ class _GeneralizerDecombinerCommandsMap(object):      @staticmethod      def rlinecurve(args): -        if len(args) < 8 or len(args) % 2 != 0: +        l = len(args) +        if l < 8 or l % 2 != 0:              raise ValueError(args)          args, last_args = args[:-6], args[-6:]          for args in _everyN(args, 2): @@ -330,8 +338,9 @@ def _convertBlendOpToArgs(blendList):      # comprehension. See calling context      args = args[:-1] -    numRegions = len(args) // numBlends - 1 -    if not (numBlends * (numRegions + 1) == len(args)): +    l = len(args) +    numRegions = l // numBlends - 1 +    if not (numBlends * (numRegions + 1) == l):          raise ValueError(blendList)      defaultArgs = [[arg] for arg in args[:numBlends]] @@ -368,7 +377,7 @@ def generalizeCommands(commands, ignoreErrors=False):                      raise          func = getattr(mapping, op, None) -        if not func: +        if func is None:              result.append((op, args))              continue          try: @@ -446,9 +455,9 @@ def _convertToBlendCmds(args):      i = 0      while i < num_args:          arg = args[i] +        i += 1          if not isinstance(arg, list):              new_args.append(arg) -            i += 1              stack_use += 1          else:              prev_stack_use = stack_use @@ -458,21 +467,26 @@ def _convertToBlendCmds(args):              # up to the max stack limit.              num_sources = len(arg) - 1              blendlist = [arg] -            i += 1              stack_use += 1 + num_sources  # 1 for the num_blends arg -            while (i < num_args) and isinstance(args[i], list): + +            # if we are here, max stack is the CFF2 max stack. +            # I use the CFF2 max stack limit here rather than +            # the 'maxstack' chosen by the client, as the default +            # maxstack may have been used unintentionally. For all +            # the other operators, this just produces a little less +            # optimization, but here it puts a hard (and low) limit +            # on the number of source fonts that can be used. +            # +            # Make sure the stack depth does not exceed (maxstack - 1), so +            # that subroutinizer can insert subroutine calls at any point. +            while ( +                (i < num_args) +                and isinstance(args[i], list) +                and stack_use + num_sources < maxStackLimit +            ):                  blendlist.append(args[i])                  i += 1                  stack_use += num_sources -                if stack_use + num_sources > maxStackLimit: -                    # if we are here, max stack is the CFF2 max stack. -                    # I use the CFF2 max stack limit here rather than -                    # the 'maxstack' chosen by the client, as the default -                    #  maxstack may have been used unintentionally. For all -                    # the other operators, this just produces a little less -                    # optimization, but here it puts a hard (and low) limit -                    # on the number of source fonts that can be used. -                    break              # blendList now contains as many single blend tuples as can be              # combined without exceeding the CFF2 stack limit.              num_blends = len(blendlist) @@ -504,6 +518,19 @@ def _addArgs(a, b):      return a + b +def _argsStackUse(args): +    stackLen = 0 +    maxLen = 0 +    for arg in args: +        if type(arg) is list: +            # Blended arg +            maxLen = max(maxLen, stackLen + _argsStackUse(arg)) +            stackLen += arg[-1] +        else: +            stackLen += 1 +    return max(stackLen, maxLen) + +  def specializeCommands(      commands,      ignoreErrors=False, @@ -697,6 +724,7 @@ def specializeCommands(              continue      # 5. Combine adjacent operators when possible, minding not to go over max stack size. +    stackUse = _argsStackUse(commands[-1][1]) if commands else 0      for i in range(len(commands) - 1, 0, -1):          op1, args1 = commands[i - 1]          op2, args2 = commands[i] @@ -707,9 +735,10 @@ def specializeCommands(              if op1 == op2:                  new_op = op1              else: -                if op2 == "rrcurveto" and len(args2) == 6: +                l = len(args2) +                if op2 == "rrcurveto" and l == 6:                      new_op = "rlinecurve" -                elif len(args2) == 2: +                elif l == 2:                      new_op = "rcurveline"          elif (op1, op2) in {("rlineto", "rlinecurve"), ("rrcurveto", "rcurveline")}: @@ -746,9 +775,14 @@ def specializeCommands(          # Make sure the stack depth does not exceed (maxstack - 1), so          # that subroutinizer can insert subroutine calls at any point. -        if new_op and len(args1) + len(args2) < maxstack: +        args1StackUse = _argsStackUse(args1) +        combinedStackUse = max(args1StackUse, len(args1) + stackUse) +        if new_op and combinedStackUse < maxstack:              commands[i - 1] = (new_op, args1 + args2)              del commands[i] +            stackUse = combinedStackUse +        else: +            stackUse = args1StackUse      # 6. Resolve any remaining made-up operators into real operators.      for i in range(len(commands)): @@ -759,9 +793,11 @@ def specializeCommands(              continue          if op[2:] == "curveto" and op[:2] not in {"rr", "hh", "vv", "vh", "hv"}: +            l = len(args) +              op0, op1 = op[:2]              if (op0 == "r") ^ (op1 == "r"): -                assert len(args) % 2 == 1 +                assert l % 2 == 1              if op0 == "0":                  op0 = "h"              if op1 == "0": @@ -772,9 +808,9 @@ def specializeCommands(                  op1 = _negateCategory(op0)              assert {op0, op1} <= {"h", "v"}, (op0, op1) -            if len(args) % 2: +            if l % 2:                  if op0 != op1:  # vhcurveto / hvcurveto -                    if (op0 == "h") ^ (len(args) % 8 == 1): +                    if (op0 == "h") ^ (l % 8 == 1):                          # Swap last two args order                          args = args[:-2] + args[-1:] + args[-2:-1]                  else:  # hhcurveto / vvcurveto @@ -822,26 +858,67 @@ if __name__ == "__main__":          default=None,          help="Number of variable-font regions for blend opertaions.",      ) +    parser.add_argument( +        "--font", +        metavar="FONTFILE", +        default=None, +        help="CFF2 font to specialize.", +    ) +    parser.add_argument( +        "-o", +        "--output-file", +        type=str, +        help="Output font file name.", +    )      options = parser.parse_args(sys.argv[1:]) -    getNumRegions = ( -        None -        if options.num_regions is None -        else lambda vsIndex: int(options.num_regions[0 if vsIndex is None else vsIndex]) -    ) - -    program = stringToProgram(options.program) -    print("Program:") -    print(programToString(program)) -    commands = programToCommands(program, getNumRegions) -    print("Commands:") -    print(commands) -    program2 = commandsToProgram(commands) -    print("Program from commands:") -    print(programToString(program2)) -    assert program == program2 -    print("Generalized program:") -    print(programToString(generalizeProgram(program, getNumRegions))) -    print("Specialized program:") -    print(programToString(specializeProgram(program, getNumRegions))) +    if options.program: +        getNumRegions = ( +            None +            if options.num_regions is None +            else lambda vsIndex: int( +                options.num_regions[0 if vsIndex is None else vsIndex] +            ) +        ) + +        program = stringToProgram(options.program) +        print("Program:") +        print(programToString(program)) +        commands = programToCommands(program, getNumRegions) +        print("Commands:") +        print(commands) +        program2 = commandsToProgram(commands) +        print("Program from commands:") +        print(programToString(program2)) +        assert program == program2 +        print("Generalized program:") +        print(programToString(generalizeProgram(program, getNumRegions))) +        print("Specialized program:") +        print(programToString(specializeProgram(program, getNumRegions))) + +    if options.font: +        from fontTools.ttLib import TTFont + +        font = TTFont(options.font) +        cff2 = font["CFF2"].cff.topDictIndex[0] +        charstrings = cff2.CharStrings +        for glyphName in charstrings.keys(): +            charstring = charstrings[glyphName] +            charstring.decompile() +            getNumRegions = charstring.private.getNumRegions +            charstring.program = specializeProgram( +                charstring.program, getNumRegions, maxstack=maxStackLimit +            ) + +        if options.output_file is None: +            from fontTools.misc.cliTools import makeOutputFileName + +            outfile = makeOutputFileName( +                options.font, overWrite=True, suffix=".specialized" +            ) +        else: +            outfile = options.output_file +        if outfile: +            print("Saving", outfile) +            font.save(outfile) diff --git a/contrib/python/fonttools/fontTools/cffLib/transforms.py b/contrib/python/fonttools/fontTools/cffLib/transforms.py index 91f6999fe69..5b474a7cd80 100644 --- a/contrib/python/fonttools/fontTools/cffLib/transforms.py +++ b/contrib/python/fonttools/fontTools/cffLib/transforms.py @@ -457,6 +457,8 @@ def remove_unused_subroutines(cff):              if subrs == font.GlobalSubrs:                  if not hasattr(font, "FDArray") and hasattr(font.Private, "Subrs"):                      local_subrs = font.Private.Subrs +                elif hasattr(font, "FDArray") and len(font.FDArray) == 1: +                    local_subrs = font.FDArray[0].Private.Subrs                  else:                      local_subrs = None              else: diff --git a/contrib/python/fonttools/fontTools/fontBuilder.py b/contrib/python/fonttools/fontTools/fontBuilder.py index 16b7ee167d2..d4af38fba48 100644 --- a/contrib/python/fonttools/fontTools/fontBuilder.py +++ b/contrib/python/fonttools/fontTools/fontBuilder.py @@ -918,7 +918,15 @@ class FontBuilder(object):          """          from .otlLib.builder import buildStatTable -        buildStatTable(self.font, axes, locations, elidedFallbackName) +        assert "name" in self.font, "name must to be set up first" + +        buildStatTable( +            self.font, +            axes, +            locations, +            elidedFallbackName, +            macNames=any(nr.platformID == 1 for nr in self.font["name"].names), +        )  def buildCmapSubTable(cmapping, format, platformID, platEncID): @@ -938,6 +946,15 @@ def addFvar(font, axes, instances):      fvar = newTable("fvar")      nameTable = font["name"] +    # if there are not currently any mac names don't add them here, that's inconsistent +    # https://github.com/fonttools/fonttools/issues/683 +    macNames = any(nr.platformID == 1 for nr in getattr(nameTable, "names", ())) + +    # we have all the best ways to express mac names +    platforms = ((3, 1, 0x409),) +    if macNames: +        platforms = ((1, 0, 0),) + platforms +      for axis_def in axes:          axis = Axis() @@ -963,7 +980,7 @@ def addFvar(font, axes, instances):          if isinstance(name, str):              name = dict(en=name) -        axis.axisNameID = nameTable.addMultilingualName(name, ttFont=font) +        axis.axisNameID = nameTable.addMultilingualName(name, ttFont=font, mac=macNames)          fvar.axes.append(axis)      for instance in instances: @@ -980,9 +997,11 @@ def addFvar(font, axes, instances):              name = dict(en=name)          inst = NamedInstance() -        inst.subfamilyNameID = nameTable.addMultilingualName(name, ttFont=font) +        inst.subfamilyNameID = nameTable.addMultilingualName( +            name, ttFont=font, mac=macNames +        )          if psname is not None: -            inst.postscriptNameID = nameTable.addName(psname) +            inst.postscriptNameID = nameTable.addName(psname, platforms=platforms)          inst.coordinates = coordinates          fvar.instances.append(inst) diff --git a/contrib/python/fonttools/fontTools/misc/testTools.py b/contrib/python/fonttools/fontTools/misc/testTools.py index be6116132d9..7d78721485c 100644 --- a/contrib/python/fonttools/fontTools/misc/testTools.py +++ b/contrib/python/fonttools/fontTools/misc/testTools.py @@ -38,7 +38,7 @@ def parseXML(xmlSnippet):              % type(xmlSnippet).__name__          )      xml += b"</root>" -    reader.parser.Parse(xml, 0) +    reader.parser.Parse(xml, 1)      return reader.root[2] diff --git a/contrib/python/fonttools/fontTools/subset/__init__.py b/contrib/python/fonttools/fontTools/subset/__init__.py index 99556d49e17..8458edc3592 100644 --- a/contrib/python/fonttools/fontTools/subset/__init__.py +++ b/contrib/python/fonttools/fontTools/subset/__init__.py @@ -2873,7 +2873,9 @@ def closure_glyphs(self, s):      # Close glyphs      for table in tables:          if table.format == 14: -            for cmap in table.uvsDict.values(): +            for varSelector, cmap in table.uvsDict.items(): +                if varSelector not in s.unicodes_requested: +                    continue                  glyphs = {g for u, g in cmap if u in s.unicodes_requested}                  if None in glyphs:                      glyphs.remove(None) @@ -2928,6 +2930,7 @@ def subset_glyphs(self, s):                      if g in s.glyphs_requested or u in s.unicodes_requested                  ]                  for v, l in t.uvsDict.items() +                if v in s.unicodes_requested              }              t.uvsDict = {v: l for v, l in t.uvsDict.items() if l}          elif t.isUnicode(): @@ -3797,6 +3800,8 @@ def main(args=None):              for t in font["cmap"].tables:                  if t.isUnicode():                      unicodes.extend(t.cmap.keys()) +                    if t.format == 14: +                        unicodes.extend(t.uvsDict.keys())          assert "" not in glyphs      log.info("Text: '%s'" % text) diff --git a/contrib/python/fonttools/fontTools/ttLib/removeOverlaps.py b/contrib/python/fonttools/fontTools/ttLib/removeOverlaps.py index 312b56b294a..6dadf4aa528 100644 --- a/contrib/python/fonttools/fontTools/ttLib/removeOverlaps.py +++ b/contrib/python/fonttools/fontTools/ttLib/removeOverlaps.py @@ -87,7 +87,11 @@ def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:  def _charString_from_SkPath(      path: pathops.Path, charString: T2CharString  ) -> T2CharString: -    t2Pen = T2CharStringPen(width=charString.width, glyphSet=None) +    if charString.width == charString.private.defaultWidthX: +        width = None +    else: +        width = charString.width - charString.private.nominalWidthX +    t2Pen = T2CharStringPen(width=width, glyphSet=None)      path.draw(t2Pen)      return t2Pen.getCharString(charString.private, charString.globalSubrs) diff --git a/contrib/python/fonttools/fontTools/ttLib/reorderGlyphs.py b/contrib/python/fonttools/fontTools/ttLib/reorderGlyphs.py index 3221261f16a..fd950ba0e32 100644 --- a/contrib/python/fonttools/fontTools/ttLib/reorderGlyphs.py +++ b/contrib/python/fonttools/fontTools/ttLib/reorderGlyphs.py @@ -19,9 +19,7 @@ from typing import (      Deque,      Iterable,      List, -    NamedTuple,      Tuple, -    Union,  ) @@ -276,3 +274,11 @@ def reorderGlyphs(font: ttLib.TTFont, new_glyph_order: List[str]):                  reorder_key = (type(value), getattr(value, "Format", None))                  for reorder in _REORDER_RULES.get(reorder_key, []):                      reorder.apply(font, value) + +    if "CFF " in font: +        cff_table = font["CFF "] +        charstrings = cff_table.cff.topDictIndex[0].CharStrings.charStrings +        cff_table.cff.topDictIndex[0].charset = new_glyph_order +        cff_table.cff.topDictIndex[0].CharStrings.charStrings = { +            k: charstrings.get(k) for k in new_glyph_order +        } diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py b/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py index a98bca2e0e1..bd6217e2ed9 100644 --- a/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py +++ b/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py @@ -129,7 +129,9 @@ class TupleVariation(object):              else:                  log.warning("bad delta format: %s" % ", ".join(sorted(attrs.keys()))) -    def compile(self, axisTags, sharedCoordIndices={}, pointData=None): +    def compile( +        self, axisTags, sharedCoordIndices={}, pointData=None, *, optimizeSize=True +    ):          assert set(self.axes.keys()) <= set(axisTags), (              "Unknown axis tag found.",              self.axes.keys(), @@ -161,7 +163,7 @@ class TupleVariation(object):              flags |= PRIVATE_POINT_NUMBERS              auxData.append(pointData) -        auxData.append(self.compileDeltas()) +        auxData.append(self.compileDeltas(optimizeSize=optimizeSize))          auxData = b"".join(auxData)          tupleData.insert(0, struct.pack(">HH", len(auxData), flags)) @@ -322,7 +324,7 @@ class TupleVariation(object):              )          return (result, pos) -    def compileDeltas(self): +    def compileDeltas(self, optimizeSize=True):          deltaX = []          deltaY = []          if self.getCoordWidth() == 2: @@ -337,12 +339,12 @@ class TupleVariation(object):                      continue                  deltaX.append(c)          bytearr = bytearray() -        self.compileDeltaValues_(deltaX, bytearr) -        self.compileDeltaValues_(deltaY, bytearr) +        self.compileDeltaValues_(deltaX, bytearr, optimizeSize=optimizeSize) +        self.compileDeltaValues_(deltaY, bytearr, optimizeSize=optimizeSize)          return bytearr      @staticmethod -    def compileDeltaValues_(deltas, bytearr=None): +    def compileDeltaValues_(deltas, bytearr=None, *, optimizeSize=True):          """[value1, value2, value3, ...] --> bytearray          Emits a sequence of runs. Each run starts with a @@ -360,18 +362,40 @@ class TupleVariation(object):          """  # Explaining the format because the 'gvar' spec is hard to understand.          if bytearr is None:              bytearr = bytearray() +          pos = 0          numDeltas = len(deltas) -        while pos < numDeltas: -            value = deltas[pos] -            if value == 0: + +        if optimizeSize: +            while pos < numDeltas: +                value = deltas[pos] +                if value == 0: +                    pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, bytearr) +                elif -128 <= value <= 127: +                    pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, bytearr) +                elif -32768 <= value <= 32767: +                    pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, bytearr) +                else: +                    pos = TupleVariation.encodeDeltaRunAsLongs_(deltas, pos, bytearr) +        else: +            minVal, maxVal = min(deltas), max(deltas) +            if minVal == 0 == maxVal:                  pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, bytearr) -            elif -128 <= value <= 127: -                pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, bytearr) -            elif -32768 <= value <= 32767: -                pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, bytearr) +            elif -128 <= minVal <= maxVal <= 127: +                pos = TupleVariation.encodeDeltaRunAsBytes_( +                    deltas, pos, bytearr, optimizeSize=False +                ) +            elif -32768 <= minVal <= maxVal <= 32767: +                pos = TupleVariation.encodeDeltaRunAsWords_( +                    deltas, pos, bytearr, optimizeSize=False +                )              else: -                pos = TupleVariation.encodeDeltaRunAsLongs_(deltas, pos, bytearr) +                pos = TupleVariation.encodeDeltaRunAsLongs_( +                    deltas, pos, bytearr, optimizeSize=False +                ) + +        assert pos == numDeltas, (pos, numDeltas) +          return bytearr      @staticmethod @@ -389,7 +413,7 @@ class TupleVariation(object):          return pos      @staticmethod -    def encodeDeltaRunAsBytes_(deltas, offset, bytearr): +    def encodeDeltaRunAsBytes_(deltas, offset, bytearr, optimizeSize=True):          pos = offset          numDeltas = len(deltas)          while pos < numDeltas: @@ -404,7 +428,12 @@ class TupleVariation(object):              # (04 0F 0F 00 0F 0F) when storing the zero value              # literally, but 7 bytes (01 0F 0F 80 01 0F 0F)              # when starting a new run. -            if value == 0 and pos + 1 < numDeltas and deltas[pos + 1] == 0: +            if ( +                optimizeSize +                and value == 0 +                and pos + 1 < numDeltas +                and deltas[pos + 1] == 0 +            ):                  break              pos += 1          runLength = pos - offset @@ -419,7 +448,7 @@ class TupleVariation(object):          return pos      @staticmethod -    def encodeDeltaRunAsWords_(deltas, offset, bytearr): +    def encodeDeltaRunAsWords_(deltas, offset, bytearr, optimizeSize=True):          pos = offset          numDeltas = len(deltas)          while pos < numDeltas: @@ -432,7 +461,7 @@ class TupleVariation(object):              # storing the zero literally (42 66 66 00 00 77 77),              # and equally 7 bytes when starting a new run              # (40 66 66 80 40 77 77). -            if value == 0: +            if optimizeSize and value == 0:                  break              # Within a word-encoded run of deltas, a single value @@ -442,7 +471,8 @@ class TupleVariation(object):              # the value literally (42 66 66 00 02 77 77), but 8 bytes              # when starting a new run (40 66 66 00 02 40 77 77).              if ( -                (-128 <= value <= 127) +                optimizeSize +                and (-128 <= value <= 127)                  and pos + 1 < numDeltas                  and (-128 <= deltas[pos + 1] <= 127)              ): @@ -470,12 +500,12 @@ class TupleVariation(object):          return pos      @staticmethod -    def encodeDeltaRunAsLongs_(deltas, offset, bytearr): +    def encodeDeltaRunAsLongs_(deltas, offset, bytearr, optimizeSize=True):          pos = offset          numDeltas = len(deltas)          while pos < numDeltas:              value = deltas[pos] -            if -32768 <= value <= 32767: +            if optimizeSize and -32768 <= value <= 32767:                  break              pos += 1          runLength = pos - offset @@ -677,7 +707,13 @@ def compileSharedTuples(  def compileTupleVariationStore( -    variations, pointCount, axisTags, sharedTupleIndices, useSharedPoints=True +    variations, +    pointCount, +    axisTags, +    sharedTupleIndices, +    useSharedPoints=True, +    *, +    optimizeSize=True,  ):      # pointCount is actually unused. Keeping for API compat.      del pointCount @@ -733,7 +769,9 @@ def compileTupleVariationStore(      ]      for v, p in zip(variations, pointDatas): -        thisTuple, thisData = v.compile(axisTags, sharedTupleIndices, pointData=p) +        thisTuple, thisData = v.compile( +            axisTags, sharedTupleIndices, pointData=p, optimizeSize=optimizeSize +        )          tuples.append(thisTuple)          data.append(thisData) diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py b/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py index fa11cf8f471..bc7d4bf1e45 100644 --- a/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py +++ b/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py @@ -713,7 +713,9 @@ class Glyph(object):          else:              self.decompileCoordinates(data) -    def compile(self, glyfTable, recalcBBoxes=True, *, boundsDone=None): +    def compile( +        self, glyfTable, recalcBBoxes=True, *, boundsDone=None, optimizeSize=None +    ):          if hasattr(self, "data"):              if recalcBBoxes:                  # must unpack glyph in order to recalculate bounding box @@ -730,7 +732,9 @@ class Glyph(object):          if self.isComposite():              data = data + self.compileComponents(glyfTable)          else: -            data = data + self.compileCoordinates() +            if optimizeSize is None: +                optimizeSize = getattr(glyfTable, "optimizeSize", True) +            data = data + self.compileCoordinates(optimizeSize=optimizeSize)          return data      def toXML(self, writer, ttFont): @@ -976,7 +980,7 @@ class Glyph(object):              data = data + struct.pack(">h", len(instructions)) + instructions          return data -    def compileCoordinates(self): +    def compileCoordinates(self, *, optimizeSize=True):          assert len(self.coordinates) == len(self.flags)          data = []          endPtsOfContours = array.array("H", self.endPtsOfContours) @@ -991,9 +995,12 @@ class Glyph(object):          deltas.toInt()          deltas.absoluteToRelative() -        # TODO(behdad): Add a configuration option for this? -        deltas = self.compileDeltasGreedy(self.flags, deltas) -        # deltas = self.compileDeltasOptimal(self.flags, deltas) +        if optimizeSize: +            # TODO(behdad): Add a configuration option for this? +            deltas = self.compileDeltasGreedy(self.flags, deltas) +            # deltas = self.compileDeltasOptimal(self.flags, deltas) +        else: +            deltas = self.compileDeltasForSpeed(self.flags, deltas)          data.extend(deltas)          return b"".join(data) @@ -1110,6 +1117,63 @@ class Glyph(object):          return (compressedFlags, compressedXs, compressedYs) +    def compileDeltasForSpeed(self, flags, deltas): +        # uses widest representation needed, for all deltas. +        compressedFlags = bytearray() +        compressedXs = bytearray() +        compressedYs = bytearray() + +        # Compute the necessary width for each axis +        xs = [d[0] for d in deltas] +        ys = [d[1] for d in deltas] +        minX, minY, maxX, maxY = min(xs), min(ys), max(xs), max(ys) +        xZero = minX == 0 and maxX == 0 +        yZero = minY == 0 and maxY == 0 +        xShort = -255 <= minX <= maxX <= 255 +        yShort = -255 <= minY <= maxY <= 255 + +        lastflag = None +        repeat = 0 +        for flag, (x, y) in zip(flags, deltas): +            # Oh, the horrors of TrueType +            # do x +            if xZero: +                flag = flag | flagXsame +            elif xShort: +                flag = flag | flagXShort +                if x > 0: +                    flag = flag | flagXsame +                else: +                    x = -x +                compressedXs.append(x) +            else: +                compressedXs.extend(struct.pack(">h", x)) +            # do y +            if yZero: +                flag = flag | flagYsame +            elif yShort: +                flag = flag | flagYShort +                if y > 0: +                    flag = flag | flagYsame +                else: +                    y = -y +                compressedYs.append(y) +            else: +                compressedYs.extend(struct.pack(">h", y)) +            # handle repeating flags +            if flag == lastflag and repeat != 255: +                repeat = repeat + 1 +                if repeat == 1: +                    compressedFlags.append(flag) +                else: +                    compressedFlags[-2] = flag | flagRepeat +                    compressedFlags[-1] = repeat +            else: +                repeat = 0 +                compressedFlags.append(flag) +            lastflag = flag +        return (compressedFlags, compressedXs, compressedYs) +      def recalcBounds(self, glyfTable, *, boundsDone=None):          """Recalculates the bounds of the glyph. @@ -1404,6 +1468,7 @@ class Glyph(object):                  pen.addComponent(glyphName, transform)              return +        self.expand(glyfTable)          coordinates, endPts, flags = self.getCoordinates(glyfTable)          if offset:              coordinates = coordinates.copy() diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_g_v_a_r.py b/contrib/python/fonttools/fontTools/ttLib/tables/_g_v_a_r.py index 044f65f716e..cdc9ef8e76a 100644 --- a/contrib/python/fonttools/fontTools/ttLib/tables/_g_v_a_r.py +++ b/contrib/python/fonttools/fontTools/ttLib/tables/_g_v_a_r.py @@ -85,6 +85,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):      def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):          result = []          glyf = ttFont["glyf"] +        optimizeSize = getattr(self, "optimizeSize", True)          for glyphName in ttFont.getGlyphOrder():              variations = self.variations.get(glyphName, [])              if not variations: @@ -93,7 +94,11 @@ class table__g_v_a_r(DefaultTable.DefaultTable):              pointCountUnused = 0  # pointCount is actually unused by compileGlyph              result.append(                  compileGlyph_( -                    variations, pointCountUnused, axisTags, sharedCoordIndices +                    variations, +                    pointCountUnused, +                    axisTags, +                    sharedCoordIndices, +                    optimizeSize=optimizeSize,                  )              )          return result @@ -248,9 +253,11 @@ class table__g_v_a_r(DefaultTable.DefaultTable):              return len(getattr(glyph, "coordinates", [])) + NUM_PHANTOM_POINTS -def compileGlyph_(variations, pointCount, axisTags, sharedCoordIndices): +def compileGlyph_( +    variations, pointCount, axisTags, sharedCoordIndices, *, optimizeSize=True +):      tupleVariationCount, tuples, data = tv.compileTupleVariationStore( -        variations, pointCount, axisTags, sharedCoordIndices +        variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize      )      if tupleVariationCount == 0:          return b"" diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/otConverters.py b/contrib/python/fonttools/fontTools/ttLib/tables/otConverters.py index 656836bd3cb..80685403018 100644 --- a/contrib/python/fonttools/fontTools/ttLib/tables/otConverters.py +++ b/contrib/python/fonttools/fontTools/ttLib/tables/otConverters.py @@ -2016,6 +2016,7 @@ converterMapping = {      # type		class      "int8": Int8,      "int16": Short, +    "int32": Long,      "uint8": UInt8,      "uint16": UShort,      "uint24": UInt24, diff --git a/contrib/python/fonttools/fontTools/ufoLib/__init__.py b/contrib/python/fonttools/fontTools/ufoLib/__init__.py index aa57beede24..42c06734ea9 100644 --- a/contrib/python/fonttools/fontTools/ufoLib/__init__.py +++ b/contrib/python/fonttools/fontTools/ufoLib/__init__.py @@ -1,35 +1,35 @@  """  A library for importing .ufo files and their descendants. -Refer to http://unifiedfontobject.com for the UFO specification. +Refer to http://unifiedfontobject.org for the UFO specification. -The UFOReader and UFOWriter classes support versions 1, 2 and 3 -of the specification. +The main interfaces are the :class:`.UFOReader` and :class:`.UFOWriter` +classes, which support versions 1, 2, and 3 of the UFO specification. -Sets that list the font info attribute names for the fontinfo.plist -formats are available for external use. These are: +Set variables are available for external use that list the font +info attribute names for the `fontinfo.plist` formats. These are: -- fontInfoAttributesVersion1 -- fontInfoAttributesVersion2 -- fontInfoAttributesVersion3 +- :obj:`.fontInfoAttributesVersion1` +- :obj:`.fontInfoAttributesVersion2` +- :obj:`.fontInfoAttributesVersion3` -A set listing the fontinfo.plist attributes that were deprecated +A set listing the `fontinfo.plist` attributes that were deprecated  in version 2 is available for external use: -- deprecatedFontInfoAttributesVersion2 +- :obj:`.deprecatedFontInfoAttributesVersion2` -Functions that do basic validation on values for fontinfo.plist +Functions that do basic validation on values for `fontinfo.plist`  are available for external use. These are -- validateFontInfoVersion2ValueForAttribute -- validateFontInfoVersion3ValueForAttribute +- :func:`.validateFontInfoVersion2ValueForAttribute` +- :func:`.validateFontInfoVersion3ValueForAttribute`  Value conversion functions are available for converting -fontinfo.plist values between the possible format versions. +`fontinfo.plist` values between the possible format versions. -- convertFontInfoValueForAttributeFromVersion1ToVersion2 -- convertFontInfoValueForAttributeFromVersion2ToVersion1 -- convertFontInfoValueForAttributeFromVersion2ToVersion3 -- convertFontInfoValueForAttributeFromVersion3ToVersion2 +- :func:`.convertFontInfoValueForAttributeFromVersion1ToVersion2` +- :func:`.convertFontInfoValueForAttributeFromVersion2ToVersion1` +- :func:`.convertFontInfoValueForAttributeFromVersion2ToVersion3` +- :func:`.convertFontInfoValueForAttributeFromVersion3ToVersion2`  """  import os @@ -201,8 +201,12 @@ class _UFOBaseIO:  class UFOReader(_UFOBaseIO): -    """ -    Read the various components of the .ufo. +    """Read the various components of a .ufo. + +    Attributes: +        path: An `os.PathLike` object pointing to the .ufo. +        validate: A boolean indicating if the data read should be +          validated. Defaults to `True`.      By default read data is validated. Set ``validate`` to      ``False`` to not validate the data. @@ -884,20 +888,27 @@ class UFOReader(_UFOBaseIO):  class UFOWriter(UFOReader): -    """ -    Write the various components of the .ufo. +    """Write the various components of a .ufo. + +    Attributes: +        path: An `os.PathLike` object pointing to the .ufo. +        formatVersion: the UFO format version as a tuple of integers (major, minor), +            or as a single integer for the major digit only (minor is implied to be 0). +            By default, the latest formatVersion will be used; currently it is 3.0, +            which is equivalent to formatVersion=(3, 0). +        fileCreator: The creator of the .ufo file. Defaults to +            `com.github.fonttools.ufoLib`. +        structure: The internal structure of the .ufo file: either `ZIP` or `PACKAGE`. +        validate: A boolean indicating if the data read should be validated. Defaults +            to `True`.      By default, the written data will be validated before writing. Set ``validate`` to      ``False`` if you do not want to validate the data. Validation can also be overriden -    on a per method level if desired. - -    The ``formatVersion`` argument allows to specify the UFO format version as a tuple -    of integers (major, minor), or as a single integer for the major digit only (minor -    is implied as 0). By default the latest formatVersion will be used; currently it's -    3.0, which is equivalent to formatVersion=(3, 0). +    on a per-method level if desired. -    An UnsupportedUFOFormat exception is raised if the requested UFO formatVersion is -    not supported. +    Raises: +        UnsupportedUFOFormat: An exception indicating that the requested UFO +            formatVersion is not supported.      """      def __init__( diff --git a/contrib/python/fonttools/fontTools/ufoLib/glifLib.py b/contrib/python/fonttools/fontTools/ufoLib/glifLib.py index 62e87db0df0..abbda491463 100644 --- a/contrib/python/fonttools/fontTools/ufoLib/glifLib.py +++ b/contrib/python/fonttools/fontTools/ufoLib/glifLib.py @@ -1191,8 +1191,12 @@ def _readGlyphFromTreeFormat1(              haveSeenAdvance = True              _readAdvance(glyphObject, element)          elif element.tag == "unicode": +            v = element.get("hex") +            if v is None: +                raise GlifLibError( +                    "A unicode element is missing its required hex attribute." +                )              try: -                v = element.get("hex")                  v = int(v, 16)                  if v not in unicodes:                      unicodes.append(v) @@ -1254,8 +1258,12 @@ def _readGlyphFromTreeFormat2(              haveSeenAdvance = True              _readAdvance(glyphObject, element)          elif element.tag == "unicode": +            v = element.get("hex") +            if v is None: +                raise GlifLibError( +                    "A unicode element is missing its required hex attribute." +                )              try: -                v = element.get("hex")                  v = int(v, 16)                  if v not in unicodes:                      unicodes.append(v) @@ -1757,7 +1765,7 @@ class _BaseParser:          parser = ParserCreate()          parser.StartElementHandler = self.startElementHandler          parser.EndElementHandler = self.endElementHandler -        parser.Parse(text) +        parser.Parse(text, 1)      def startElementHandler(self, name, attrs):          self._elementStack.append(name) diff --git a/contrib/python/fonttools/fontTools/ufoLib/plistlib.py b/contrib/python/fonttools/fontTools/ufoLib/plistlib.py index 38bb266b21d..0242e9d26fc 100644 --- a/contrib/python/fonttools/fontTools/ufoLib/plistlib.py +++ b/contrib/python/fonttools/fontTools/ufoLib/plistlib.py @@ -1,5 +1,5 @@  """DEPRECATED - This module is kept here only as a backward compatibility shim -for the old ufoLib.plistlib module, which was moved to fontTools.misc.plistlib. +for the old `ufoLib.plistlib` module, which was moved to :class:`fontTools.misc.plistlib`.  Please use the latter instead.  """ diff --git a/contrib/python/fonttools/fontTools/ufoLib/pointPen.py b/contrib/python/fonttools/fontTools/ufoLib/pointPen.py index baef9a583ec..4a8126cd648 100644 --- a/contrib/python/fonttools/fontTools/ufoLib/pointPen.py +++ b/contrib/python/fonttools/fontTools/ufoLib/pointPen.py @@ -1,5 +1,5 @@  """DEPRECATED - This module is kept here only as a backward compatibility shim -for the old ufoLib.pointPen module, which was moved to fontTools.pens.pointPen. +for the old `ufoLib.pointPen` module, which was moved to :class:`fontTools.pens.pointPen`.  Please use the latter instead.  """ diff --git a/contrib/python/fonttools/fontTools/varLib/__init__.py b/contrib/python/fonttools/fontTools/varLib/__init__.py index 36b1851cba7..43a16da5a9d 100644 --- a/contrib/python/fonttools/fontTools/varLib/__init__.py +++ b/contrib/python/fonttools/fontTools/varLib/__init__.py @@ -85,6 +85,15 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):      fvar = newTable("fvar")      nameTable = font["name"] +    # if there are not currently any mac names don't add them here, that's inconsistent +    # https://github.com/fonttools/fonttools/issues/683 +    macNames = any(nr.platformID == 1 for nr in getattr(nameTable, "names", ())) + +    # we have all the best ways to express mac names +    platforms = ((3, 1, 0x409),) +    if macNames: +        platforms = ((1, 0, 0),) + platforms +      for a in axes.values():          axis = Axis()          axis.axisTag = Tag(a.tag) @@ -95,7 +104,7 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):              a.maximum,          )          axis.axisNameID = nameTable.addMultilingualName( -            a.labelNames, font, minNameID=256 +            a.labelNames, font, minNameID=256, mac=macNames          )          axis.flags = int(a.hidden)          fvar.axes.append(axis) @@ -121,10 +130,12 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):          psname = instance.postScriptFontName          inst = NamedInstance() -        inst.subfamilyNameID = nameTable.addMultilingualName(localisedStyleName) +        inst.subfamilyNameID = nameTable.addMultilingualName( +            localisedStyleName, mac=macNames +        )          if psname is not None:              psname = tostr(psname) -            inst.postscriptNameID = nameTable.addName(psname) +            inst.postscriptNameID = nameTable.addName(psname, platforms=platforms)          inst.coordinates = {              axes[k].tag: axes[k].map_backward(v) for k, v in coordinates.items()          } diff --git a/contrib/python/fonttools/fontTools/varLib/interpolatable.py b/contrib/python/fonttools/fontTools/varLib/interpolatable.py index 60dbfeb7fc2..c5d7ecf525e 100644 --- a/contrib/python/fonttools/fontTools/varLib/interpolatable.py +++ b/contrib/python/fonttools/fontTools/varLib/interpolatable.py @@ -693,7 +693,7 @@ def main(args=None):      from fontTools import configLogger -    configLogger(level=("INFO" if args.verbose else "ERROR")) +    configLogger(level=("INFO" if args.verbose else "WARNING"))      if args.debug:          configLogger(level="DEBUG") @@ -750,41 +750,43 @@ def main(args=None):                  for k, vv in axis_triples.items()              } -        elif args.inputs[0].endswith(".ttf"): +        elif args.inputs[0].endswith(".ttf") or args.inputs[0].endswith(".otf"):              from fontTools.ttLib import TTFont +            # Is variable font? +              font = TTFont(args.inputs[0])              upem = font["head"].unitsPerEm -            if "gvar" in font: -                # Is variable font - -                fvar = font["fvar"] -                axisMapping = {} -                for axis in fvar.axes: -                    axisMapping[axis.axisTag] = { -                        -1: axis.minValue, -                        0: axis.defaultValue, -                        1: axis.maxValue, -                    } -                normalized = False -                if "avar" in font: -                    avar = font["avar"] -                    if getattr(avar.table, "VarStore", None): -                        axisMapping = {tag: {-1: -1, 0: 0, 1: 1} for tag in axisMapping} -                        normalized = True -                    else: -                        for axisTag, segments in avar.segments.items(): -                            fvarMapping = axisMapping[axisTag].copy() -                            for location, value in segments.items(): -                                axisMapping[axisTag][value] = piecewiseLinearMap( -                                    location, fvarMapping -                                ) +            fvar = font["fvar"] +            axisMapping = {} +            for axis in fvar.axes: +                axisMapping[axis.axisTag] = { +                    -1: axis.minValue, +                    0: axis.defaultValue, +                    1: axis.maxValue, +                } +            normalized = False +            if "avar" in font: +                avar = font["avar"] +                if getattr(avar.table, "VarStore", None): +                    axisMapping = {tag: {-1: -1, 0: 0, 1: 1} for tag in axisMapping} +                    normalized = True +                else: +                    for axisTag, segments in avar.segments.items(): +                        fvarMapping = axisMapping[axisTag].copy() +                        for location, value in segments.items(): +                            axisMapping[axisTag][value] = piecewiseLinearMap( +                                location, fvarMapping +                            ) + +            # Gather all glyphs at their "master" locations +            ttGlyphSets = {} +            glyphsets = defaultdict(dict) + +            if "gvar" in font:                  gvar = font["gvar"]                  glyf = font["glyf"] -                # Gather all glyphs at their "master" locations -                ttGlyphSets = {} -                glyphsets = defaultdict(dict)                  if glyphs is None:                      glyphs = sorted(gvar.variations.keys()) @@ -806,32 +808,87 @@ def main(args=None):                              glyphname, glyphsets[locTuple], ttGlyphSets[locTuple], glyf                          ) -                names = ["''"] -                fonts = [font.getGlyphSet()] -                locations = [{}] -                axis_triples = {a: (-1, 0, +1) for a in sorted(axisMapping.keys())} -                for locTuple in sorted(glyphsets.keys(), key=lambda v: (len(v), v)): -                    name = ( -                        "'" -                        + " ".join( -                            "%s=%s" -                            % ( -                                k, -                                floatToFixedToStr( -                                    piecewiseLinearMap(v, axisMapping[k]), 14 -                                ), -                            ) -                            for k, v in locTuple +            elif "CFF2" in font: +                fvarAxes = font["fvar"].axes +                cff2 = font["CFF2"].cff.topDictIndex[0] +                charstrings = cff2.CharStrings + +                if glyphs is None: +                    glyphs = sorted(charstrings.keys()) +                for glyphname in glyphs: +                    cs = charstrings[glyphname] +                    private = cs.private + +                    # Extract vsindex for the glyph +                    vsindices = {getattr(private, "vsindex", 0)} +                    vsindex = getattr(private, "vsindex", 0) +                    last_op = 0 +                    # The spec says vsindex can only appear once and must be the first +                    # operator in the charstring, but we support multiple. +                    # https://github.com/harfbuzz/boring-expansion-spec/issues/158 +                    for op in enumerate(cs.program): +                        if op == "blend": +                            vsindices.add(vsindex) +                        elif op == "vsindex": +                            assert isinstance(last_op, int) +                            vsindex = last_op +                        last_op = op + +                    if not hasattr(private, "vstore"): +                        continue + +                    varStore = private.vstore.otVarStore +                    for vsindex in vsindices: +                        varData = varStore.VarData[vsindex] +                        for regionIndex in varData.VarRegionIndex: +                            region = varStore.VarRegionList.Region[regionIndex] + +                            locDict = {} +                            loc = [] +                            for axisIndex, axis in enumerate(region.VarRegionAxis): +                                tag = fvarAxes[axisIndex].axisTag +                                val = axis.PeakCoord +                                locDict[tag] = val +                                loc.append((tag, val)) + +                            locTuple = tuple(loc) +                            if locTuple not in ttGlyphSets: +                                ttGlyphSets[locTuple] = font.getGlyphSet( +                                    location=locDict, +                                    normalized=True, +                                    recalcBounds=False, +                                ) + +                            glyphset = glyphsets[locTuple] +                            glyphset[glyphname] = ttGlyphSets[locTuple][glyphname] + +            names = ["''"] +            fonts = [font.getGlyphSet()] +            locations = [{}] +            axis_triples = {a: (-1, 0, +1) for a in sorted(axisMapping.keys())} +            for locTuple in sorted(glyphsets.keys(), key=lambda v: (len(v), v)): +                name = ( +                    "'" +                    + " ".join( +                        "%s=%s" +                        % ( +                            k, +                            floatToFixedToStr( +                                piecewiseLinearMap(v, axisMapping[k]), 14 +                            ),                          ) -                        + "'" +                        for k, v in locTuple                      ) -                    if normalized: -                        name += " (normalized)" -                    names.append(name) -                    fonts.append(glyphsets[locTuple]) -                    locations.append(dict(locTuple)) -                args.ignore_missing = True -                args.inputs = [] +                    + "'" +                ) +                if normalized: +                    name += " (normalized)" +                names.append(name) +                fonts.append(glyphsets[locTuple]) +                locations.append(dict(locTuple)) + +            args.ignore_missing = True +            args.inputs = []      if not locations:          locations = [{} for _ in fonts] @@ -854,6 +911,10 @@ def main(args=None):          names.append(basename(filename).rsplit(".", 1)[0]) +    if len(fonts) < 2: +        log.warning("Font file does not seem to be variable. Nothing to check.") +        return +      glyphsets = []      for font in fonts:          if hasattr(font, "getGlyphSet"): diff --git a/contrib/python/fonttools/fontTools/varLib/interpolatableTestContourOrder.py b/contrib/python/fonttools/fontTools/varLib/interpolatableTestContourOrder.py index 9edb1afcb55..36885297984 100644 --- a/contrib/python/fonttools/fontTools/varLib/interpolatableTestContourOrder.py +++ b/contrib/python/fonttools/fontTools/varLib/interpolatableTestContourOrder.py @@ -55,10 +55,10 @@ def test_contour_order(glyph0, glyph1):              m1GreenReversed = [(-m[0],) + m[1:] for m in m1Green]              (                  matching_control_reversed, -                matching_cost_control_reversed, -                identity_cost_control_reversed, -            ) = matching_for_vectors(m0Control, m1ControlReversed) -            done = matching_cost_control_reversed == identity_cost_control_reversed +                matching_cost_green_reversed, +                identity_cost_green_reversed, +            ) = matching_for_vectors(m0Green, m1GreenReversed) +            done = matching_cost_green_reversed == identity_cost_green_reversed          if not done:              # Otherwise, use the worst of the two matchings. diff --git a/contrib/python/fonttools/fontTools/varLib/multiVarStore.py b/contrib/python/fonttools/fontTools/varLib/multiVarStore.py index f24a6e6f755..2d074a353d9 100644 --- a/contrib/python/fonttools/fontTools/varLib/multiVarStore.py +++ b/contrib/python/fonttools/fontTools/varLib/multiVarStore.py @@ -50,7 +50,7 @@ class OnlineMultiVarStoreBuilder(object):          self._cache = None          self._data = None -    def finish(self, optimize=True): +    def finish(self):          self._regionList.RegionCount = len(self._regionList.Region)          self._store.MultiVarDataCount = len(self._store.MultiVarData)          return self._store diff --git a/contrib/python/fonttools/fontTools/varLib/mutator.py b/contrib/python/fonttools/fontTools/varLib/mutator.py index 80e46bb2441..f9f93790263 100644 --- a/contrib/python/fonttools/fontTools/varLib/mutator.py +++ b/contrib/python/fonttools/fontTools/varLib/mutator.py @@ -408,7 +408,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):              if set(excludedUnicodeLangIDs) == set(range(len((varfont["ltag"].tags)))):                  del varfont["ltag"]          varfont["name"].names[:] = [ -            n for n in varfont["name"].names if n.nameID not in exclude +            n +            for n in varfont["name"].names +            if n.nameID < 256 or n.nameID not in exclude          ]      if "wght" in location and "OS/2" in varfont: diff --git a/contrib/python/fonttools/fontTools/varLib/stat.py b/contrib/python/fonttools/fontTools/varLib/stat.py index 46c9498dc72..eacbf580aed 100644 --- a/contrib/python/fonttools/fontTools/varLib/stat.py +++ b/contrib/python/fonttools/fontTools/varLib/stat.py @@ -39,11 +39,18 @@ def buildVFStatTable(ttFont: TTFont, doc: DesignSpaceDocument, vfName: str) -> N      region = getVFUserRegion(doc, vf) +    # if there are not currently any mac names don't add them here, that's inconsistent +    # https://github.com/fonttools/fonttools/issues/683 +    macNames = any( +        nr.platformID == 1 for nr in getattr(ttFont.get("name"), "names", ()) +    ) +      return fontTools.otlLib.builder.buildStatTable(          ttFont,          getStatAxes(doc, region),          getStatLocations(doc, region),          doc.elidedFallbackName if doc.elidedFallbackName is not None else 2, +        macNames=macNames,      ) diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make index 402baf8a093..874cdd1b3b7 100644 --- a/contrib/python/fonttools/ya.make +++ b/contrib/python/fonttools/ya.make @@ -2,7 +2,7 @@  PY3_LIBRARY() -VERSION(4.54.1) +VERSION(4.55.0)  LICENSE(MIT)  | 
