From 89d764f028e86b093fbacbc3a15628550c838c2a Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 19 Feb 2020 15:36:11 -0800 Subject: [PATCH 1/6] deps: add support for BoringSSL QUIC APIs Sourced from: https://github.com/akamai/openssl/commits/OpenSSL_1_1_1d-quic This is a cherry-pick of 2a4b03a306439307e0b822b17eda3bdabddfbb68 on the master-quic-support2 branch (2019-10-07) Which was a rebase/squash of master-quic-support: * 5aa62ce Add support for more secrets - Todd Short/Todd Short (master-quic-support) * 58e0643 Tweeks to quic_change_cipher_state() - Todd Short/Todd Short * 8169702 Move QUIC code out of tls13_change_cipher_state() - Todd Short/Todd Short * a08cfe6 Correctly disable middlebox compat - Todd Short/Todd Short * 3a9eabf Add OPENSSL_NO_QUIC wrapper - Todd Short/Todd Short * f550eca Add client early traffic secret storage - Todd Short/Todd Short * 1b787ae Quick fix: s2c to c2s for early secret - Todd Short/Todd Short * f97e6a9 Don't process an incomplete message - Todd Short/Todd Short * 81f0ce2 Reset init state in SSL_process_quic_post_handshake() - Todd Short/Todd Short * 5d59cf9 Fix quic_transport constructors/parsers - Todd Short/Todd Short * 5e5f91c Fix INSTALL nit. - Todd Short/Todd Short * bd290ab Fix duplicate word in docs - Todd Short/Todd Short * 699590b fixup! Handle partial handshake messages - Todd Short/Todd Short * a472a8d Handle partial handshake messages - Todd Short/Todd Short * 363cf3d fixup! Use proper secrets for handshake - Todd Short/Todd Short * b03fee6 Use proper secrets for handshake - Todd Short/Todd Short * 2ab1aa0 Move QUIC transport params to encrypted extensions - Todd Short/Todd Short * 0d16af9 Make temp secret names less confusing - Todd Short/Todd Short * abb6f39 New method to get QUIC secret length - Todd Short/Todd Short * 05fdae9 Add support for BoringSSL QUIC APIs - Todd Short/Todd Short This adds a compatible API for BoringSSL's QUIC support, based on the current |draft-ietf-quic-tls|. Based on BoringSSL commit 3c034b2cf386b3131f75520705491871a2e0cafe Based on BoringSSL commit c8e0f90f83b9ec38ea833deb86b5a41360b62b6a Based on BoringSSL commit 3cbb0299a28a8bd0136257251a78b91a96c5eec8 Based on BoringSSL commit cc9d935256539af2d3b7f831abf57c0d685ffd81 Based on BoringSSL commit e6eef1ca16a022e476bbaedffef044597cfc8f4b Based on BoringSSL commit 6f733791148cf8a076bf0e95498235aadbe5926d Based on BoringSSL commit 384d0eaf1930af1ebc47eda751f0c78dfcba1c03 Based on BoringSSL commit a0373182eb5cc7b81d49f434596b473c7801c942 Based on BoringSSL commit b1b76aee3cb43ce11889403c5334283d951ebd37 New method to get QUIC secret length Make temp secret names less confusing Move QUIC transport params to encrypted extensions Use proper secrets for handshake fixup! Use proper secrets for handshake Handle partial handshake messages fixup! Handle partial handshake messages Fix duplicate word in docs Fix INSTALL nit. Fix quic_transport constructors/parsers Reset init state in SSL_process_quic_post_handshake() Don't process an incomplete message Quick fix: s2c to c2s for early secret Add client early traffic secret storage Add OPENSSL_NO_QUIC wrapper Correctly disable middlebox compat Move QUIC code out of tls13_change_cipher_state() Create quic_change_cipher_state() that does the minimal required to generate the QUIC secrets. (e.g. encryption contexts are not initialized). Tweeks to quic_change_cipher_state() Add support for more secrets PR-URL: https://github.com/nodejs/quic/pull/350 Reviewed-By: Anna Henningsen --- deps/openssl/openssl/CHANGES | 3 + deps/openssl/openssl/Configure | 3 + deps/openssl/openssl/INSTALL | 3 + deps/openssl/openssl/crypto/err/openssl.txt | 18 ++ deps/openssl/openssl/crypto/kdf/hkdf.c | 2 +- .../openssl/doc/man3/SSL_CIPHER_get_name.pod | 13 + .../doc/man3/SSL_CTX_set_quic_method.pod | 232 ++++++++++++++ deps/openssl/openssl/include/openssl/evp.h | 4 + .../openssl/include/openssl/ossl_typ.h | 2 + deps/openssl/openssl/include/openssl/ssl.h | 45 +++ deps/openssl/openssl/include/openssl/sslerr.h | 18 +- deps/openssl/openssl/include/openssl/tls1.h | 3 + deps/openssl/openssl/ssl/build.info | 3 +- deps/openssl/openssl/ssl/s3_msg.c | 12 +- deps/openssl/openssl/ssl/ssl_ciph.c | 32 ++ deps/openssl/openssl/ssl/ssl_err.c | 24 ++ deps/openssl/openssl/ssl/ssl_lib.c | 41 ++- deps/openssl/openssl/ssl/ssl_locl.h | 44 +++ deps/openssl/openssl/ssl/ssl_quic.c | 285 ++++++++++++++++++ deps/openssl/openssl/ssl/statem/extensions.c | 29 ++ .../openssl/ssl/statem/extensions_clnt.c | 52 ++++ .../openssl/ssl/statem/extensions_srvr.c | 55 +++- deps/openssl/openssl/ssl/statem/statem.c | 21 +- deps/openssl/openssl/ssl/statem/statem_clnt.c | 8 + deps/openssl/openssl/ssl/statem/statem_lib.c | 19 +- deps/openssl/openssl/ssl/statem/statem_locl.h | 19 ++ deps/openssl/openssl/ssl/statem/statem_quic.c | 109 +++++++ deps/openssl/openssl/ssl/statem/statem_srvr.c | 3 +- deps/openssl/openssl/ssl/tls13_enc.c | 155 ++++++++-- deps/openssl/openssl/test/sslapitest.c | 132 ++++++++ deps/openssl/openssl/test/ssltestlib.c | 5 + deps/openssl/openssl/util/libssl.num | 11 + deps/openssl/openssl/util/private.num | 2 + 33 files changed, 1373 insertions(+), 34 deletions(-) create mode 100644 deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod create mode 100644 deps/openssl/openssl/ssl/ssl_quic.c create mode 100644 deps/openssl/openssl/ssl/statem/statem_quic.c diff --git a/deps/openssl/openssl/CHANGES b/deps/openssl/openssl/CHANGES index c6ca3439480e94..427ff4a4c7dabe 100644 --- a/deps/openssl/openssl/CHANGES +++ b/deps/openssl/openssl/CHANGES @@ -9,6 +9,9 @@ Changes between 1.1.1c and 1.1.1d [10 Sep 2019] + *) Implement BoringSSL's QUIC API + [Todd Short] + *) Fixed a fork protection issue. OpenSSL 1.1.1 introduced a rewritten random number generator (RNG). This was intended to include protection in the event of a fork() system call in order to ensure that the parent and child diff --git a/deps/openssl/openssl/Configure b/deps/openssl/openssl/Configure index 5a699836f32a09..aa5f528ed3f318 100755 --- a/deps/openssl/openssl/Configure +++ b/deps/openssl/openssl/Configure @@ -378,6 +378,7 @@ my @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -494,6 +495,8 @@ my @disable_cascades = ( sub { !$disabled{"unit-test"} } => [ "heartbeats" ], sub { !$disabled{"msan"} } => [ "asm" ], + + "tls1_3" => [ "quic" ], ); # Avoid protocol support holes. Also disable all versions below N, if version diff --git a/deps/openssl/openssl/INSTALL b/deps/openssl/openssl/INSTALL index 2119cbae9e597d..56fcf9c459ad6a 100644 --- a/deps/openssl/openssl/INSTALL +++ b/deps/openssl/openssl/INSTALL @@ -453,6 +453,9 @@ no-psk Don't build support for Pre-Shared Key based ciphersuites. + no-quic + Don't build with support for QUIC. + no-rdrand Don't use hardware RDRAND capabilities. diff --git a/deps/openssl/openssl/crypto/err/openssl.txt b/deps/openssl/openssl/crypto/err/openssl.txt index a433b032407889..4622f3c3b6fe1c 100644 --- a/deps/openssl/openssl/crypto/err/openssl.txt +++ b/deps/openssl/openssl/crypto/err/openssl.txt @@ -1186,6 +1186,9 @@ SSL_F_PARSE_CA_NAMES:541:parse_ca_names SSL_F_PITEM_NEW:624:pitem_new SSL_F_PQUEUE_NEW:625:pqueue_new SSL_F_PROCESS_KEY_SHARE_EXT:439:* +SSL_F_QUIC_CHANGE_CIPHER_STATE:639:quic_change_cipher_state +SSL_F_QUIC_GET_MESSAGE:640:quic_get_message +SSL_F_QUIC_SET_ENCRYPTION_SECRETS:641:quic_set_encryption_secrets SSL_F_READ_STATE_MACHINE:352:read_state_machine SSL_F_SET_CLIENT_CIPHERSUITE:540:set_client_ciphersuite SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET:595:srp_generate_client_master_secret @@ -1196,7 +1199,9 @@ SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM:130:ssl3_check_cert_and_algorithm SSL_F_SSL3_CTRL:213:ssl3_ctrl SSL_F_SSL3_CTX_CTRL:133:ssl3_ctx_ctrl SSL_F_SSL3_DIGEST_CACHED_RECORDS:293:ssl3_digest_cached_records +SSL_F_SSL3_DISPATCH_ALERT:642:ssl3_dispatch_alert SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC:292:ssl3_do_change_cipher_spec +SSL_F_SSL3_DO_WRITE:643:ssl3_do_write SSL_F_SSL3_ENC:608:ssl3_enc SSL_F_SSL3_FINAL_FINISH_MAC:285:ssl3_final_finish_mac SSL_F_SSL3_FINISH_MAC:587:ssl3_finish_mac @@ -1304,6 +1309,8 @@ SSL_F_SSL_PARSE_SERVERHELLO_USE_SRTP_EXT:311:* SSL_F_SSL_PEEK:270:SSL_peek SSL_F_SSL_PEEK_EX:432:SSL_peek_ex SSL_F_SSL_PEEK_INTERNAL:522:ssl_peek_internal +SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE:644:SSL_process_quic_post_handshake +SSL_F_SSL_PROVIDE_QUIC_DATA:645:SSL_provide_quic_data SSL_F_SSL_READ:223:SSL_read SSL_F_SSL_READ_EARLY_DATA:529:SSL_read_early_data SSL_F_SSL_READ_EX:434:SSL_read_ex @@ -1353,6 +1360,7 @@ SSL_F_SSL_WRITE_EARLY_DATA:526:SSL_write_early_data SSL_F_SSL_WRITE_EARLY_FINISH:527:* SSL_F_SSL_WRITE_EX:433:SSL_write_ex SSL_F_SSL_WRITE_INTERNAL:524:ssl_write_internal +SSL_F_STATEM_FLUSH:646:statem_flush SSL_F_STATE_MACHINE:353:state_machine SSL_F_TLS12_CHECK_PEER_SIGALG:333:tls12_check_peer_sigalg SSL_F_TLS12_COPY_SIGALGS:533:tls12_copy_sigalgs @@ -1416,6 +1424,8 @@ SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH:619:\ tls_construct_ctos_post_handshake_auth SSL_F_TLS_CONSTRUCT_CTOS_PSK:501:tls_construct_ctos_psk SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES:509:tls_construct_ctos_psk_kex_modes +SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS:647:\ + tls_construct_ctos_quic_transport_params SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE:473:tls_construct_ctos_renegotiate SSL_F_TLS_CONSTRUCT_CTOS_SCT:474:tls_construct_ctos_sct SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME:475:tls_construct_ctos_server_name @@ -1457,6 +1467,8 @@ SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE:456:tls_construct_stoc_key_share SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN:548:tls_construct_stoc_maxfragmentlen SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG:457:tls_construct_stoc_next_proto_neg SSL_F_TLS_CONSTRUCT_STOC_PSK:504:tls_construct_stoc_psk +SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS:648:\ + tls_construct_stoc_quic_transport_params SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE:458:tls_construct_stoc_renegotiate SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME:459:tls_construct_stoc_server_name SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET:460:tls_construct_stoc_session_ticket @@ -1485,6 +1497,8 @@ SSL_F_TLS_PARSE_CTOS_MAXFRAGMENTLEN:571:tls_parse_ctos_maxfragmentlen SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH:620:tls_parse_ctos_post_handshake_auth SSL_F_TLS_PARSE_CTOS_PSK:505:tls_parse_ctos_psk SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES:572:tls_parse_ctos_psk_kex_modes +SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS:649:\ + tls_parse_ctos_quic_transport_params SSL_F_TLS_PARSE_CTOS_RENEGOTIATE:464:tls_parse_ctos_renegotiate SSL_F_TLS_PARSE_CTOS_SERVER_NAME:573:tls_parse_ctos_server_name SSL_F_TLS_PARSE_CTOS_SESSION_TICKET:574:tls_parse_ctos_session_ticket @@ -1503,6 +1517,8 @@ SSL_F_TLS_PARSE_STOC_KEY_SHARE:445:tls_parse_stoc_key_share SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN:581:tls_parse_stoc_maxfragmentlen SSL_F_TLS_PARSE_STOC_NPN:582:tls_parse_stoc_npn SSL_F_TLS_PARSE_STOC_PSK:502:tls_parse_stoc_psk +SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS:650:\ + tls_parse_stoc_quic_transport_params SSL_F_TLS_PARSE_STOC_RENEGOTIATE:448:tls_parse_stoc_renegotiate SSL_F_TLS_PARSE_STOC_SCT:564:tls_parse_stoc_sct SSL_F_TLS_PARSE_STOC_SERVER_NAME:583:tls_parse_stoc_server_name @@ -2702,6 +2718,7 @@ SSL_R_INCONSISTENT_EARLY_DATA_ALPN:222:inconsistent early data alpn SSL_R_INCONSISTENT_EARLY_DATA_SNI:231:inconsistent early data sni SSL_R_INCONSISTENT_EXTMS:104:inconsistent extms SSL_R_INSUFFICIENT_SECURITY:241:insufficient security +SSL_R_INTERNAL_ERROR:294:internal error SSL_R_INVALID_ALERT:205:invalid alert SSL_R_INVALID_CCS_MESSAGE:260:invalid ccs message SSL_R_INVALID_CERTIFICATE_OR_ALG:238:invalid certificate or alg @@ -2877,6 +2894,7 @@ SSL_R_VERSION_TOO_LOW:396:version too low SSL_R_WRONG_CERTIFICATE_TYPE:383:wrong certificate type SSL_R_WRONG_CIPHER_RETURNED:261:wrong cipher returned SSL_R_WRONG_CURVE:378:wrong curve +SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED:295:wrong encryption level received SSL_R_WRONG_SIGNATURE_LENGTH:264:wrong signature length SSL_R_WRONG_SIGNATURE_SIZE:265:wrong signature size SSL_R_WRONG_SIGNATURE_TYPE:370:wrong signature type diff --git a/deps/openssl/openssl/crypto/kdf/hkdf.c b/deps/openssl/openssl/crypto/kdf/hkdf.c index ae46fad609ac99..722b6c036ff8b6 100644 --- a/deps/openssl/openssl/crypto/kdf/hkdf.c +++ b/deps/openssl/openssl/crypto/kdf/hkdf.c @@ -15,7 +15,7 @@ #include "internal/cryptlib.h" #include "internal/evp_int.h" -#define HKDF_MAXBUF 1024 +#define HKDF_MAXBUF 2048 static unsigned char *HKDF(const EVP_MD *evp_md, const unsigned char *salt, size_t salt_len, diff --git a/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod b/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod index 26edae3d80be93..20437b76e84791 100644 --- a/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod +++ b/deps/openssl/openssl/doc/man3/SSL_CIPHER_get_name.pod @@ -13,6 +13,7 @@ SSL_CIPHER_get_digest_nid, SSL_CIPHER_get_handshake_digest, SSL_CIPHER_get_kx_nid, SSL_CIPHER_get_auth_nid, +SSL_CIPHER_get_prf_nid, SSL_CIPHER_is_aead, SSL_CIPHER_find, SSL_CIPHER_get_id, @@ -34,6 +35,7 @@ SSL_CIPHER_get_protocol_id const EVP_MD *SSL_CIPHER_get_handshake_digest(const SSL_CIPHER *c); int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c); int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c); + int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); int SSL_CIPHER_is_aead(const SSL_CIPHER *c); const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr); uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *c); @@ -91,6 +93,15 @@ TLS 1.3 cipher suites) B is returned. Examples (not comprehensive) NID_auth_ecdsa NID_auth_psk +SSL_CIPHER_get_prf_nid() retuns the pseudo-random function NID for B. If B is +a pre-TLS-1.2 cipher, it returns B but note these ciphers use +SHA-256 in TLS 1.2. Other return values may be treated uniformly in all +applicable versions. Examples (not comprehensive): + + NID_md5_sha1 + NID_sha256 + NID_sha384 + SSL_CIPHER_is_aead() returns 1 if the cipher B is AEAD (e.g. GCM or ChaCha20/Poly1305), and 0 if it is not AEAD. @@ -201,6 +212,8 @@ required to enable this function. The OPENSSL_cipher_name() function was added in OpenSSL 1.1.1. +The SSL_CIPHER_get_prf_nid() function was added in OpenSSL 3.0.0. + =head1 COPYRIGHT Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved. diff --git a/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod b/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod new file mode 100644 index 00000000000000..60bf704944b2eb --- /dev/null +++ b/deps/openssl/openssl/doc/man3/SSL_CTX_set_quic_method.pod @@ -0,0 +1,232 @@ +=pod + +=head1 NAME + +SSL_QUIC_METHOD, +OSSL_ENCRYPTION_LEVEL, +SSL_CTX_set_quic_method, +SSL_set_quic_method, +SSL_set_quic_transport_params, +SSL_get_peer_quic_transport_params, +SSL_quic_max_handshake_flight_len, +SSL_quic_read_level, +SSL_quic_write_level, +SSL_provide_quic_data, +SSL_process_quic_post_handshake, +SSL_is_quic +- QUIC support + +=head1 SYNOPSIS + + #include + + typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + typedef enum ssl_encryption_level_t OSSL_ENCRYPTION_LEVEL; + + int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); + int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); + int SSL_set_quic_transport_params(SSL *ssl, + const uint8_t *params, + size_t params_len); + void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len); + size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); + OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); + OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); + int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int SSL_process_quic_post_handshake(SSL *ssl); + int SSL_is_quic(SSL *ssl); + +=head1 DESCRIPTION + +SSL_CTX_set_quic_method() and SSL_set_quic_method() configures the QUIC methods. +This should only be configured with a minimum version of TLS 1.3. B +must remain valid for the lifetime of B or B. Calling this disables +the SSL_OP_ENABLE_MIDDLEBOX_COMPAT option, which is not required for QUIC. + +SSL_set_quic_transport_params() configures B to send B (of length +B) in the quic_transport_parameters extension in either the +ClientHello or EncryptedExtensions handshake message. This extension will +only be sent if the TLS version is at least 1.3, and for a server, only if +the client sent the extension. The buffer pointed to by B only need be +valid for the duration of the call to this function. + +SSL_get_peer_quic_transport_params() provides the caller with the value of the +quic_transport_parameters extension sent by the peer. A pointer to the buffer +containing the TransportParameters will be put in B<*out_params>, and its +length in B<*out_params_len>. This buffer will be valid for the lifetime of the +B. If no params were received from the peer, B<*out_params_len> will be 0. + +SSL_quic_max_handshake_flight_len() returns the maximum number of bytes +that may be received at the given encryption level. This function should be +used to limit buffering in the QUIC implementation. + +See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4. + +SSL_quic_read_level() returns the current read encryption level. + +SSL_quic_write_level() returns the current write encryption level. + +SSL_provide_quic_data() provides data from QUIC at a particular encryption +level B. It is an error to call this function outside of the handshake +or with an encryption level other than the current read level. It returns one +on success and zero on error. + +SSL_process_quic_post_handshake() processes any data that QUIC has provided +after the handshake has completed. This includes NewSessionTicket messages +sent by the server. + +SSL_is_quic() indicates whether a connection uses QUIC. + +=head1 NOTES + +These APIs are implementations of BoringSSL's QUIC APIs. + +QUIC acts as an underlying transport for the TLS 1.3 handshake. The following +functions allow a QUIC implementation to serve as the underlying transport as +described in draft-ietf-quic-tls. + +When configured for QUIC, SSL_do_handshake() will drive the handshake as +before, but it will not use the configured B. It will call functions on +B to configure secrets and send data. If data is needed from +the peer, it will return B. When received, the caller +should call SSL_provide_quic_data() and then SSL_do_handshake() to continue +the handshake. After the handshake is complete, the caller should call +SSL_provide_quic_data() for any post-handshake data, followed by +SSL_process_quic_post_handshake() to process it. It is an error to call +SSL_read()/SSL_read_ex() and SSL_write()/SSL_write_ex() in QUIC. + +Note that secrets for an encryption level may be available to QUIC before the +level is active in TLS. Callers should use SSL_quic_read_level() to determine +the active read level for SSL_provide_quic_data(). SSL_do_handshake() will +pass the active write level to add_handshake_data() when writing data. Callers +can use SSL_quic_write_level() to query the active write level when +generating their own errors. + +See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more +details. + +To avoid DoS attacks, the QUIC implementation must limit the amount of data +being queued up. The implementation can call +SSL_quic_max_handshake_flight_len() to get the maximum buffer length at each +encryption level. + +draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters +used by QUIC for each endpoint to unilaterally declare its supported +transport parameters. draft-ietf-quic-transport (section 7.4) defines the +contents of that extension (a TransportParameters struct) and describes how +to handle it and its semantic meaning. + +OpenSSL handles this extension as an opaque byte string. The caller is +responsible for serializing and parsing it. + +=head2 OSSL_ENCRYPTION_LEVEL + +B (B) represents the +encryption levels: + +=over 4 + +=item ssl_encryption_initial + +The initial encryption level that is used for client and server hellos. + +=item ssl_encryption_early_data + +The encryption level for early data. This is a write-level for the client +and a read-level for the server. + +=item ssl_encryption_handshake + +The encryption level for the remainder of the handshake. + +=item ssl_encryption_application + +The encryption level for the application data. + +=back + +=head2 SSL_QUIC_METHOD + +The B (B) describes the +QUIC methods. + + struct ssl_quic_method_st { + int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len); + int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int (*flush_flight)(SSL *ssl); + int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); + }; + typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + +set_encryption_secrets() configures the read and write secrets for the given +encryption level. This function will always be called before an encryption +level other than B is used. Note, however, that +secrets for a level may be configured before TLS is ready to send or accept +data at that level. + +When reading packets at a given level, the QUIC implementation must send +ACKs at the same level, so this function provides read and write secrets +together. The exception is B, where secrets are +only available in the client to server direction. The other secret will be +NULL. The server acknowledges such data at B, +which will be configured in the same SSL_do_handshake() call. + +This function should use SSL_get_current_cipher() to determine the TLS +cipher suite. + +add_handshake_data() adds handshake data to the current flight at the given +encryption level. It returns one on success and zero on error. + +OpenSSL will pack data from a single encryption level together, but a +single handshake flight may include multiple encryption levels. Callers +should defer writing data to the network until flush_flight() to better +pack QUIC packets into transport datagrams. + +flush_flight() is called when the current flight is complete and should be +written to the transport. Note a flight may contain data at several +encryption levels. + +send_alert() sends a fatal alert at the specified encryption level. + +All QUIC methods return 1 on success and 0 on error. + +=head1 RETURN VALUES + +SSL_CTX_set_quic_method(), +SSL_set_quic_method(), +SSL_set_quic_transport_params(), and +SSL_process_quic_post_handshake() +return 1 on success, and 0 on error. + +SSL_quic_read_level() and SSL_quic_write_level() return the current +encryption level as B (B). + +SSL_quic_max_handshake_flight_len() returns the maximum length of a flight +for a given encryption level. + +SSL_is_quic() returns 1 if QUIC is being used, 0 if not. + +=head1 SEE ALSO + +L, L, L + +=head1 HISTORY + +These functions were added in OpenSSL 3.0.0. + +=head1 COPYRIGHT + +Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/deps/openssl/openssl/include/openssl/evp.h b/deps/openssl/openssl/include/openssl/evp.h index 545654a98b1c08..073a957ad33504 100644 --- a/deps/openssl/openssl/include/openssl/evp.h +++ b/deps/openssl/openssl/include/openssl/evp.h @@ -1324,6 +1324,10 @@ void EVP_PKEY_asn1_set_security_bits(EVP_PKEY_ASN1_METHOD *ameth, */ # define EVP_PKEY_FLAG_SIGCTX_CUSTOM 4 +/* Used by Chromium/QUIC */ +# define X25519_PRIVATE_KEY_LEN 32 +# define X25519_PUBLIC_VALUE_LEN 32 + const EVP_PKEY_METHOD *EVP_PKEY_meth_find(int type); EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags); void EVP_PKEY_meth_get0_info(int *ppkey_id, int *pflags, diff --git a/deps/openssl/openssl/include/openssl/ossl_typ.h b/deps/openssl/openssl/include/openssl/ossl_typ.h index 7993ca28f3da5c..c374713bff2e82 100644 --- a/deps/openssl/openssl/include/openssl/ossl_typ.h +++ b/deps/openssl/openssl/include/openssl/ossl_typ.h @@ -175,6 +175,8 @@ typedef struct ct_policy_eval_ctx_st CT_POLICY_EVAL_CTX; typedef struct ossl_store_info_st OSSL_STORE_INFO; typedef struct ossl_store_search_st OSSL_STORE_SEARCH; +typedef struct ssl_quic_method_st SSL_QUIC_METHOD; + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ defined(INTMAX_MAX) && defined(UINTMAX_MAX) typedef intmax_t ossl_intmax_t; diff --git a/deps/openssl/openssl/include/openssl/ssl.h b/deps/openssl/openssl/include/openssl/ssl.h index 6724ccf2d25211..f21458cd5ecdf1 100644 --- a/deps/openssl/openssl/include/openssl/ssl.h +++ b/deps/openssl/openssl/include/openssl/ssl.h @@ -2432,6 +2432,51 @@ void SSL_set_allow_early_data_cb(SSL *s, SSL_allow_early_data_cb_fn cb, void *arg); +# ifndef OPENSSL_NO_QUIC +/* + * QUIC integration - The QUIC interface matches BoringSSL + * + * ssl_encryption_level_t represents a specific QUIC encryption level used to + * transmit handshake messages. BoringSSL has this as an 'enum'. + */ +typedef enum ssl_encryption_level_t { + ssl_encryption_initial = 0, + ssl_encryption_early_data, + ssl_encryption_handshake, + ssl_encryption_application +} OSSL_ENCRYPTION_LEVEL; + +struct ssl_quic_method_st { + int (*set_encryption_secrets)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len); + int (*add_handshake_data)(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); + int (*flush_flight)(SSL *ssl); + int (*send_alert)(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert); +}; + +__owur int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method); +__owur int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); +__owur int SSL_set_quic_transport_params(SSL *ssl, + const uint8_t *params, + size_t params_len); +void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len); +__owur size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level); +__owur OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl); +__owur OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl); +__owur int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len); +__owur int SSL_process_quic_post_handshake(SSL *ssl); + +__owur int SSL_is_quic(SSL *ssl); + +# endif + +int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c); + # ifdef __cplusplus } # endif diff --git a/deps/openssl/openssl/include/openssl/sslerr.h b/deps/openssl/openssl/include/openssl/sslerr.h index 3d6850dea36e26..30605c06847be2 100644 --- a/deps/openssl/openssl/include/openssl/sslerr.h +++ b/deps/openssl/openssl/include/openssl/sslerr.h @@ -11,9 +11,7 @@ #ifndef HEADER_SSLERR_H # define HEADER_SSLERR_H -# ifndef HEADER_SYMHACKS_H -# include -# endif +# include # ifdef __cplusplus extern "C" @@ -95,6 +93,9 @@ int ERR_load_SSL_strings(void); # define SSL_F_PITEM_NEW 624 # define SSL_F_PQUEUE_NEW 625 # define SSL_F_PROCESS_KEY_SHARE_EXT 439 +# define SSL_F_QUIC_CHANGE_CIPHER_STATE 639 +# define SSL_F_QUIC_GET_MESSAGE 640 +# define SSL_F_QUIC_SET_ENCRYPTION_SECRETS 641 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SET_CLIENT_CIPHERSUITE 540 # define SSL_F_SRP_GENERATE_CLIENT_MASTER_SECRET 595 @@ -105,7 +106,9 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL3_CTRL 213 # define SSL_F_SSL3_CTX_CTRL 133 # define SSL_F_SSL3_DIGEST_CACHED_RECORDS 293 +# define SSL_F_SSL3_DISPATCH_ALERT 642 # define SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC 292 +# define SSL_F_SSL3_DO_WRITE 643 # define SSL_F_SSL3_ENC 608 # define SSL_F_SSL3_FINAL_FINISH_MAC 285 # define SSL_F_SSL3_FINISH_MAC 587 @@ -210,6 +213,8 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_PEEK 270 # define SSL_F_SSL_PEEK_EX 432 # define SSL_F_SSL_PEEK_INTERNAL 522 +# define SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE 644 +# define SSL_F_SSL_PROVIDE_QUIC_DATA 645 # define SSL_F_SSL_READ 223 # define SSL_F_SSL_READ_EARLY_DATA 529 # define SSL_F_SSL_READ_EX 434 @@ -259,6 +264,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_WRITE_EARLY_FINISH 527 # define SSL_F_SSL_WRITE_EX 433 # define SSL_F_SSL_WRITE_INTERNAL 524 +# define SSL_F_STATEM_FLUSH 646 # define SSL_F_STATE_MACHINE 353 # define SSL_F_TLS12_CHECK_PEER_SIGALG 333 # define SSL_F_TLS12_COPY_SIGALGS 533 @@ -318,6 +324,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_CTOS_POST_HANDSHAKE_AUTH 619 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK 501 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES 509 +# define SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS 647 # define SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE 473 # define SSL_F_TLS_CONSTRUCT_CTOS_SCT 474 # define SSL_F_TLS_CONSTRUCT_CTOS_SERVER_NAME 475 @@ -357,6 +364,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN 548 # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457 # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504 +# define SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS 648 # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458 # define SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME 459 # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET 460 @@ -382,6 +390,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_CTOS_POST_HANDSHAKE_AUTH 620 # define SSL_F_TLS_PARSE_CTOS_PSK 505 # define SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES 572 +# define SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS 649 # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464 # define SSL_F_TLS_PARSE_CTOS_SERVER_NAME 573 # define SSL_F_TLS_PARSE_CTOS_SESSION_TICKET 574 @@ -400,6 +409,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_STOC_MAXFRAGMENTLEN 581 # define SSL_F_TLS_PARSE_STOC_NPN 582 # define SSL_F_TLS_PARSE_STOC_PSK 502 +# define SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS 650 # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448 # define SSL_F_TLS_PARSE_STOC_SCT 564 # define SSL_F_TLS_PARSE_STOC_SERVER_NAME 583 @@ -564,6 +574,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_INCONSISTENT_EARLY_DATA_SNI 231 # define SSL_R_INCONSISTENT_EXTMS 104 # define SSL_R_INSUFFICIENT_SECURITY 241 +# define SSL_R_INTERNAL_ERROR 294 # define SSL_R_INVALID_ALERT 205 # define SSL_R_INVALID_CCS_MESSAGE 260 # define SSL_R_INVALID_CERTIFICATE_OR_ALG 238 @@ -761,6 +772,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_WRONG_CERTIFICATE_TYPE 383 # define SSL_R_WRONG_CIPHER_RETURNED 261 # define SSL_R_WRONG_CURVE 378 +# define SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED 295 # define SSL_R_WRONG_SIGNATURE_LENGTH 264 # define SSL_R_WRONG_SIGNATURE_SIZE 265 # define SSL_R_WRONG_SIGNATURE_TYPE 370 diff --git a/deps/openssl/openssl/include/openssl/tls1.h b/deps/openssl/openssl/include/openssl/tls1.h index 76d9fda46e207e..6e16c97316ddcb 100644 --- a/deps/openssl/openssl/include/openssl/tls1.h +++ b/deps/openssl/openssl/include/openssl/tls1.h @@ -148,6 +148,9 @@ extern "C" { /* Temporary extension type */ # define TLSEXT_TYPE_renegotiate 0xff01 +/* ExtensionType value from draft-ietf-quic-tls-13 */ +# define TLSEXT_TYPE_quic_transport_parameters 0xffa5 + # ifndef OPENSSL_NO_NEXTPROTONEG /* This is not an IANA defined extension number */ # define TLSEXT_TYPE_next_proto_neg 13172 diff --git a/deps/openssl/openssl/ssl/build.info b/deps/openssl/openssl/ssl/build.info index bb2f1deb530066..eec0d14f2c5634 100644 --- a/deps/openssl/openssl/ssl/build.info +++ b/deps/openssl/openssl/ssl/build.info @@ -12,4 +12,5 @@ SOURCE[../libssl]=\ ssl_asn1.c ssl_txt.c ssl_init.c ssl_conf.c ssl_mcnf.c \ bio_ssl.c ssl_err.c tls_srp.c t1_trce.c ssl_utst.c \ record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \ - statem/statem.c record/ssl3_record_tls13.c + statem/statem.c record/ssl3_record_tls13.c \ + ssl_quic.c statem/statem_quic.c diff --git a/deps/openssl/openssl/ssl/s3_msg.c b/deps/openssl/openssl/ssl/s3_msg.c index 42382547fb2abb..a06054a0ead780 100644 --- a/deps/openssl/openssl/ssl/s3_msg.c +++ b/deps/openssl/openssl/ssl/s3_msg.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -75,6 +75,16 @@ int ssl3_dispatch_alert(SSL *s) s->s3->alert_dispatch = 0; alertlen = 2; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + if (!s->quic_method->send_alert(s, s->quic_write_level, + s->s3->send_alert[1])) { + SSLerr(SSL_F_SSL3_DISPATCH_ALERT, SSL_R_INTERNAL_ERROR); + return 0; + } + i = 1; + } else +#endif i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], &alertlen, 1, 0, &written); if (i <= 0) { diff --git a/deps/openssl/openssl/ssl/ssl_ciph.c b/deps/openssl/openssl/ssl/ssl_ciph.c index 27a1b2ec68b3d0..8b15616feb69bb 100644 --- a/deps/openssl/openssl/ssl/ssl_ciph.c +++ b/deps/openssl/openssl/ssl/ssl_ciph.c @@ -2162,3 +2162,35 @@ int ssl_cert_is_disabled(size_t idx) return 1; return 0; } + +int SSL_CIPHER_get_prf_nid(const SSL_CIPHER *c) +{ + switch (c->algorithm2 & (0xFF << TLS1_PRF_DGST_SHIFT)) { + default: + break; + case TLS1_PRF_SHA1_MD5: /* TLS1_PRF */ + return NID_md5_sha1; + case TLS1_PRF_SHA256: + return NID_sha256; + case TLS1_PRF_SHA384: + return NID_sha384; + case TLS1_PRF_GOST94: + return NID_id_GostR3411_94_prf; + case TLS1_PRF_GOST12_256: + return NID_id_GostR3411_2012_256; + case TLS1_PRF_GOST12_512: + return NID_id_GostR3411_2012_512; + } + /* TLSv1.3 ciphers don't specify separate PRF */ + switch (c->algorithm2 & SSL_HANDSHAKE_MAC_MASK) { + default: + break; + case SSL_HANDSHAKE_MAC_MD5_SHA1: /* SSL_HANDSHAKE_MAC_DEFAULT */ + return NID_md5_sha1; + case SSL_HANDSHAKE_MAC_SHA256: + return NID_sha256; + case SSL_HANDSHAKE_MAC_SHA384: + return NID_sha384; + } + return NID_undef; +} diff --git a/deps/openssl/openssl/ssl/ssl_err.c b/deps/openssl/openssl/ssl/ssl_err.c index 4b12ed1485d985..f5d5e8eed845cd 100644 --- a/deps/openssl/openssl/ssl/ssl_err.c +++ b/deps/openssl/openssl/ssl/ssl_err.c @@ -122,6 +122,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_PITEM_NEW, 0), "pitem_new"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_PQUEUE_NEW, 0), "pqueue_new"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_PROCESS_KEY_SHARE_EXT, 0), ""}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_CHANGE_CIPHER_STATE, 0), + "quic_change_cipher_state"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_GET_MESSAGE, 0), "quic_get_message"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, 0), + "quic_set_encryption_secrets"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_READ_STATE_MACHINE, 0), "read_state_machine"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SET_CLIENT_CIPHERSUITE, 0), "set_client_ciphersuite"}, @@ -139,8 +144,11 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_CTX_CTRL, 0), "ssl3_ctx_ctrl"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DIGEST_CACHED_RECORDS, 0), "ssl3_digest_cached_records"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DISPATCH_ALERT, 0), + "ssl3_dispatch_alert"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_CHANGE_CIPHER_SPEC, 0), "ssl3_do_change_cipher_spec"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_DO_WRITE, 0), "ssl3_do_write"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_ENC, 0), "ssl3_enc"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_FINAL_FINISH_MAC, 0), "ssl3_final_finish_mac"}, @@ -302,6 +310,10 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK, 0), "SSL_peek"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_EX, 0), "SSL_peek_ex"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PEEK_INTERNAL, 0), "ssl_peek_internal"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, 0), + "SSL_process_quic_post_handshake"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_PROVIDE_QUIC_DATA, 0), + "SSL_provide_quic_data"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ, 0), "SSL_read"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_READ_EARLY_DATA, 0), "SSL_read_early_data"}, @@ -378,6 +390,7 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EARLY_FINISH, 0), ""}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_EX, 0), "SSL_write_ex"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_WRITE_INTERNAL, 0), "ssl_write_internal"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_STATEM_FLUSH, 0), "statem_flush"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_STATE_MACHINE, 0), "state_machine"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS12_CHECK_PEER_SIGALG, 0), "tls12_check_peer_sigalg"}, @@ -479,6 +492,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_ctos_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PSK_KEX_MODES, 0), "tls_construct_ctos_psk_kex_modes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, 0), + "tls_construct_ctos_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_RENEGOTIATE, 0), "tls_construct_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_SCT, 0), @@ -550,6 +565,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_construct_stoc_next_proto_neg"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_PSK, 0), "tls_construct_stoc_psk"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, 0), + "tls_construct_stoc_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE, 0), "tls_construct_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_SERVER_NAME, 0), @@ -596,6 +613,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK, 0), "tls_parse_ctos_psk"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_PSK_KEX_MODES, 0), "tls_parse_ctos_psk_kex_modes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, 0), + "tls_parse_ctos_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_RENEGOTIATE, 0), "tls_parse_ctos_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_CTOS_SERVER_NAME, 0), @@ -628,6 +647,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "tls_parse_stoc_maxfragmentlen"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_NPN, 0), "tls_parse_stoc_npn"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_PSK, 0), "tls_parse_stoc_psk"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, 0), + "tls_parse_stoc_quic_transport_params"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_RENEGOTIATE, 0), "tls_parse_stoc_renegotiate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_PARSE_STOC_SCT, 0), "tls_parse_stoc_sct"}, @@ -905,6 +926,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_EXTMS), "inconsistent extms"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INSUFFICIENT_SECURITY), "insufficient security"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INTERNAL_ERROR), "internal error"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_ALERT), "invalid alert"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CCS_MESSAGE), "invalid ccs message"}, @@ -1248,6 +1270,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED), "wrong cipher returned"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "wrong curve"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED), + "wrong encryption level received"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH), "wrong signature length"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE), diff --git a/deps/openssl/openssl/ssl/ssl_lib.c b/deps/openssl/openssl/ssl/ssl_lib.c index ac820cf9fe160c..c002c5ec539633 100644 --- a/deps/openssl/openssl/ssl/ssl_lib.c +++ b/deps/openssl/openssl/ssl/ssl_lib.c @@ -839,6 +839,10 @@ SSL *SSL_new(SSL_CTX *ctx) s->job = NULL; +#ifndef OPENSSL_NO_QUIC + s->quic_method = ctx->quic_method; +#endif + #ifndef OPENSSL_NO_CT if (!SSL_set_ct_validation_callback(s, ctx->ct_validation_callback, ctx->ct_validation_callback_arg)) @@ -1204,6 +1208,18 @@ void SSL_free(SSL *s) OPENSSL_free(s->pha_context); EVP_MD_CTX_free(s->pha_dgst); +#ifndef OPENSSL_NO_QUIC + OPENSSL_free(s->ext.quic_transport_params); + OPENSSL_free(s->ext.peer_quic_transport_params); + while (s->quic_input_data_head != NULL) { + QUIC_DATA *qd; + + qd = s->quic_input_data_head; + s->quic_input_data_head = qd->next; + OPENSSL_free(qd); + } +#endif + sk_X509_NAME_pop_free(s->ca_names, X509_NAME_free); sk_X509_NAME_pop_free(s->client_ca_names, X509_NAME_free); @@ -1723,6 +1739,12 @@ static int ssl_io_intern(void *vargs) int ssl_read_internal(SSL *s, void *buf, size_t num, size_t *readbytes) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_READ_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_READ_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -1855,6 +1877,12 @@ int SSL_get_early_data_status(const SSL *s) static int ssl_peek_internal(SSL *s, void *buf, size_t num, size_t *readbytes) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_PEEK_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_PEEK_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -1915,6 +1943,12 @@ int SSL_peek_ex(SSL *s, void *buf, size_t num, size_t *readbytes) int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + SSLerr(SSL_F_SSL_WRITE_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } +#endif if (s->handshake_func == NULL) { SSLerr(SSL_F_SSL_WRITE_INTERNAL, SSL_R_UNINITIALIZED); return -1; @@ -3511,6 +3545,11 @@ int SSL_get_error(const SSL *s, int i) } if (SSL_want_read(s)) { +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + return SSL_ERROR_WANT_READ; + } +#endif bio = SSL_get_rbio(s); if (BIO_should_read(bio)) return SSL_ERROR_WANT_READ; @@ -3889,7 +3928,7 @@ EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) { - if ((s->session != NULL) && (s->session->cipher != NULL)) + if (s->session != NULL) return s->session->cipher; return NULL; } diff --git a/deps/openssl/openssl/ssl/ssl_locl.h b/deps/openssl/openssl/ssl/ssl_locl.h index 25875c9f6d464f..5a7a8591190ed7 100644 --- a/deps/openssl/openssl/ssl/ssl_locl.h +++ b/deps/openssl/openssl/ssl/ssl_locl.h @@ -315,6 +315,13 @@ /* Flag used on OpenSSL ciphersuite ids to indicate they are for SSLv3+ */ # define SSL3_CK_CIPHERSUITE_FLAG 0x03000000 +/* Check if an SSL structure is using QUIC (which uses TLSv1.3) */ +# ifndef OPENSSL_NO_QUIC +# define SSL_IS_QUIC(s) (s->quic_method != NULL) +# else +# define SSL_IS_QUIC(s) 0 +# endif + /* Check if an SSL structure is using DTLS */ # define SSL_IS_DTLS(s) (s->method->ssl3_enc->enc_flags & SSL_ENC_FLAG_DTLS) @@ -715,6 +722,7 @@ typedef enum tlsext_index_en { TLSEXT_IDX_cryptopro_bug, TLSEXT_IDX_early_data, TLSEXT_IDX_certificate_authorities, + TLSEXT_IDX_quic_transport_params, TLSEXT_IDX_padding, TLSEXT_IDX_psk, /* Dummy index - must always be the last entry */ @@ -1064,7 +1072,24 @@ struct ssl_ctx_st { /* Do we advertise Post-handshake auth support? */ int pha_enabled; + +#ifndef OPENSSL_NO_QUIC + const SSL_QUIC_METHOD *quic_method; +#endif +}; + +typedef struct cert_pkey_st CERT_PKEY; + +#ifndef OPENSSL_NO_QUIC +struct quic_data_st { + struct quic_data_st *next; + OSSL_ENCRYPTION_LEVEL level; + size_t offset; + size_t length; }; +typedef struct quic_data_st QUIC_DATA; +int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level); +#endif struct ssl_st { /* @@ -1153,6 +1178,11 @@ struct ssl_st { unsigned char handshake_traffic_hash[EVP_MAX_MD_SIZE]; unsigned char client_app_traffic_secret[EVP_MAX_MD_SIZE]; unsigned char server_app_traffic_secret[EVP_MAX_MD_SIZE]; +# ifndef OPENSSL_NO_QUIC + unsigned char client_hand_traffic_secret[EVP_MAX_MD_SIZE]; + unsigned char server_hand_traffic_secret[EVP_MAX_MD_SIZE]; + unsigned char client_early_traffic_secret[EVP_MAX_MD_SIZE]; +# endif unsigned char exporter_master_secret[EVP_MAX_MD_SIZE]; unsigned char early_exporter_master_secret[EVP_MAX_MD_SIZE]; EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */ @@ -1365,8 +1395,22 @@ struct ssl_st { * selected. */ int tick_identity; + +#ifndef OPENSSL_NO_QUIC + uint8_t *quic_transport_params; + size_t quic_transport_params_len; + uint8_t *peer_quic_transport_params; + size_t peer_quic_transport_params_len; +#endif } ext; +#ifndef OPENSSL_NO_QUIC + OSSL_ENCRYPTION_LEVEL quic_read_level; + OSSL_ENCRYPTION_LEVEL quic_write_level; + QUIC_DATA *quic_input_data_head; + QUIC_DATA *quic_input_data_tail; + const SSL_QUIC_METHOD *quic_method; +#endif /* * Parsed form of the ClientHello, kept around across client_hello_cb * calls. diff --git a/deps/openssl/openssl/ssl/ssl_quic.c b/deps/openssl/openssl/ssl/ssl_quic.c new file mode 100644 index 00000000000000..1651894854aa84 --- /dev/null +++ b/deps/openssl/openssl/ssl/ssl_quic.c @@ -0,0 +1,285 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "ssl_locl.h" +#include "internal/cryptlib.h" +#include "internal/refcount.h" + +#ifdef OPENSSL_NO_QUIC +NON_EMPTY_TRANSLATION_UNIT +#else + +int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, + size_t params_len) +{ + uint8_t *tmp; + + if (params == NULL || params_len == 0) { + tmp = NULL; + params_len = 0; + } else { + tmp = OPENSSL_memdup(params, params_len); + if (tmp == NULL) + return 0; + } + + OPENSSL_free(ssl->ext.quic_transport_params); + ssl->ext.quic_transport_params = tmp; + ssl->ext.quic_transport_params_len = params_len; + return 1; +} + +void SSL_get_peer_quic_transport_params(const SSL *ssl, + const uint8_t **out_params, + size_t *out_params_len) +{ + *out_params = ssl->ext.peer_quic_transport_params; + *out_params_len = ssl->ext.peer_quic_transport_params_len; +} + +size_t SSL_quic_max_handshake_flight_len(const SSL *ssl, OSSL_ENCRYPTION_LEVEL level) +{ + /* + * Limits flights to 16K by default when there are no large + * (certificate-carrying) messages. + */ + static const size_t DEFAULT_FLIGHT_LIMIT = 16384; + + switch (level) { + case ssl_encryption_initial: + return DEFAULT_FLIGHT_LIMIT; + case ssl_encryption_early_data: + /* QUIC does not send EndOfEarlyData. */ + return 0; + case ssl_encryption_handshake: + if (ssl->server) { + /* + * Servers may receive Certificate message if configured to request + * client certificates. + */ + if ((ssl->verify_mode & SSL_VERIFY_PEER) + && ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) + return ssl->max_cert_list; + } else { + /* + * Clients may receive both Certificate message and a CertificateRequest + * message. + */ + if (2*ssl->max_cert_list > DEFAULT_FLIGHT_LIMIT) + return 2 * ssl->max_cert_list; + } + return DEFAULT_FLIGHT_LIMIT; + case ssl_encryption_application: + return DEFAULT_FLIGHT_LIMIT; + } + + return 0; +} + +OSSL_ENCRYPTION_LEVEL SSL_quic_read_level(const SSL *ssl) +{ + return ssl->quic_read_level; +} + +OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl) +{ + return ssl->quic_write_level; +} + +int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len) +{ + size_t l; + + if (!SSL_IS_QUIC(ssl)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + /* Level can be different than the current read, but not less */ + if (level < ssl->quic_read_level + || (ssl->quic_input_data_tail != NULL && level < ssl->quic_input_data_tail->level)) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + return 0; + } + + /* Split the QUIC messages up, if necessary */ + while (len > 0) { + QUIC_DATA *qd; + const uint8_t *p = data + 1; + + /* Check for an incomplete block */ + qd = ssl->quic_input_data_tail; + if (qd != NULL) { + l = qd->length - qd->offset; + if (l != 0) { + /* we still need to copy `l` bytes into the last data block */ + if (l > len) + l = len; + memcpy((char*)(qd+1) + qd->offset, data, l); + qd->offset += l; + len -= l; + data += l; + continue; + } + } + + n2l3(p, l); + l += SSL3_HM_HEADER_LENGTH; + + qd = OPENSSL_zalloc(sizeof(QUIC_DATA) + l); + if (qd == NULL) { + SSLerr(SSL_F_SSL_PROVIDE_QUIC_DATA, SSL_R_INTERNAL_ERROR); + return 0; + } + + qd->next = NULL; + qd->length = l; + qd->level = level; + /* partial data received? */ + if (l > len) + l = len; + qd->offset = l; + + memcpy((void*)(qd + 1), data, l); + if (ssl->quic_input_data_tail != NULL) + ssl->quic_input_data_tail->next = qd; + else + ssl->quic_input_data_head = qd; + ssl->quic_input_data_tail = qd; + + data += l; + len -= l; + } + + return 1; +} + +int SSL_CTX_set_quic_method(SSL_CTX *ctx, const SSL_QUIC_METHOD *quic_method) +{ + switch (ctx->method->version) { + case DTLS1_VERSION: + case DTLS1_2_VERSION: + case DTLS_ANY_VERSION: + case DTLS1_BAD_VER: + return 0; + default: + break; + } + ctx->quic_method = quic_method; + ctx->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; + return 1; +} + +int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method) +{ + switch (ssl->method->version) { + case DTLS1_VERSION: + case DTLS1_2_VERSION: + case DTLS_ANY_VERSION: + case DTLS1_BAD_VER: + return 0; + default: + break; + } + ssl->quic_method = quic_method; + ssl->options &= ~SSL_OP_ENABLE_MIDDLEBOX_COMPAT; + return 1; +} + +int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level) +{ + uint8_t *c2s_secret = NULL; + uint8_t *s2c_secret = NULL; + size_t len; + const EVP_MD *md; + + if (!SSL_IS_QUIC(ssl)) + return 1; + + /* secrets from the POV of the client */ + switch (level) { + case ssl_encryption_early_data: + c2s_secret = ssl->client_early_traffic_secret; + break; + case ssl_encryption_handshake: + c2s_secret = ssl->client_hand_traffic_secret; + s2c_secret = ssl->server_hand_traffic_secret; + break; + case ssl_encryption_application: + c2s_secret = ssl->client_app_traffic_secret; + s2c_secret = ssl->server_app_traffic_secret; + break; + default: + return 1; + } + + md = ssl_handshake_md(ssl); + if (md == NULL) { + /* May not have selected cipher, yet */ + const SSL_CIPHER *c = NULL; + + if (ssl->session != NULL) + c = SSL_SESSION_get0_cipher(ssl->session); + else if (ssl->psksession != NULL) + c = SSL_SESSION_get0_cipher(ssl->psksession); + + if (c != NULL) + md = SSL_CIPHER_get_handshake_digest(c); + } + + if ((len = EVP_MD_size(md)) <= 0) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + + if (ssl->server) { + if (!ssl->quic_method->set_encryption_secrets(ssl, level, c2s_secret, + s2c_secret, len)) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + if (!ssl->quic_method->set_encryption_secrets(ssl, level, s2c_secret, + c2s_secret, len)) { + SSLfatal(ssl, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_SET_ENCRYPTION_SECRETS, + ERR_R_INTERNAL_ERROR); + return 0; + } + } + + return 1; +} + +int SSL_process_quic_post_handshake(SSL *ssl) +{ + int ret; + + if (SSL_in_init(ssl) || !SSL_IS_QUIC(ssl)) { + SSLerr(SSL_F_SSL_PROCESS_QUIC_POST_HANDSHAKE, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + ossl_statem_set_in_init(ssl, 1); + ret = ssl->handshake_func(ssl); + ossl_statem_set_in_init(ssl, 0); + + if (ret <= 0) + return 0; + return 1; +} + +int SSL_is_quic(SSL* ssl) +{ + return SSL_IS_QUIC(ssl); +} + +#endif diff --git a/deps/openssl/openssl/ssl/statem/extensions.c b/deps/openssl/openssl/ssl/statem/extensions.c index 24410991b29996..2368ba78182360 100644 --- a/deps/openssl/openssl/ssl/statem/extensions.c +++ b/deps/openssl/openssl/ssl/statem/extensions.c @@ -56,6 +56,10 @@ static int final_sig_algs(SSL *s, unsigned int context, int sent); static int final_early_data(SSL *s, unsigned int context, int sent); static int final_maxfragmentlen(SSL *s, unsigned int context, int sent); static int init_post_handshake_auth(SSL *s, unsigned int context); +#ifndef OPENSSL_NO_QUIC +static int init_quic_transport_params(SSL *s, unsigned int context); +static int final_quic_transport_params(SSL *s, unsigned int context, int sent); +#endif /* Structure to define a built-in extension */ typedef struct extensions_definition_st { @@ -373,6 +377,19 @@ static const EXTENSION_DEFINITION ext_defs[] = { tls_construct_certificate_authorities, tls_construct_certificate_authorities, NULL, }, +#ifndef OPENSSL_NO_QUIC + { + TLSEXT_TYPE_quic_transport_parameters, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS + | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY, + init_quic_transport_params, + tls_parse_ctos_quic_transport_params, tls_parse_stoc_quic_transport_params, + tls_construct_stoc_quic_transport_params, tls_construct_ctos_quic_transport_params, + final_quic_transport_params, + }, +#else + INVALID_EXTENSION, +#endif { /* Must be immediately before pre_shared_key */ TLSEXT_TYPE_padding, @@ -1701,3 +1718,15 @@ static int init_post_handshake_auth(SSL *s, unsigned int context) return 1; } + +#ifndef OPENSSL_NO_QUIC +static int init_quic_transport_params(SSL *s, unsigned int context) +{ + return 1; +} + +static int final_quic_transport_params(SSL *s, unsigned int context, int sent) +{ + return 1; +} +#endif diff --git a/deps/openssl/openssl/ssl/statem/extensions_clnt.c b/deps/openssl/openssl/ssl/statem/extensions_clnt.c index f0ae642fa09857..a5f86d08b06486 100644 --- a/deps/openssl/openssl/ssl/statem/extensions_clnt.c +++ b/deps/openssl/openssl/ssl/statem/extensions_clnt.c @@ -1214,7 +1214,28 @@ EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, #endif } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_construct_stoc_quic_transport_params() */ +EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + return EXT_RETURN_SENT; +} +#endif /* * Parse the server's renegotiation binding and abort if it's not right */ @@ -1912,6 +1933,18 @@ int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, return 0; } +#ifndef OPENSSL_NO_QUIC + /* + * QUIC server must send 0xFFFFFFFF or it's a PROTOCOL_VIOLATION + * per draft-ietf-quic-tls-24 S4.5 + */ + if (s->quic_method != NULL && max_early_data != 0xFFFFFFFF) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PARSE_STOC_EARLY_DATA, + SSL_R_INVALID_MAX_EARLY_DATA); + return 0; + } +#endif + s->session->ext.max_early_data = max_early_data; return 1; @@ -1999,3 +2032,22 @@ int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, return 1; } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_parse_ctos_quic_transport_params() */ +int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params, + &s->ext.peer_quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} +#endif diff --git a/deps/openssl/openssl/ssl/statem/extensions_srvr.c b/deps/openssl/openssl/ssl/statem/extensions_srvr.c index ab5453f63eccdb..a274e8600019f8 100644 --- a/deps/openssl/openssl/ssl/statem/extensions_srvr.c +++ b/deps/openssl/openssl/ssl/statem/extensions_srvr.c @@ -1298,6 +1298,26 @@ int tls_parse_ctos_post_handshake_auth(SSL *s, PACKET *pkt, unsigned int context return 1; } +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_parse_stoc_quic_transport_params() */ +int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx) +{ + OPENSSL_free(s->ext.peer_quic_transport_params); + s->ext.peer_quic_transport_params = NULL; + s->ext.peer_quic_transport_params_len = 0; + + if (!PACKET_memdup(pkt, + &s->ext.peer_quic_transport_params, + &s->ext.peer_quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_PARSE_CTOS_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return 0; + } + return 1; +} +#endif + /* * Add the server's renegotiation binding */ @@ -1915,12 +1935,20 @@ EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, size_t chainidx) { if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) { - if (s->max_early_data == 0) + uint32_t max_early_data = s->max_early_data; + + if (max_early_data == 0) return EXT_RETURN_NOT_SENT; +#ifndef OPENSSL_NO_QUIC + /* QUIC server must always send 0xFFFFFFFF, per draft-ietf-quic-tls-24 S4.5 */ + if (s->quic_method != NULL) + max_early_data = 0xFFFFFFFF; +#endif + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_early_data) || !WPACKET_start_sub_packet_u16(pkt) - || !WPACKET_put_bytes_u32(pkt, s->max_early_data) + || !WPACKET_put_bytes_u32(pkt, max_early_data) || !WPACKET_close(pkt)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_STOC_EARLY_DATA, ERR_R_INTERNAL_ERROR); @@ -1961,3 +1989,26 @@ EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, return EXT_RETURN_SENT; } + +#ifndef OPENSSL_NO_QUIC +/* SAME AS tls_construct_ctos_quic_transport_params() */ +EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx) +{ + if (s->ext.quic_transport_params == NULL + || s->ext.quic_transport_params_len == 0) { + return EXT_RETURN_NOT_SENT; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_quic_transport_parameters) + || !WPACKET_sub_memcpy_u16(pkt, s->ext.quic_transport_params, + s->ext.quic_transport_params_len)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_STOC_QUIC_TRANSPORT_PARAMS, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} +#endif diff --git a/deps/openssl/openssl/ssl/statem/statem.c b/deps/openssl/openssl/ssl/statem/statem.c index e3c5ec003874bb..f1bc4b362f8d3f 100644 --- a/deps/openssl/openssl/ssl/statem/statem.c +++ b/deps/openssl/openssl/ssl/statem/statem.c @@ -575,6 +575,10 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) * In DTLS we get the whole message in one go - header and body */ ret = dtls_get_message(s, &mt, &len); +#ifndef OPENSSL_NO_QUIC + } else if (SSL_IS_QUIC(s)) { + ret = quic_get_message(s, &mt, &len); +#endif } else { ret = tls_get_message_header(s, &mt); } @@ -604,8 +608,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) return SUB_STATE_ERROR; } - /* dtls_get_message already did this */ - if (!SSL_IS_DTLS(s) + /* dtls_get_message/quic_get_message already did this */ + if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s) && s->s3->tmp.message_size > 0 && !grow_init_buf(s, s->s3->tmp.message_size + SSL3_HM_HEADER_LENGTH)) { @@ -618,8 +622,8 @@ static SUB_STATE_RETURN read_state_machine(SSL *s) /* Fall through */ case READ_STATE_BODY: - if (!SSL_IS_DTLS(s)) { - /* We already got this above for DTLS */ + if (!SSL_IS_DTLS(s) && !SSL_IS_QUIC(s)) { + /* We already got this above for DTLS & QUIC */ ret = tls_get_message_body(s, &len); if (ret == 0) { /* Could be non-blocking IO */ @@ -900,6 +904,15 @@ static SUB_STATE_RETURN write_state_machine(SSL *s) int statem_flush(SSL *s) { s->rwstate = SSL_WRITING; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) { + if (!s->quic_method->flush_flight(s)) { + /* NOTE: BIO_flush() does not generate an error */ + SSLerr(SSL_F_STATEM_FLUSH, ERR_R_INTERNAL_ERROR); + return 0; + } + } else +#endif if (BIO_flush(s->wbio) <= 0) { return 0; } diff --git a/deps/openssl/openssl/ssl/statem/statem_clnt.c b/deps/openssl/openssl/ssl/statem/statem_clnt.c index 6410414fb64a66..3e210867c8a6f7 100644 --- a/deps/openssl/openssl/ssl/statem/statem_clnt.c +++ b/deps/openssl/openssl/ssl/statem/statem_clnt.c @@ -909,6 +909,14 @@ int ossl_statem_client_construct_message(SSL *s, WPACKET *pkt, break; case TLS_ST_CW_END_OF_EARLY_DATA: +#ifndef OPENSSL_NO_QUIC + /* QUIC does not send EndOfEarlyData, draft-ietf-quic-tls-24 S8.3 */ + if (s->quic_method != NULL) { + *confunc = NULL; + *mt = SSL3_MT_DUMMY; + break; + } +#endif *confunc = tls_construct_end_of_early_data; *mt = SSL3_MT_END_OF_EARLY_DATA; break; diff --git a/deps/openssl/openssl/ssl/statem/statem_lib.c b/deps/openssl/openssl/ssl/statem/statem_lib.c index 22e9f0490e2d94..ccaa5353630f96 100644 --- a/deps/openssl/openssl/ssl/statem/statem_lib.c +++ b/deps/openssl/openssl/ssl/statem/statem_lib.c @@ -42,9 +42,23 @@ int ssl3_do_write(SSL *s, int type) { int ret; size_t written = 0; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s) && type == SSL3_RT_HANDSHAKE) { + ret = s->quic_method->add_handshake_data(s, s->quic_write_level, + (const uint8_t*)&s->init_buf->data[s->init_off], + s->init_num); + if (!ret) { + ret = -1; + /* QUIC can't sent anything out sice the above failed */ + SSLerr(SSL_F_SSL3_DO_WRITE, SSL_R_INTERNAL_ERROR); + } else { + written = s->init_num; + } + } else +#endif + ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], + s->init_num, &written); - ret = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], - s->init_num, &written); if (ret < 0) return -1; if (type == SSL3_RT_HANDSHAKE) @@ -1144,6 +1158,7 @@ int tls_get_message_header(SSL *s, int *mt) do { while (s->init_num < SSL3_HM_HEADER_LENGTH) { + /* QUIC: either create a special ssl_read_bytes... or if/else this */ i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &recvd_type, &p[s->init_num], SSL3_HM_HEADER_LENGTH - s->init_num, diff --git a/deps/openssl/openssl/ssl/statem/statem_locl.h b/deps/openssl/openssl/ssl/statem/statem_locl.h index e27c0c13a2bbd5..1551dac9527851 100644 --- a/deps/openssl/openssl/ssl/statem/statem_locl.h +++ b/deps/openssl/openssl/ssl/statem/statem_locl.h @@ -93,6 +93,7 @@ WORK_STATE ossl_statem_server_post_process_message(SSL *s, WORK_STATE wst); __owur int tls_get_message_header(SSL *s, int *mt); __owur int tls_get_message_body(SSL *s, size_t *len); __owur int dtls_get_message(SSL *s, int *mt, size_t *len); +__owur int quic_get_message(SSL *s, int *mt, size_t *len); /* Message construction and processing functions */ __owur int tls_process_initial_server_flight(SSL *s); @@ -236,6 +237,10 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_ctos_post_handshake_auth(SSL *, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +int tls_parse_ctos_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); +#endif EXT_RETURN tls_construct_stoc_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, @@ -298,6 +303,11 @@ EXT_RETURN tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, size_t chainidx); EXT_RETURN tls_construct_stoc_psk(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_stoc_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); +#endif /* Client Extension processing */ EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int context, @@ -368,6 +378,11 @@ EXT_RETURN tls_construct_ctos_psk(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); EXT_RETURN tls_construct_ctos_post_handshake_auth(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +EXT_RETURN tls_construct_ctos_quic_transport_params(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx); +#endif int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); @@ -413,6 +428,10 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); int tls_parse_stoc_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx); +#ifndef OPENSSL_NO_QUIC +int tls_parse_stoc_quic_transport_params(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx); +#endif int tls_handle_alpn(SSL *s); diff --git a/deps/openssl/openssl/ssl/statem/statem_quic.c b/deps/openssl/openssl/ssl/statem/statem_quic.c new file mode 100644 index 00000000000000..771ccd4df42f56 --- /dev/null +++ b/deps/openssl/openssl/ssl/statem/statem_quic.c @@ -0,0 +1,109 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "../ssl_locl.h" +#include "statem_locl.h" +#include "internal/cryptlib.h" + +#ifdef OPENSSL_NO_QUIC +NON_EMPTY_TRANSLATION_UNIT +#else + +int quic_get_message(SSL *s, int *mt, size_t *len) +{ + size_t l; + QUIC_DATA *qd = s->quic_input_data_head; + uint8_t *p; + + if (qd == NULL || (qd->length - qd->offset) != 0) { + s->rwstate = SSL_READING; + *len = 0; + return 0; + } + + /* This is where we check for the proper level, not when data is given */ + if (qd->level != s->quic_read_level) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, + SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED); + *len = 0; + return 0; + } + + if (!BUF_MEM_grow_clean(s->init_buf, (int)qd->length)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_GET_MESSAGE, + ERR_R_BUF_LIB); + *len = 0; + return 0; + } + + /* Copy buffered data */ + memcpy(s->init_buf->data, (void*)(qd + 1), qd->length); + s->init_buf->length = qd->length; + s->quic_input_data_head = qd->next; + if (s->quic_input_data_head == NULL) + s->quic_input_data_tail = NULL; + OPENSSL_free(qd); + + s->s3->tmp.message_type = *mt = *(s->init_buf->data); + p = (uint8_t*)s->init_buf->data + 1; + n2l3(p, l); + s->init_num = s->s3->tmp.message_size = *len = l; + s->init_msg = s->init_buf->data + SSL3_HM_HEADER_LENGTH; + + /* No CCS in QUIC/TLSv1.3? */ + if (*mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, + SSL_F_QUIC_GET_MESSAGE, + SSL_R_CCS_RECEIVED_EARLY); + *len = 0; + return 0; + } + + /* + * If receiving Finished, record MAC of prior handshake messages for + * Finished verification. + */ + if (*mt == SSL3_MT_FINISHED && !ssl3_take_mac(s)) { + /* SSLfatal() already called */ + *len = 0; + return 0; + } + + /* + * We defer feeding in the HRR until later. We'll do it as part of + * processing the message + * The TLsv1.3 handshake transcript stops at the ClientFinished + * message. + */ +#define SERVER_HELLO_RANDOM_OFFSET (SSL3_HM_HEADER_LENGTH + 2) + /* KeyUpdate and NewSessionTicket do not need to be added */ + if (!SSL_IS_TLS13(s) || (s->s3->tmp.message_type != SSL3_MT_NEWSESSION_TICKET + && s->s3->tmp.message_type != SSL3_MT_KEY_UPDATE)) { + if (s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO + || s->init_num < SERVER_HELLO_RANDOM_OFFSET + SSL3_RANDOM_SIZE + || memcmp(hrrrandom, + s->init_buf->data + SERVER_HELLO_RANDOM_OFFSET, + SSL3_RANDOM_SIZE) != 0) { + if (!ssl3_finish_mac(s, (unsigned char *)s->init_buf->data, + s->init_num + SSL3_HM_HEADER_LENGTH)) { + /* SSLfatal() already called */ + *len = 0; + return 0; + } + } + } + if (s->msg_callback) + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, + (size_t)s->init_num + SSL3_HM_HEADER_LENGTH, s, + s->msg_callback_arg); + + return 1; +} + +#endif diff --git a/deps/openssl/openssl/ssl/statem/statem_srvr.c b/deps/openssl/openssl/ssl/statem/statem_srvr.c index 8cf9c40d15c007..0b608695584321 100644 --- a/deps/openssl/openssl/ssl/statem/statem_srvr.c +++ b/deps/openssl/openssl/ssl/statem/statem_srvr.c @@ -57,7 +57,8 @@ static int ossl_statem_server13_read_transition(SSL *s, int mt) return 1; } break; - } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED) { + } else if (s->ext.early_data == SSL_EARLY_DATA_ACCEPTED + && !SSL_IS_QUIC(s)) { if (mt == SSL3_MT_END_OF_EARLY_DATA) { st->hand_state = TLS_ST_SR_END_OF_EARLY_DATA; return 1; diff --git a/deps/openssl/openssl/ssl/tls13_enc.c b/deps/openssl/openssl/ssl/tls13_enc.c index b5f57a02f747f2..1458e1cb79ae0c 100644 --- a/deps/openssl/openssl/ssl/tls13_enc.c +++ b/deps/openssl/openssl/ssl/tls13_enc.c @@ -427,27 +427,140 @@ static int derive_secret_key_and_iv(SSL *s, int sending, const EVP_MD *md, return 0; } -int tls13_change_cipher_state(SSL *s, int which) -{ #ifdef CHARSET_EBCDIC - static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; - static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; - static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; - static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char client_early_traffic[] = {0x63, 0x20, 0x65, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char client_handshake_traffic[] = {0x63, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char client_application_traffic[] = {0x63, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char server_handshake_traffic[] = {0x73, 0x20, 0x68, 0x73, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char server_application_traffic[] = {0x73, 0x20, 0x61, 0x70, 0x20, /*traffic*/0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x00}; +static const unsigned char exporter_master_secret[] = {0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char resumption_master_secret[] = {0x72, 0x65, 0x73, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; +static const unsigned char early_exporter_master_secret[] = {0x65, 0x20, 0x65, 0x78, 0x70, 0x20, /* master*/ 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x00}; #else - static const unsigned char client_early_traffic[] = "c e traffic"; - static const unsigned char client_handshake_traffic[] = "c hs traffic"; - static const unsigned char client_application_traffic[] = "c ap traffic"; - static const unsigned char server_handshake_traffic[] = "s hs traffic"; - static const unsigned char server_application_traffic[] = "s ap traffic"; - static const unsigned char exporter_master_secret[] = "exp master"; - static const unsigned char resumption_master_secret[] = "res master"; - static const unsigned char early_exporter_master_secret[] = "e exp master"; +static const unsigned char client_early_traffic[] = "c e traffic"; +static const unsigned char client_handshake_traffic[] = "c hs traffic"; +static const unsigned char client_application_traffic[] = "c ap traffic"; +static const unsigned char server_handshake_traffic[] = "s hs traffic"; +static const unsigned char server_application_traffic[] = "s ap traffic"; +static const unsigned char exporter_master_secret[] = "exp master"; +static const unsigned char resumption_master_secret[] = "res master"; +static const unsigned char early_exporter_master_secret[] = "e exp master"; #endif +#ifndef OPENSSL_NO_QUIC +static int quic_change_cipher_state(SSL *s, int which) +{ + unsigned char hash[EVP_MAX_MD_SIZE]; + size_t hashlen = 0; + int hashleni; + int ret = 0; + const EVP_MD *md = NULL; + OSSL_ENCRYPTION_LEVEL level = ssl_encryption_initial; + int is_handshake = ((which & SSL3_CC_HANDSHAKE) == SSL3_CC_HANDSHAKE); + int is_client_read = ((which & SSL3_CHANGE_CIPHER_CLIENT_READ) == SSL3_CHANGE_CIPHER_CLIENT_READ); + int is_server_write = ((which & SSL3_CHANGE_CIPHER_SERVER_WRITE) == SSL3_CHANGE_CIPHER_SERVER_WRITE); + int is_early = (which & SSL3_CC_EARLY); + + md = ssl_handshake_md(s); + if (!ssl3_digest_cached_records(s, 1) + || !ssl_handshake_hash(s, hash, sizeof(hash), &hashlen)) { + /* SSLfatal() already called */; + goto err; + } + + /* Ensure cast to size_t is safe */ + hashleni = EVP_MD_size(md); + if (!ossl_assert(hashleni >= 0)) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_QUIC_CHANGE_CIPHER_STATE, + ERR_R_EVP_LIB); + goto err; + } + hashlen = (size_t)hashleni; + + if (is_client_read || is_server_write) { + if (is_handshake) { + level = ssl_encryption_handshake; + + if (!tls13_hkdf_expand(s, md, s->handshake_secret, client_handshake_traffic, + sizeof(client_handshake_traffic)-1, hash, hashlen, + s->client_hand_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_HANDSHAKE_LABEL, s->client_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, s->client_hand_traffic_secret, + s->client_finished_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->handshake_secret, server_handshake_traffic, + sizeof(server_handshake_traffic)-1, hash, hashlen, + s->server_hand_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, SERVER_HANDSHAKE_LABEL, s->server_hand_traffic_secret, hashlen) + || !tls13_derive_finishedkey(s, md, s->server_hand_traffic_secret, + s->server_finished_secret, hashlen)) { + /* SSLfatal() already called */ + goto err; + } + } else { + level = ssl_encryption_application; + + if (!tls13_hkdf_expand(s, md, s->master_secret, client_application_traffic, + sizeof(client_application_traffic)-1, hash, hashlen, + s->client_app_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_APPLICATION_LABEL, s->client_app_traffic_secret, hashlen) + || !tls13_hkdf_expand(s, md, s->master_secret, server_application_traffic, + sizeof(server_application_traffic)-1, hash, hashlen, + s->server_app_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, SERVER_APPLICATION_LABEL, s->server_app_traffic_secret, hashlen)) { + /* SSLfatal() already called */ + goto err; + } + } + if (!quic_set_encryption_secrets(s, level)) { + /* SSLfatal() already called */ + goto err; + } + if (s->server) + s->quic_write_level = level; + else + s->quic_read_level = level; + } else { + /* is_client_write || is_server_read */ + + if (is_early) { + level = ssl_encryption_early_data; + + if (!tls13_hkdf_expand(s, md, s->early_secret, client_early_traffic, + sizeof(client_early_traffic)-1, hash, hashlen, + s->client_early_traffic_secret, hashlen, 1) + || !ssl_log_secret(s, CLIENT_EARLY_LABEL, s->client_early_traffic_secret, hashlen) + || !quic_set_encryption_secrets(s, level)) { + /* SSLfatal() already called */ + goto err; + } + } else if (is_handshake) { + level = ssl_encryption_handshake; + } else { + level = ssl_encryption_application; + /* + * We also create the resumption master secret, but this time use the + * hash for the whole handshake including the Client Finished + */ + if (!tls13_hkdf_expand(s, md, s->master_secret, resumption_master_secret, + sizeof(resumption_master_secret)-1, hash, hashlen, + s->resumption_master_secret, hashlen, 1)) { + /* SSLfatal() already called */ + goto err; + } + } + + if (s->server) + s->quic_read_level = level; + else + s->quic_write_level = level; + } + + ret = 1; + err: + return ret; +} +#endif /* OPENSSL_NO_QUIC */ +int tls13_change_cipher_state(SSL *s, int which) +{ unsigned char *iv; unsigned char secret[EVP_MAX_MD_SIZE]; unsigned char hashval[EVP_MAX_MD_SIZE]; @@ -463,6 +576,11 @@ int tls13_change_cipher_state(SSL *s, int which) const EVP_MD *md = NULL; const EVP_CIPHER *cipher = NULL; +#ifndef OPENSSL_NO_QUIC + if (SSL_IS_QUIC(s)) + return quic_change_cipher_state(s, which); +#endif + if (which & SSL3_CC_READ) { if (s->enc_read_ctx != NULL) { EVP_CIPHER_CTX_reset(s->enc_read_ctx); @@ -707,6 +825,7 @@ int tls13_change_cipher_state(SSL *s, int which) s->statem.enc_write_state = ENC_WRITE_STATE_WRITE_PLAIN_ALERTS; else s->statem.enc_write_state = ENC_WRITE_STATE_VALID; + ret = 1; err: OPENSSL_cleanse(secret, sizeof(secret)); diff --git a/deps/openssl/openssl/test/sslapitest.c b/deps/openssl/openssl/test/sslapitest.c index 7a142268fa764a..6c1214c6b06a78 100644 --- a/deps/openssl/openssl/test/sslapitest.c +++ b/deps/openssl/openssl/test/sslapitest.c @@ -6205,6 +6205,135 @@ static int test_ca_names(int tst) return testresult; } +#ifndef OPENSSL_NO_QUIC + +static int test_quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *read_secret, + const uint8_t *write_secret, size_t secret_len) +{ + test_printf_stderr("quic_set_encryption_secrets() %s, lvl=%d, len=%zd\n", + ssl->server ? "server" : "client", level, secret_len); + return 1; +} +static int test_quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level, + const uint8_t *data, size_t len) +{ + SSL *peer = (SSL*)SSL_get_app_data(ssl); + + test_printf_stderr("quic_add_handshake_data() %s, lvl=%d, *data=0x%02X, len=%zd\n", + ssl->server ? "server" : "client", level, (int)*data, len); + if (!TEST_ptr(peer)) + return 0; + + if (!TEST_true(SSL_provide_quic_data(peer, level, data, len))) { + ERR_print_errors_fp(stderr); + return 0; + } + + return 1; +} +static int test_quic_flush_flight(SSL *ssl) +{ + test_printf_stderr("quic_flush_flight() %s\n", ssl->server ? "server" : "client"); + return 1; +} +static int test_quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, uint8_t alert) +{ + test_printf_stderr("quic_send_alert() %s, lvl=%d, alert=%d\n", + ssl->server ? "server" : "client", level, alert); + return 1; +} + +static SSL_QUIC_METHOD quic_method = { + test_quic_set_encryption_secrets, + test_quic_add_handshake_data, + test_quic_flush_flight, + test_quic_send_alert, +}; +static int test_quic_api(void) +{ + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL; + int testresult = 0; + static const char *server_str = "SERVER"; + static const char *client_str = "CLIENT"; + const uint8_t *peer_str; + size_t peer_str_len; + + /* Clean up logging space */ + memset(client_log_buffer, 0, sizeof(client_log_buffer)); + memset(server_log_buffer, 0, sizeof(server_log_buffer)); + client_log_buffer_index = 0; + server_log_buffer_index = 0; + error_writing_log = 0; + + + if (!TEST_ptr(sctx = SSL_CTX_new(TLS_server_method())) + || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) + || !TEST_ptr(sctx->quic_method) + || !TEST_ptr(serverssl = SSL_new(sctx)) + || !TEST_true(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, NULL)) + || !TEST_false(SSL_IS_QUIC(serverssl)) + || !TEST_true(SSL_set_quic_method(serverssl, &quic_method)) + || !TEST_true(SSL_IS_QUIC(serverssl))) + goto end; + + SSL_CTX_free(sctx); + sctx = NULL; + SSL_free(serverssl); + serverssl = NULL; + + if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), + TLS_client_method(), + TLS1_3_VERSION, 0, + &sctx, &cctx, cert, privkey)) + || !TEST_true(SSL_CTX_set_quic_method(sctx, &quic_method)) + || !TEST_true(SSL_CTX_set_quic_method(cctx, &quic_method)) + || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, + &clientssl, NULL, NULL)) + || !TEST_true(SSL_set_quic_transport_params(serverssl, + (unsigned char*)server_str, + sizeof(server_str))) + || !TEST_true(SSL_set_quic_transport_params(clientssl, + (unsigned char*)client_str, + sizeof(client_str))) + || !TEST_true(SSL_set_app_data(serverssl, clientssl)) + || !TEST_true(SSL_set_app_data(clientssl, serverssl)) + || !TEST_true(create_ssl_connection(serverssl, clientssl, + SSL_ERROR_NONE)) + || !TEST_true(SSL_version(serverssl) == TLS1_3_VERSION) + || !TEST_true(SSL_version(clientssl) == TLS1_3_VERSION) + || !(TEST_int_eq(SSL_quic_read_level(clientssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_read_level(serverssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_write_level(clientssl), ssl_encryption_application)) + || !(TEST_int_eq(SSL_quic_write_level(serverssl), ssl_encryption_application))) + goto end; + + SSL_get_peer_quic_transport_params(serverssl, &peer_str, &peer_str_len); + if (!TEST_mem_eq(peer_str, peer_str_len, client_str, sizeof(client_str))) + goto end; + SSL_get_peer_quic_transport_params(clientssl, &peer_str, &peer_str_len); + if (!TEST_mem_eq(peer_str, peer_str_len, server_str, sizeof(server_str))) + goto end; + + /* Deal with two NewSessionTickets */ + if (!TEST_true(SSL_process_quic_post_handshake(clientssl)) + || !TEST_true(SSL_process_quic_post_handshake(clientssl))) + goto end; + + testresult = 1; + +end: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return testresult; +} +#endif + int setup_tests(void) { if (!TEST_ptr(certsdir = test_get_argument(0)) @@ -6322,6 +6451,9 @@ int setup_tests(void) ADD_ALL_TESTS(test_cert_cb, 6); ADD_ALL_TESTS(test_client_cert_cb, 2); ADD_ALL_TESTS(test_ca_names, 3); +#ifndef OPENSSL_NO_QUIC + ADD_TEST(test_quic_api); +#endif return 1; } diff --git a/deps/openssl/openssl/test/ssltestlib.c b/deps/openssl/openssl/test/ssltestlib.c index 456afdf4716e07..5f61e6339020bd 100644 --- a/deps/openssl/openssl/test/ssltestlib.c +++ b/deps/openssl/openssl/test/ssltestlib.c @@ -917,6 +917,11 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want) if (!create_bare_ssl_connection(serverssl, clientssl, want, 1)) return 0; +#ifndef OPENSSL_NO_QUIC + /* QUIC does not support SSL_read_ex */ + if (SSL_is_quic(clientssl)) + return 1; +#endif /* * We attempt to read some data on the client side which we expect to fail. * This will ensure we have received the NewSessionTicket in TLSv1.3 where diff --git a/deps/openssl/openssl/util/libssl.num b/deps/openssl/openssl/util/libssl.num index 297522c36391f3..15785fe10d0181 100644 --- a/deps/openssl/openssl/util/libssl.num +++ b/deps/openssl/openssl/util/libssl.num @@ -498,3 +498,14 @@ SSL_CTX_get_recv_max_early_data 498 1_1_1 EXIST::FUNCTION: SSL_CTX_set_recv_max_early_data 499 1_1_1 EXIST::FUNCTION: SSL_CTX_set_post_handshake_auth 500 1_1_1 EXIST::FUNCTION: SSL_get_signature_type_nid 501 1_1_1a EXIST::FUNCTION: +SSL_quic_read_level 10094 1_1_1d EXIST::FUNCTION:QUIC +SSL_set_quic_transport_params 10095 1_1_1d EXIST::FUNCTION:QUIC +SSL_CIPHER_get_prf_nid 10096 1_1_1d EXIST::FUNCTION: +SSL_is_quic 10097 1_1_1d EXIST::FUNCTION:QUIC +SSL_get_peer_quic_transport_params 10098 1_1_1d EXIST::FUNCTION:QUIC +SSL_quic_write_level 10099 1_1_1d EXIST::FUNCTION:QUIC +SSL_CTX_set_quic_method 10100 1_1_1d EXIST::FUNCTION:QUIC +SSL_set_quic_method 10101 1_1_1d EXIST::FUNCTION:QUIC +SSL_quic_max_handshake_flight_len 10102 1_1_1d EXIST::FUNCTION:QUIC +SSL_process_quic_post_handshake 10103 1_1_1d EXIST::FUNCTION:QUIC +SSL_provide_quic_data 10104 1_1_1d EXIST::FUNCTION:QUIC diff --git a/deps/openssl/openssl/util/private.num b/deps/openssl/openssl/util/private.num index a6ef44e4a6eb3d..51fb81c0df2839 100644 --- a/deps/openssl/openssl/util/private.num +++ b/deps/openssl/openssl/util/private.num @@ -87,6 +87,8 @@ custom_ext_free_cb datatype custom_ext_parse_cb datatype pem_password_cb datatype ssl_ct_validation_cb datatype +OSSL_ENCRYPTION_LEVEL datatype +SSL_QUIC_METHOD datatype # BIO_append_filename define BIO_destroy_bio_pair define From afbc851d1d3a219e949d6517c000d860da64202e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 19 Feb 2020 16:16:36 -0800 Subject: [PATCH 2/6] deps: update openssl config PR-URL: https://github.com/nodejs/quic/pull/350 Reviewed-By: Anna Henningsen --- .../config/archs/BSD-x86/asm/configdata.pm | 26 +- .../archs/BSD-x86/asm/crypto/bf/bf-586.s | 78 ++-- .../archs/BSD-x86/asm/crypto/bn/bn-586.s | 242 +++++----- .../archs/BSD-x86/asm/crypto/bn/co-586.s | 432 +++++++++--------- .../archs/BSD-x86/asm/crypto/buildinf.h | 2 +- .../archs/BSD-x86/asm/crypto/des/crypt586.s | 36 +- .../archs/BSD-x86/asm/crypto/des/des-586.s | 104 ++--- .../archs/BSD-x86/asm/crypto/md5/md5-586.s | 136 +++--- .../archs/BSD-x86/asm/crypto/ripemd/rmd-586.s | 320 ++++++------- .../archs/BSD-x86/asm/crypto/sha/sha1-586.s | 160 +++---- .../config/archs/BSD-x86/asm/include/progs.h | 2 +- .../config/archs/BSD-x86/asm/openssl.gypi | 2 + .../archs/BSD-x86/asm_avx2/configdata.pm | 26 +- .../archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s | 78 ++-- .../archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s | 242 +++++----- .../archs/BSD-x86/asm_avx2/crypto/bn/co-586.s | 432 +++++++++--------- .../archs/BSD-x86/asm_avx2/crypto/buildinf.h | 2 +- .../BSD-x86/asm_avx2/crypto/des/crypt586.s | 36 +- .../BSD-x86/asm_avx2/crypto/des/des-586.s | 104 ++--- .../BSD-x86/asm_avx2/crypto/md5/md5-586.s | 136 +++--- .../BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s | 320 ++++++------- .../BSD-x86/asm_avx2/crypto/sha/sha1-586.s | 160 +++---- .../archs/BSD-x86/asm_avx2/include/progs.h | 2 +- .../archs/BSD-x86/asm_avx2/openssl.gypi | 2 + .../config/archs/BSD-x86/no-asm/configdata.pm | 26 +- .../archs/BSD-x86/no-asm/crypto/buildinf.h | 2 +- .../archs/BSD-x86/no-asm/include/progs.h | 2 +- .../config/archs/BSD-x86/no-asm/openssl.gypi | 2 + .../config/archs/BSD-x86_64/asm/configdata.pm | 25 +- .../archs/BSD-x86_64/asm/crypto/buildinf.h | 2 +- .../archs/BSD-x86_64/asm/include/progs.h | 2 +- .../config/archs/BSD-x86_64/asm/openssl.gypi | 2 + .../archs/BSD-x86_64/asm_avx2/configdata.pm | 25 +- .../BSD-x86_64/asm_avx2/crypto/buildinf.h | 2 +- .../archs/BSD-x86_64/asm_avx2/include/progs.h | 2 +- .../archs/BSD-x86_64/asm_avx2/openssl.gypi | 2 + .../archs/BSD-x86_64/no-asm/configdata.pm | 25 +- .../archs/BSD-x86_64/no-asm/crypto/buildinf.h | 2 +- .../archs/BSD-x86_64/no-asm/include/progs.h | 2 +- .../archs/BSD-x86_64/no-asm/openssl.gypi | 2 + .../config/archs/VC-WIN32/asm/configdata.pm | 27 +- .../archs/VC-WIN32/asm/crypto/buildinf.h | 2 +- .../config/archs/VC-WIN32/asm/include/progs.h | 2 +- .../config/archs/VC-WIN32/asm/openssl.gypi | 2 + .../archs/VC-WIN32/asm_avx2/configdata.pm | 27 +- .../archs/VC-WIN32/asm_avx2/crypto/buildinf.h | 2 +- .../archs/VC-WIN32/asm_avx2/include/progs.h | 2 +- .../archs/VC-WIN32/asm_avx2/openssl.gypi | 2 + .../archs/VC-WIN32/no-asm/configdata.pm | 27 +- .../archs/VC-WIN32/no-asm/crypto/buildinf.h | 2 +- .../archs/VC-WIN32/no-asm/include/progs.h | 2 +- .../config/archs/VC-WIN32/no-asm/openssl.gypi | 2 + .../archs/VC-WIN64-ARM/no-asm/configdata.pm | 27 +- .../VC-WIN64-ARM/no-asm/crypto/buildinf.h | 2 +- .../archs/VC-WIN64-ARM/no-asm/include/progs.h | 2 +- .../archs/VC-WIN64-ARM/no-asm/openssl.gypi | 2 + .../config/archs/VC-WIN64A/asm/configdata.pm | 27 +- .../archs/VC-WIN64A/asm/crypto/buildinf.h | 2 +- .../archs/VC-WIN64A/asm/include/progs.h | 2 +- .../config/archs/VC-WIN64A/asm/openssl.gypi | 2 + .../archs/VC-WIN64A/asm_avx2/configdata.pm | 27 +- .../VC-WIN64A/asm_avx2/crypto/buildinf.h | 2 +- .../archs/VC-WIN64A/asm_avx2/include/progs.h | 2 +- .../archs/VC-WIN64A/asm_avx2/openssl.gypi | 2 + .../archs/VC-WIN64A/no-asm/configdata.pm | 27 +- .../archs/VC-WIN64A/no-asm/crypto/buildinf.h | 2 +- .../archs/VC-WIN64A/no-asm/include/progs.h | 2 +- .../archs/VC-WIN64A/no-asm/openssl.gypi | 2 + .../config/archs/aix-gcc/asm/configdata.pm | 25 +- .../archs/aix-gcc/asm/crypto/buildinf.h | 2 +- .../config/archs/aix-gcc/asm/include/progs.h | 2 +- .../config/archs/aix-gcc/asm/openssl.gypi | 2 + .../archs/aix-gcc/asm_avx2/configdata.pm | 25 +- .../archs/aix-gcc/asm_avx2/crypto/buildinf.h | 2 +- .../archs/aix-gcc/asm_avx2/include/progs.h | 2 +- .../archs/aix-gcc/asm_avx2/openssl.gypi | 2 + .../config/archs/aix-gcc/no-asm/configdata.pm | 25 +- .../archs/aix-gcc/no-asm/crypto/buildinf.h | 2 +- .../archs/aix-gcc/no-asm/include/progs.h | 2 +- .../config/archs/aix-gcc/no-asm/openssl.gypi | 2 + .../config/archs/aix64-gcc/asm/configdata.pm | 25 +- .../archs/aix64-gcc/asm/crypto/buildinf.h | 2 +- .../archs/aix64-gcc/asm/include/progs.h | 2 +- .../config/archs/aix64-gcc/asm/openssl.gypi | 2 + .../archs/aix64-gcc/asm_avx2/configdata.pm | 25 +- .../aix64-gcc/asm_avx2/crypto/buildinf.h | 2 +- .../archs/aix64-gcc/asm_avx2/include/progs.h | 2 +- .../archs/aix64-gcc/asm_avx2/openssl.gypi | 2 + .../archs/aix64-gcc/no-asm/configdata.pm | 25 +- .../archs/aix64-gcc/no-asm/crypto/buildinf.h | 2 +- .../archs/aix64-gcc/no-asm/include/progs.h | 2 +- .../archs/aix64-gcc/no-asm/openssl.gypi | 2 + .../archs/darwin-i386-cc/asm/configdata.pm | 25 +- .../darwin-i386-cc/asm/crypto/buildinf.h | 2 +- .../archs/darwin-i386-cc/asm/include/progs.h | 2 +- .../archs/darwin-i386-cc/asm/openssl.gypi | 2 + .../darwin-i386-cc/asm_avx2/configdata.pm | 25 +- .../darwin-i386-cc/asm_avx2/crypto/buildinf.h | 2 +- .../darwin-i386-cc/asm_avx2/include/progs.h | 2 +- .../darwin-i386-cc/asm_avx2/openssl.gypi | 2 + .../archs/darwin-i386-cc/no-asm/configdata.pm | 25 +- .../darwin-i386-cc/no-asm/crypto/buildinf.h | 2 +- .../darwin-i386-cc/no-asm/include/progs.h | 2 +- .../archs/darwin-i386-cc/no-asm/openssl.gypi | 2 + .../darwin64-x86_64-cc/asm/configdata.pm | 25 +- .../darwin64-x86_64-cc/asm/crypto/buildinf.h | 2 +- .../darwin64-x86_64-cc/asm/include/progs.h | 2 +- .../archs/darwin64-x86_64-cc/asm/openssl.gypi | 2 + .../darwin64-x86_64-cc/asm_avx2/configdata.pm | 25 +- .../asm_avx2/crypto/buildinf.h | 2 +- .../asm_avx2/include/progs.h | 2 +- .../darwin64-x86_64-cc/asm_avx2/openssl.gypi | 2 + .../darwin64-x86_64-cc/no-asm/configdata.pm | 25 +- .../no-asm/crypto/buildinf.h | 2 +- .../darwin64-x86_64-cc/no-asm/include/progs.h | 2 +- .../darwin64-x86_64-cc/no-asm/openssl.gypi | 2 + .../archs/linux-aarch64/asm/configdata.pm | 25 +- .../archs/linux-aarch64/asm/crypto/buildinf.h | 2 +- .../archs/linux-aarch64/asm/include/progs.h | 2 +- .../archs/linux-aarch64/asm/openssl.gypi | 2 + .../linux-aarch64/asm_avx2/configdata.pm | 25 +- .../linux-aarch64/asm_avx2/crypto/buildinf.h | 2 +- .../linux-aarch64/asm_avx2/include/progs.h | 2 +- .../archs/linux-aarch64/asm_avx2/openssl.gypi | 2 + .../archs/linux-aarch64/no-asm/configdata.pm | 25 +- .../linux-aarch64/no-asm/crypto/buildinf.h | 2 +- .../linux-aarch64/no-asm/include/progs.h | 2 +- .../archs/linux-aarch64/no-asm/openssl.gypi | 2 + .../archs/linux-armv4/asm/configdata.pm | 25 +- .../archs/linux-armv4/asm/crypto/buildinf.h | 2 +- .../archs/linux-armv4/asm/include/progs.h | 2 +- .../config/archs/linux-armv4/asm/openssl.gypi | 2 + .../archs/linux-armv4/asm_avx2/configdata.pm | 25 +- .../linux-armv4/asm_avx2/crypto/buildinf.h | 2 +- .../linux-armv4/asm_avx2/include/progs.h | 2 +- .../archs/linux-armv4/asm_avx2/openssl.gypi | 2 + .../archs/linux-armv4/no-asm/configdata.pm | 25 +- .../linux-armv4/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-armv4/no-asm/include/progs.h | 2 +- .../archs/linux-armv4/no-asm/openssl.gypi | 2 + .../config/archs/linux-elf/asm/configdata.pm | 25 +- .../archs/linux-elf/asm/crypto/buildinf.h | 2 +- .../archs/linux-elf/asm/include/progs.h | 2 +- .../config/archs/linux-elf/asm/openssl.gypi | 2 + .../archs/linux-elf/asm_avx2/configdata.pm | 25 +- .../linux-elf/asm_avx2/crypto/buildinf.h | 2 +- .../archs/linux-elf/asm_avx2/include/progs.h | 2 +- .../archs/linux-elf/asm_avx2/openssl.gypi | 2 + .../archs/linux-elf/no-asm/configdata.pm | 25 +- .../archs/linux-elf/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-elf/no-asm/include/progs.h | 2 +- .../archs/linux-elf/no-asm/openssl.gypi | 2 + .../config/archs/linux-ppc/asm/configdata.pm | 25 +- .../archs/linux-ppc/asm/crypto/buildinf.h | 2 +- .../archs/linux-ppc/asm/include/progs.h | 2 +- .../config/archs/linux-ppc/asm/openssl.gypi | 2 + .../archs/linux-ppc/asm_avx2/configdata.pm | 25 +- .../linux-ppc/asm_avx2/crypto/buildinf.h | 2 +- .../archs/linux-ppc/asm_avx2/include/progs.h | 2 +- .../archs/linux-ppc/asm_avx2/openssl.gypi | 2 + .../archs/linux-ppc/no-asm/configdata.pm | 25 +- .../archs/linux-ppc/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-ppc/no-asm/include/progs.h | 2 +- .../archs/linux-ppc/no-asm/openssl.gypi | 2 + .../archs/linux-ppc64/asm/configdata.pm | 25 +- .../archs/linux-ppc64/asm/crypto/buildinf.h | 2 +- .../archs/linux-ppc64/asm/include/progs.h | 2 +- .../config/archs/linux-ppc64/asm/openssl.gypi | 2 + .../archs/linux-ppc64/asm_avx2/configdata.pm | 25 +- .../linux-ppc64/asm_avx2/crypto/buildinf.h | 2 +- .../linux-ppc64/asm_avx2/include/progs.h | 2 +- .../archs/linux-ppc64/asm_avx2/openssl.gypi | 2 + .../archs/linux-ppc64/no-asm/configdata.pm | 25 +- .../linux-ppc64/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-ppc64/no-asm/include/progs.h | 2 +- .../archs/linux-ppc64/no-asm/openssl.gypi | 2 + .../archs/linux-ppc64le/asm/configdata.pm | 25 +- .../archs/linux-ppc64le/asm/crypto/buildinf.h | 2 +- .../archs/linux-ppc64le/asm/include/progs.h | 2 +- .../archs/linux-ppc64le/asm/openssl.gypi | 2 + .../linux-ppc64le/asm_avx2/configdata.pm | 25 +- .../linux-ppc64le/asm_avx2/crypto/buildinf.h | 2 +- .../linux-ppc64le/asm_avx2/include/progs.h | 2 +- .../archs/linux-ppc64le/asm_avx2/openssl.gypi | 2 + .../archs/linux-ppc64le/no-asm/configdata.pm | 25 +- .../linux-ppc64le/no-asm/crypto/buildinf.h | 2 +- .../linux-ppc64le/no-asm/include/progs.h | 2 +- .../archs/linux-ppc64le/no-asm/openssl.gypi | 2 + .../config/archs/linux-x32/asm/configdata.pm | 25 +- .../archs/linux-x32/asm/crypto/buildinf.h | 2 +- .../archs/linux-x32/asm/include/progs.h | 2 +- .../config/archs/linux-x32/asm/openssl.gypi | 2 + .../archs/linux-x32/asm_avx2/configdata.pm | 25 +- .../linux-x32/asm_avx2/crypto/buildinf.h | 2 +- .../archs/linux-x32/asm_avx2/include/progs.h | 2 +- .../archs/linux-x32/asm_avx2/openssl.gypi | 2 + .../archs/linux-x32/no-asm/configdata.pm | 25 +- .../archs/linux-x32/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-x32/no-asm/include/progs.h | 2 +- .../archs/linux-x32/no-asm/openssl.gypi | 2 + .../archs/linux-x86_64/asm/configdata.pm | 25 +- .../archs/linux-x86_64/asm/crypto/buildinf.h | 2 +- .../archs/linux-x86_64/asm/include/progs.h | 2 +- .../archs/linux-x86_64/asm/openssl.gypi | 2 + .../archs/linux-x86_64/asm_avx2/configdata.pm | 25 +- .../linux-x86_64/asm_avx2/crypto/buildinf.h | 2 +- .../linux-x86_64/asm_avx2/include/progs.h | 2 +- .../archs/linux-x86_64/asm_avx2/openssl.gypi | 2 + .../archs/linux-x86_64/no-asm/configdata.pm | 25 +- .../linux-x86_64/no-asm/crypto/buildinf.h | 2 +- .../archs/linux-x86_64/no-asm/include/progs.h | 2 +- .../archs/linux-x86_64/no-asm/openssl.gypi | 2 + .../archs/linux32-s390x/asm/configdata.pm | 25 +- .../archs/linux32-s390x/asm/crypto/buildinf.h | 2 +- .../archs/linux32-s390x/asm/include/progs.h | 2 +- .../archs/linux32-s390x/asm/openssl.gypi | 2 + .../linux32-s390x/asm_avx2/configdata.pm | 25 +- .../linux32-s390x/asm_avx2/crypto/buildinf.h | 2 +- .../linux32-s390x/asm_avx2/include/progs.h | 2 +- .../archs/linux32-s390x/asm_avx2/openssl.gypi | 2 + .../archs/linux32-s390x/no-asm/configdata.pm | 25 +- .../linux32-s390x/no-asm/crypto/buildinf.h | 2 +- .../linux32-s390x/no-asm/include/progs.h | 2 +- .../archs/linux32-s390x/no-asm/openssl.gypi | 2 + .../archs/linux64-mips64/asm/configdata.pm | 26 +- .../linux64-mips64/asm/crypto/buildinf.h | 2 +- .../asm/crypto/sha/sha256-mips.S | 1 + .../asm/crypto/sha/sha512-mips.S | 1 + .../archs/linux64-mips64/asm/include/progs.h | 2 +- .../archs/linux64-mips64/asm/openssl.gypi | 2 + .../linux64-mips64/asm_avx2/configdata.pm | 26 +- .../linux64-mips64/asm_avx2/crypto/buildinf.h | 2 +- .../asm_avx2/crypto/sha/sha256-mips.S | 1 + .../asm_avx2/crypto/sha/sha512-mips.S | 1 + .../linux64-mips64/asm_avx2/include/progs.h | 2 +- .../linux64-mips64/asm_avx2/openssl.gypi | 2 + .../archs/linux64-mips64/no-asm/configdata.pm | 26 +- .../linux64-mips64/no-asm/crypto/buildinf.h | 2 +- .../linux64-mips64/no-asm/include/progs.h | 2 +- .../archs/linux64-mips64/no-asm/openssl.gypi | 2 + .../archs/linux64-s390x/asm/configdata.pm | 25 +- .../archs/linux64-s390x/asm/crypto/buildinf.h | 2 +- .../archs/linux64-s390x/asm/include/progs.h | 2 +- .../archs/linux64-s390x/asm/openssl.gypi | 2 + .../linux64-s390x/asm_avx2/configdata.pm | 25 +- .../linux64-s390x/asm_avx2/crypto/buildinf.h | 2 +- .../linux64-s390x/asm_avx2/include/progs.h | 2 +- .../archs/linux64-s390x/asm_avx2/openssl.gypi | 2 + .../archs/linux64-s390x/no-asm/configdata.pm | 25 +- .../linux64-s390x/no-asm/crypto/buildinf.h | 2 +- .../linux64-s390x/no-asm/include/progs.h | 2 +- .../archs/linux64-s390x/no-asm/openssl.gypi | 2 + .../archs/solaris-x86-gcc/asm/configdata.pm | 25 +- .../solaris-x86-gcc/asm/crypto/buildinf.h | 2 +- .../archs/solaris-x86-gcc/asm/include/progs.h | 2 +- .../archs/solaris-x86-gcc/asm/openssl.gypi | 2 + .../solaris-x86-gcc/asm_avx2/configdata.pm | 25 +- .../asm_avx2/crypto/buildinf.h | 2 +- .../solaris-x86-gcc/asm_avx2/include/progs.h | 2 +- .../solaris-x86-gcc/asm_avx2/openssl.gypi | 2 + .../solaris-x86-gcc/no-asm/configdata.pm | 25 +- .../solaris-x86-gcc/no-asm/crypto/buildinf.h | 2 +- .../solaris-x86-gcc/no-asm/include/progs.h | 2 +- .../archs/solaris-x86-gcc/no-asm/openssl.gypi | 2 + .../solaris64-x86_64-gcc/asm/configdata.pm | 25 +- .../asm/crypto/buildinf.h | 2 +- .../solaris64-x86_64-gcc/asm/include/progs.h | 2 +- .../solaris64-x86_64-gcc/asm/openssl.gypi | 2 + .../asm_avx2/configdata.pm | 25 +- .../asm_avx2/crypto/buildinf.h | 2 +- .../asm_avx2/include/progs.h | 2 +- .../asm_avx2/openssl.gypi | 2 + .../solaris64-x86_64-gcc/no-asm/configdata.pm | 25 +- .../no-asm/crypto/buildinf.h | 2 +- .../no-asm/include/progs.h | 2 +- .../solaris64-x86_64-gcc/no-asm/openssl.gypi | 2 + 276 files changed, 3317 insertions(+), 1707 deletions(-) diff --git a/deps/openssl/config/archs/BSD-x86/asm/configdata.pm b/deps/openssl/config/archs/BSD-x86/asm/configdata.pm index 60838a7aa0d12e..7ecca4ed5792cd 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9170,6 +9173,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9235,6 +9243,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14013,6 +14026,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14026,6 +14040,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14133,6 +14148,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14185,6 +14204,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -16086,3 +16109,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s index db384026d9d2d6..3e834e9b7b05a4 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bf/bf-586.s @@ -11,7 +11,7 @@ L_BF_encrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -19,7 +19,7 @@ L_BF_encrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 0 + # Round 0 movl 4(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -39,7 +39,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 8(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -59,7 +59,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 12(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -79,7 +79,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 16(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -99,7 +99,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 20(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -119,7 +119,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 24(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -139,7 +139,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 28(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -159,7 +159,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 32(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -179,7 +179,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 36(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -199,7 +199,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 40(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -219,7 +219,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 44(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -239,7 +239,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 48(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -259,7 +259,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 52(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -279,7 +279,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 56(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -299,7 +299,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 60(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -319,7 +319,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 64(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -336,7 +336,7 @@ L_BF_encrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (16) enc=1 + # Load parameter 0 (16) enc=1 movl 20(%esp),%eax xorl %ebx,%edi movl 68(%ebp),%edx @@ -360,7 +360,7 @@ L_BF_decrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -368,7 +368,7 @@ L_BF_decrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 16 + # Round 16 movl 64(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -388,7 +388,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 60(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -408,7 +408,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 56(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -428,7 +428,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 52(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -448,7 +448,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 48(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -468,7 +468,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 44(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -488,7 +488,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 40(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -508,7 +508,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 36(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -528,7 +528,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 32(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -548,7 +548,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 28(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -568,7 +568,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 24(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -588,7 +588,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 20(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -608,7 +608,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 16(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -628,7 +628,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 12(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -648,7 +648,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 8(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -668,7 +668,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 4(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -685,7 +685,7 @@ L_BF_decrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (1) enc=0 + # Load parameter 0 (1) enc=0 movl 20(%esp),%eax xorl %ebx,%edi movl (%ebp),%edx @@ -708,7 +708,7 @@ L_BF_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -719,9 +719,9 @@ L_BF_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 3 + # get and push parameter 3 movl 48(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s index 80c8db4d292bc9..c7c0a81c38b98a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/bn-586.s @@ -116,7 +116,7 @@ L001maw_non_sse2: jz L006maw_finish .align 4,0x90 L007maw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -125,7 +125,7 @@ L007maw_loop: adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -134,7 +134,7 @@ L007maw_loop: adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -143,7 +143,7 @@ L007maw_loop: adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -152,7 +152,7 @@ L007maw_loop: adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -161,7 +161,7 @@ L007maw_loop: adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -170,7 +170,7 @@ L007maw_loop: adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -179,7 +179,7 @@ L007maw_loop: adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ebp addl %esi,%eax @@ -199,7 +199,7 @@ L006maw_finish: jnz L008maw_finish2 jmp L009maw_end L008maw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -210,7 +210,7 @@ L008maw_finish2: movl %eax,(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -221,7 +221,7 @@ L008maw_finish2: movl %eax,4(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -232,7 +232,7 @@ L008maw_finish2: movl %eax,8(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -243,7 +243,7 @@ L008maw_finish2: movl %eax,12(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -254,7 +254,7 @@ L008maw_finish2: movl %eax,16(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -265,7 +265,7 @@ L008maw_finish2: movl %eax,20(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -328,56 +328,56 @@ L011mw_non_sse2: andl $4294967288,%ebp jz L013mw_finish L014mw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ecx addl %esi,%eax @@ -396,7 +396,7 @@ L013mw_finish: jnz L015mw_finish2 jmp L016mw_end L015mw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax @@ -405,7 +405,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ecx addl %esi,%eax @@ -414,7 +414,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ecx addl %esi,%eax @@ -423,7 +423,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ecx addl %esi,%eax @@ -432,7 +432,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ecx addl %esi,%eax @@ -441,7 +441,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ecx addl %esi,%eax @@ -450,7 +450,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ecx addl %esi,%eax @@ -503,42 +503,42 @@ L018sqr_non_sse2: andl $4294967288,%ebx jz L020sw_finish L021sw_loop: - # Round 0 + # Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) movl %edx,4(%esi) - # Round 4 + # Round 4 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) movl %edx,12(%esi) - # Round 8 + # Round 8 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) movl %edx,20(%esi) - # Round 12 + # Round 12 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) movl %edx,28(%esi) - # Round 16 + # Round 16 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) movl %edx,36(%esi) - # Round 20 + # Round 20 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) movl %edx,44(%esi) - # Round 24 + # Round 24 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) movl %edx,52(%esi) - # Round 28 + # Round 28 movl 28(%edi),%eax mull %eax movl %eax,56(%esi) @@ -552,49 +552,49 @@ L020sw_finish: movl 28(%esp),%ebx andl $7,%ebx jz L022sw_end - # Tail Round 0 + # Tail Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) decl %ebx movl %edx,4(%esi) jz L022sw_end - # Tail Round 1 + # Tail Round 1 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) decl %ebx movl %edx,12(%esi) jz L022sw_end - # Tail Round 2 + # Tail Round 2 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) decl %ebx movl %edx,20(%esi) jz L022sw_end - # Tail Round 3 + # Tail Round 3 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) decl %ebx movl %edx,28(%esi) jz L022sw_end - # Tail Round 4 + # Tail Round 4 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) decl %ebx movl %edx,36(%esi) jz L022sw_end - # Tail Round 5 + # Tail Round 5 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) decl %ebx movl %edx,44(%esi) jz L022sw_end - # Tail Round 6 + # Tail Round 6 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) @@ -633,7 +633,7 @@ L_bn_add_words_begin: andl $4294967288,%ebp jz L023aw_finish L024aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -642,7 +642,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -651,7 +651,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -660,7 +660,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -669,7 +669,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -678,7 +678,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -687,7 +687,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -696,7 +696,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx addl %eax,%ecx @@ -715,7 +715,7 @@ L023aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L025aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -726,7 +726,7 @@ L023aw_finish: decl %ebp movl %ecx,(%ebx) jz L025aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -737,7 +737,7 @@ L023aw_finish: decl %ebp movl %ecx,4(%ebx) jz L025aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -748,7 +748,7 @@ L023aw_finish: decl %ebp movl %ecx,8(%ebx) jz L025aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -759,7 +759,7 @@ L023aw_finish: decl %ebp movl %ecx,12(%ebx) jz L025aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -770,7 +770,7 @@ L023aw_finish: decl %ebp movl %ecx,16(%ebx) jz L025aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -781,7 +781,7 @@ L023aw_finish: decl %ebp movl %ecx,20(%ebx) jz L025aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -814,7 +814,7 @@ L_bn_sub_words_begin: andl $4294967288,%ebp jz L026aw_finish L027aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -823,7 +823,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -832,7 +832,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -841,7 +841,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -850,7 +850,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -859,7 +859,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -868,7 +868,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -877,7 +877,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -896,7 +896,7 @@ L026aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L028aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -907,7 +907,7 @@ L026aw_finish: decl %ebp movl %ecx,(%ebx) jz L028aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -918,7 +918,7 @@ L026aw_finish: decl %ebp movl %ecx,4(%ebx) jz L028aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -929,7 +929,7 @@ L026aw_finish: decl %ebp movl %ecx,8(%ebx) jz L028aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -940,7 +940,7 @@ L026aw_finish: decl %ebp movl %ecx,12(%ebx) jz L028aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -951,7 +951,7 @@ L026aw_finish: decl %ebp movl %ecx,16(%ebx) jz L028aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -962,7 +962,7 @@ L026aw_finish: decl %ebp movl %ecx,20(%ebx) jz L028aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -995,7 +995,7 @@ L_bn_sub_part_words_begin: andl $4294967288,%ebp jz L029aw_finish L030aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1004,7 +1004,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1013,7 +1013,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1022,7 +1022,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1031,7 +1031,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1040,7 +1040,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1049,7 +1049,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1058,7 +1058,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1077,7 +1077,7 @@ L029aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L031aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1091,7 +1091,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 1 + # Tail Round 1 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1105,7 +1105,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 2 + # Tail Round 2 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1119,7 +1119,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 3 + # Tail Round 3 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1133,7 +1133,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 4 + # Tail Round 4 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1147,7 +1147,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 5 + # Tail Round 5 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1161,7 +1161,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 6 + # Tail Round 6 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1180,14 +1180,14 @@ L031aw_end: cmpl $0,%ebp je L032pw_end jge L033pw_pos - # pw_neg + # pw_neg movl $0,%edx subl %ebp,%edx movl %edx,%ebp andl $4294967288,%ebp jz L034pw_neg_finish L035pw_neg_loop: - # dl<0 Round 0 + # dl<0 Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1196,7 +1196,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # dl<0 Round 1 + # dl<0 Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1205,7 +1205,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # dl<0 Round 2 + # dl<0 Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1214,7 +1214,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # dl<0 Round 3 + # dl<0 Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1223,7 +1223,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # dl<0 Round 4 + # dl<0 Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1232,7 +1232,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # dl<0 Round 5 + # dl<0 Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1241,7 +1241,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # dl<0 Round 6 + # dl<0 Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1250,7 +1250,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # dl<0 Round 7 + # dl<0 Round 7 movl $0,%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1270,7 +1270,7 @@ L034pw_neg_finish: subl %edx,%ebp andl $7,%ebp jz L032pw_end - # dl<0 Tail Round 0 + # dl<0 Tail Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1281,7 +1281,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,(%ebx) jz L032pw_end - # dl<0 Tail Round 1 + # dl<0 Tail Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1292,7 +1292,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,4(%ebx) jz L032pw_end - # dl<0 Tail Round 2 + # dl<0 Tail Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1303,7 +1303,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,8(%ebx) jz L032pw_end - # dl<0 Tail Round 3 + # dl<0 Tail Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1314,7 +1314,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,12(%ebx) jz L032pw_end - # dl<0 Tail Round 4 + # dl<0 Tail Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1325,7 +1325,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,16(%ebx) jz L032pw_end - # dl<0 Tail Round 5 + # dl<0 Tail Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1336,7 +1336,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,20(%ebx) jz L032pw_end - # dl<0 Tail Round 6 + # dl<0 Tail Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1350,42 +1350,42 @@ L033pw_pos: andl $4294967288,%ebp jz L036pw_pos_finish L037pw_pos_loop: - # dl>0 Round 0 + # dl>0 Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L038pw_nc0 - # dl>0 Round 1 + # dl>0 Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L039pw_nc1 - # dl>0 Round 2 + # dl>0 Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L040pw_nc2 - # dl>0 Round 3 + # dl>0 Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L041pw_nc3 - # dl>0 Round 4 + # dl>0 Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L042pw_nc4 - # dl>0 Round 5 + # dl>0 Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L043pw_nc5 - # dl>0 Round 6 + # dl>0 Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) jnc L044pw_nc6 - # dl>0 Round 7 + # dl>0 Round 7 movl 28(%esi),%ecx subl %eax,%ecx movl %ecx,28(%ebx) @@ -1399,49 +1399,49 @@ L036pw_pos_finish: movl 36(%esp),%ebp andl $7,%ebp jz L032pw_end - # dl>0 Tail Round 0 + # dl>0 Tail Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L046pw_tail_nc0 decl %ebp jz L032pw_end - # dl>0 Tail Round 1 + # dl>0 Tail Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L047pw_tail_nc1 decl %ebp jz L032pw_end - # dl>0 Tail Round 2 + # dl>0 Tail Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L048pw_tail_nc2 decl %ebp jz L032pw_end - # dl>0 Tail Round 3 + # dl>0 Tail Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L049pw_tail_nc3 decl %ebp jz L032pw_end - # dl>0 Tail Round 4 + # dl>0 Tail Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L050pw_tail_nc4 decl %ebp jz L032pw_end - # dl>0 Tail Round 5 + # dl>0 Tail Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L051pw_tail_nc5 decl %ebp jz L032pw_end - # dl>0 Tail Round 6 + # dl>0 Tail Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s index 0196f42b789164..d2608b28564f5a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/bn/co-586.s @@ -14,9 +14,9 @@ L_bn_mul_comba8_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -25,17 +25,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -44,24 +44,24 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -70,31 +70,31 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -103,38 +103,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 16(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[4]*b[0] + # mul a[4]*b[0] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[0]*b[4] + # mul a[0]*b[4] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -143,45 +143,45 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 20(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[5]*b[0] + # mul a[5]*b[0] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[4]*b[1] + # mul a[4]*b[1] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[1]*b[4] + # mul a[1]*b[4] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[0]*b[5] + # mul a[0]*b[5] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -190,52 +190,52 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 24(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[6]*b[0] + # mul a[6]*b[0] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[5]*b[1] + # mul a[5]*b[1] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[4]*b[2] + # mul a[4]*b[2] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[2]*b[4] + # mul a[2]*b[4] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[1]*b[5] + # mul a[1]*b[5] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[0]*b[6] + # mul a[0]*b[6] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -244,59 +244,59 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,24(%eax) movl 28(%esi),%eax - # saved r[6] - # ################## Calculate word 7 + # saved r[6] + # ################## Calculate word 7 xorl %ebx,%ebx - # mul a[7]*b[0] + # mul a[7]*b[0] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[6]*b[1] + # mul a[6]*b[1] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[5]*b[2] + # mul a[5]*b[2] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[4]*b[3] + # mul a[4]*b[3] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[3]*b[4] + # mul a[3]*b[4] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[2]*b[5] + # mul a[2]*b[5] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[1]*b[6] + # mul a[1]*b[6] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[0]*b[7] + # mul a[0]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -305,52 +305,52 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,28(%eax) movl 28(%esi),%eax - # saved r[7] - # ################## Calculate word 8 + # saved r[7] + # ################## Calculate word 8 xorl %ecx,%ecx - # mul a[7]*b[1] + # mul a[7]*b[1] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[6]*b[2] + # mul a[6]*b[2] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[5]*b[3] + # mul a[5]*b[3] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[4]*b[4] + # mul a[4]*b[4] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[3]*b[5] + # mul a[3]*b[5] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[2]*b[6] + # mul a[2]*b[6] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[1]*b[7] + # mul a[1]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -359,45 +359,45 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,32(%eax) movl 28(%esi),%eax - # saved r[8] - # ################## Calculate word 9 + # saved r[8] + # ################## Calculate word 9 xorl %ebp,%ebp - # mul a[7]*b[2] + # mul a[7]*b[2] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[6]*b[3] + # mul a[6]*b[3] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[5]*b[4] + # mul a[5]*b[4] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[4]*b[5] + # mul a[4]*b[5] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[3]*b[6] + # mul a[3]*b[6] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[2]*b[7] + # mul a[2]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -406,38 +406,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,36(%eax) movl 28(%esi),%eax - # saved r[9] - # ################## Calculate word 10 + # saved r[9] + # ################## Calculate word 10 xorl %ebx,%ebx - # mul a[7]*b[3] + # mul a[7]*b[3] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[6]*b[4] + # mul a[6]*b[4] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[5]*b[5] + # mul a[5]*b[5] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[4]*b[6] + # mul a[4]*b[6] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[3]*b[7] + # mul a[3]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -446,31 +446,31 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,40(%eax) movl 28(%esi),%eax - # saved r[10] - # ################## Calculate word 11 + # saved r[10] + # ################## Calculate word 11 xorl %ecx,%ecx - # mul a[7]*b[4] + # mul a[7]*b[4] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[6]*b[5] + # mul a[6]*b[5] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[5]*b[6] + # mul a[5]*b[6] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[4]*b[7] + # mul a[4]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -479,24 +479,24 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,44(%eax) movl 28(%esi),%eax - # saved r[11] - # ################## Calculate word 12 + # saved r[11] + # ################## Calculate word 12 xorl %ebp,%ebp - # mul a[7]*b[5] + # mul a[7]*b[5] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[6]*b[6] + # mul a[6]*b[6] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[5]*b[7] + # mul a[5]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -505,17 +505,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,48(%eax) movl 28(%esi),%eax - # saved r[12] - # ################## Calculate word 13 + # saved r[12] + # ################## Calculate word 13 xorl %ebx,%ebx - # mul a[7]*b[6] + # mul a[7]*b[6] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[6]*b[7] + # mul a[6]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -524,18 +524,18 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,52(%eax) movl 28(%esi),%eax - # saved r[13] - # ################## Calculate word 14 + # saved r[13] + # ################## Calculate word 14 xorl %ecx,%ecx - # mul a[7]*b[7] + # mul a[7]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%eax) - # saved r[14] - # save r[15] + # saved r[14] + # save r[15] movl %ebx,60(%eax) popl %ebx popl %ebp @@ -557,9 +557,9 @@ L_bn_mul_comba4_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -568,17 +568,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -587,24 +587,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -613,31 +613,31 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -646,24 +646,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 12(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -672,17 +672,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 12(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -691,18 +691,18 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 12(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%eax) - # saved r[6] - # save r[7] + # saved r[6] + # save r[7] movl %ecx,28(%eax) popl %ebx popl %ebp @@ -723,9 +723,9 @@ L_bn_sqr_comba8_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -733,10 +733,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -747,10 +747,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -759,7 +759,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -767,10 +767,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -780,7 +780,7 @@ L_bn_sqr_comba8_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -791,10 +791,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,12(%edi) movl (%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[4]*a[0] + # sqr a[4]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -804,7 +804,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -813,7 +813,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -821,10 +821,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 20(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[5]*a[0] + # sqr a[5]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -834,7 +834,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ecx movl 4(%esi),%edx - # sqr a[4]*a[1] + # sqr a[4]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -844,7 +844,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -855,10 +855,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,20(%edi) movl (%esi),%edx - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[6]*a[0] + # sqr a[6]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -868,7 +868,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[5]*a[1] + # sqr a[5]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -878,7 +878,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebp movl 8(%esi),%edx - # sqr a[4]*a[2] + # sqr a[4]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -887,7 +887,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 12(%esi),%eax adcl $0,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -895,10 +895,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,24(%edi) movl 28(%esi),%eax - # saved r[6] - # ############### Calculate word 7 + # saved r[6] + # ############### Calculate word 7 xorl %ebx,%ebx - # sqr a[7]*a[0] + # sqr a[7]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -908,7 +908,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[6]*a[1] + # sqr a[6]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -918,7 +918,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebx movl 8(%esi),%edx - # sqr a[5]*a[2] + # sqr a[5]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -928,7 +928,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebx movl 12(%esi),%edx - # sqr a[4]*a[3] + # sqr a[4]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -939,10 +939,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,28(%edi) movl 4(%esi),%edx - # saved r[7] - # ############### Calculate word 8 + # saved r[7] + # ############### Calculate word 8 xorl %ecx,%ecx - # sqr a[7]*a[1] + # sqr a[7]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -952,7 +952,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[6]*a[2] + # sqr a[6]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -962,7 +962,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ecx movl 12(%esi),%edx - # sqr a[5]*a[3] + # sqr a[5]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -971,7 +971,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 16(%esi),%eax adcl $0,%ecx - # sqr a[4]*a[4] + # sqr a[4]*a[4] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -979,10 +979,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,32(%edi) movl 28(%esi),%eax - # saved r[8] - # ############### Calculate word 9 + # saved r[8] + # ############### Calculate word 9 xorl %ebp,%ebp - # sqr a[7]*a[2] + # sqr a[7]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -992,7 +992,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebp movl 12(%esi),%edx - # sqr a[6]*a[3] + # sqr a[6]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1002,7 +1002,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 16(%esi),%edx - # sqr a[5]*a[4] + # sqr a[5]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1013,10 +1013,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,36(%edi) movl 12(%esi),%edx - # saved r[9] - # ############### Calculate word 10 + # saved r[9] + # ############### Calculate word 10 xorl %ebx,%ebx - # sqr a[7]*a[3] + # sqr a[7]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1026,7 +1026,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 16(%esi),%edx - # sqr a[6]*a[4] + # sqr a[6]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1035,7 +1035,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 20(%esi),%eax adcl $0,%ebx - # sqr a[5]*a[5] + # sqr a[5]*a[5] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1043,10 +1043,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,40(%edi) movl 28(%esi),%eax - # saved r[10] - # ############### Calculate word 11 + # saved r[10] + # ############### Calculate word 11 xorl %ecx,%ecx - # sqr a[7]*a[4] + # sqr a[7]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1056,7 +1056,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 20(%esi),%edx - # sqr a[6]*a[5] + # sqr a[6]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1067,10 +1067,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,44(%edi) movl 20(%esi),%edx - # saved r[11] - # ############### Calculate word 12 + # saved r[11] + # ############### Calculate word 12 xorl %ebp,%ebp - # sqr a[7]*a[5] + # sqr a[7]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1079,7 +1079,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 24(%esi),%eax adcl $0,%ebp - # sqr a[6]*a[6] + # sqr a[6]*a[6] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1087,10 +1087,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,48(%edi) movl 28(%esi),%eax - # saved r[12] - # ############### Calculate word 13 + # saved r[12] + # ############### Calculate word 13 xorl %ebx,%ebx - # sqr a[7]*a[6] + # sqr a[7]*a[6] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1100,16 +1100,16 @@ L_bn_sqr_comba8_begin: movl 28(%esi),%eax adcl $0,%ebx movl %ecx,52(%edi) - # saved r[13] - # ############### Calculate word 14 + # saved r[13] + # ############### Calculate word 14 xorl %ecx,%ecx - # sqr a[7]*a[7] + # sqr a[7]*a[7] mull %eax addl %eax,%ebp adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%edi) - # saved r[14] + # saved r[14] movl %ebx,60(%edi) popl %ebx popl %ebp @@ -1130,9 +1130,9 @@ L_bn_sqr_comba4_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1140,10 +1140,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1154,10 +1154,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1166,7 +1166,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -1174,10 +1174,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1187,7 +1187,7 @@ L_bn_sqr_comba4_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1198,10 +1198,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,12(%edi) movl 4(%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1210,7 +1210,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1218,10 +1218,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 12(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1231,16 +1231,16 @@ L_bn_sqr_comba4_begin: movl 12(%esi),%eax adcl $0,%ecx movl %ebp,20(%edi) - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%edi) - # saved r[6] + # saved r[6] movl %ecx,28(%edi) popl %ebx popl %ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h index feabbb00ba4bc3..500cd68ba0669e 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:16 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:31 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s index e4f05f09e3e588..d2c370231dfa7f 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/crypt586.s @@ -9,7 +9,7 @@ L_fcrypt_body_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words xorl %edi,%edi xorl %esi,%esi call L000PIC_me_up @@ -22,7 +22,7 @@ L000PIC_me_up: pushl $25 L001start: - # Round 0 + # Round 0 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -72,7 +72,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 1 + # Round 1 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -122,7 +122,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 2 + # Round 2 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -172,7 +172,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 3 + # Round 3 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -222,7 +222,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 4 + # Round 4 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -272,7 +272,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 5 + # Round 5 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -322,7 +322,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 6 + # Round 6 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -372,7 +372,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 7 + # Round 7 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -422,7 +422,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 8 + # Round 8 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -472,7 +472,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 9 + # Round 9 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -522,7 +522,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 10 + # Round 10 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -572,7 +572,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 11 + # Round 11 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -622,7 +622,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 12 + # Round 12 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -672,7 +672,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 13 + # Round 13 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -722,7 +722,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 14 + # Round 14 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -772,7 +772,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 15 + # Round 15 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -829,7 +829,7 @@ L001start: movl %ebx,(%esp) jnz L001start - # FP + # FP movl 28(%esp),%edx rorl $1,%edi movl %esi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s index 14d61fda5f9598..5ddd0ed7311ec1 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/des/des-586.s @@ -4,7 +4,7 @@ .align 4 __x86_DES_encrypt: pushl %ecx - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -33,7 +33,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -62,7 +62,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -91,7 +91,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -120,7 +120,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -149,7 +149,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -178,7 +178,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -207,7 +207,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -236,7 +236,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -265,7 +265,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -294,7 +294,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -323,7 +323,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -352,7 +352,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -381,7 +381,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -410,7 +410,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -439,7 +439,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -474,7 +474,7 @@ __x86_DES_encrypt: .align 4 __x86_DES_decrypt: pushl %ecx - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -503,7 +503,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -532,7 +532,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -561,7 +561,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -590,7 +590,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -619,7 +619,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -648,7 +648,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -677,7 +677,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -706,7 +706,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -735,7 +735,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -764,7 +764,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -793,7 +793,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -822,7 +822,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -851,7 +851,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -880,7 +880,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -909,7 +909,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -948,7 +948,7 @@ L_DES_encrypt1_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%esi xorl %ecx,%ecx pushl %ebx @@ -957,7 +957,7 @@ L_DES_encrypt1_begin: movl 28(%esp),%ebx movl 4(%esi),%edi - # IP + # IP roll $4,%eax movl %eax,%esi xorl %edi,%eax @@ -1007,7 +1007,7 @@ L001decrypt: call __x86_DES_decrypt L002done: - # FP + # FP movl 20(%esp),%edx rorl $1,%esi movl %edi,%eax @@ -1060,7 +1060,7 @@ L_DES_encrypt2_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%eax xorl %ecx,%ecx pushl %ebx @@ -1083,7 +1083,7 @@ L004decrypt: call __x86_DES_decrypt L005done: - # Fixup + # Fixup rorl $3,%edi movl 20(%esp),%eax rorl $3,%esi @@ -1105,12 +1105,12 @@ L_DES_encrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1169,7 +1169,7 @@ L_DES_encrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1225,12 +1225,12 @@ L_DES_decrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1289,7 +1289,7 @@ L_DES_decrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1345,7 +1345,7 @@ L_DES_ncbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1356,11 +1356,11 @@ L_DES_ncbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 pushl %ecx - # get and push parameter 3 + # get and push parameter 3 movl 52(%esp),%eax pushl %eax pushl %ebx @@ -1524,7 +1524,7 @@ L_DES_ede3_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 6 + # getting iv ptr from parameter 6 movl 44(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1535,15 +1535,15 @@ L_DES_ede3_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 7 + # getting encrypt flag from parameter 7 movl 64(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 movl 56(%esp),%eax pushl %eax - # get and push parameter 4 + # get and push parameter 4 movl 56(%esp),%eax pushl %eax - # get and push parameter 3 + # get and push parameter 3 movl 56(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s index 91e941d1b41b3a..2f4efe555caebb 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/md5/md5-586.s @@ -21,10 +21,10 @@ L_md5_block_asm_data_order_begin: movl 12(%edi),%edx L000start: - # R0 section + # R0 section movl %ecx,%edi movl (%esi),%ebp - # R0 0 + # R0 0 xorl %edx,%edi andl %ebx,%edi leal 3614090360(%eax,%ebp,1),%eax @@ -34,7 +34,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 1 + # R0 1 xorl %ecx,%edi andl %eax,%edi leal 3905402710(%edx,%ebp,1),%edx @@ -44,7 +44,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 2 + # R0 2 xorl %ebx,%edi andl %edx,%edi leal 606105819(%ecx,%ebp,1),%ecx @@ -54,7 +54,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 3 + # R0 3 xorl %eax,%edi andl %ecx,%edi leal 3250441966(%ebx,%ebp,1),%ebx @@ -64,7 +64,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 4 + # R0 4 xorl %edx,%edi andl %ebx,%edi leal 4118548399(%eax,%ebp,1),%eax @@ -74,7 +74,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 5 + # R0 5 xorl %ecx,%edi andl %eax,%edi leal 1200080426(%edx,%ebp,1),%edx @@ -84,7 +84,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 6 + # R0 6 xorl %ebx,%edi andl %edx,%edi leal 2821735955(%ecx,%ebp,1),%ecx @@ -94,7 +94,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 7 + # R0 7 xorl %eax,%edi andl %ecx,%edi leal 4249261313(%ebx,%ebp,1),%ebx @@ -104,7 +104,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 8 + # R0 8 xorl %edx,%edi andl %ebx,%edi leal 1770035416(%eax,%ebp,1),%eax @@ -114,7 +114,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 9 + # R0 9 xorl %ecx,%edi andl %eax,%edi leal 2336552879(%edx,%ebp,1),%edx @@ -124,7 +124,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 10 + # R0 10 xorl %ebx,%edi andl %edx,%edi leal 4294925233(%ecx,%ebp,1),%ecx @@ -134,7 +134,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 11 + # R0 11 xorl %eax,%edi andl %ecx,%edi leal 2304563134(%ebx,%ebp,1),%ebx @@ -144,7 +144,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 12 + # R0 12 xorl %edx,%edi andl %ebx,%edi leal 1804603682(%eax,%ebp,1),%eax @@ -154,7 +154,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 13 + # R0 13 xorl %ecx,%edi andl %eax,%edi leal 4254626195(%edx,%ebp,1),%edx @@ -164,7 +164,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 14 + # R0 14 xorl %ebx,%edi andl %edx,%edi leal 2792965006(%ecx,%ebp,1),%ecx @@ -174,7 +174,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 15 + # R0 15 xorl %eax,%edi andl %ecx,%edi leal 1236535329(%ebx,%ebp,1),%ebx @@ -185,8 +185,8 @@ L000start: movl %ecx,%edi addl %ecx,%ebx - # R1 section - # R1 16 + # R1 section + # R1 16 xorl %ebx,%edi andl %edx,%edi leal 4129170786(%eax,%ebp,1),%eax @@ -196,7 +196,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 17 + # R1 17 xorl %eax,%edi andl %ecx,%edi leal 3225465664(%edx,%ebp,1),%edx @@ -206,7 +206,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 18 + # R1 18 xorl %edx,%edi andl %ebx,%edi leal 643717713(%ecx,%ebp,1),%ecx @@ -216,7 +216,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 19 + # R1 19 xorl %ecx,%edi andl %eax,%edi leal 3921069994(%ebx,%ebp,1),%ebx @@ -226,7 +226,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 20 + # R1 20 xorl %ebx,%edi andl %edx,%edi leal 3593408605(%eax,%ebp,1),%eax @@ -236,7 +236,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 21 + # R1 21 xorl %eax,%edi andl %ecx,%edi leal 38016083(%edx,%ebp,1),%edx @@ -246,7 +246,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 22 + # R1 22 xorl %edx,%edi andl %ebx,%edi leal 3634488961(%ecx,%ebp,1),%ecx @@ -256,7 +256,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 23 + # R1 23 xorl %ecx,%edi andl %eax,%edi leal 3889429448(%ebx,%ebp,1),%ebx @@ -266,7 +266,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 24 + # R1 24 xorl %ebx,%edi andl %edx,%edi leal 568446438(%eax,%ebp,1),%eax @@ -276,7 +276,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 25 + # R1 25 xorl %eax,%edi andl %ecx,%edi leal 3275163606(%edx,%ebp,1),%edx @@ -286,7 +286,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 26 + # R1 26 xorl %edx,%edi andl %ebx,%edi leal 4107603335(%ecx,%ebp,1),%ecx @@ -296,7 +296,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 27 + # R1 27 xorl %ecx,%edi andl %eax,%edi leal 1163531501(%ebx,%ebp,1),%ebx @@ -306,7 +306,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 28 + # R1 28 xorl %ebx,%edi andl %edx,%edi leal 2850285829(%eax,%ebp,1),%eax @@ -316,7 +316,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 29 + # R1 29 xorl %eax,%edi andl %ecx,%edi leal 4243563512(%edx,%ebp,1),%edx @@ -326,7 +326,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 30 + # R1 30 xorl %edx,%edi andl %ebx,%edi leal 1735328473(%ecx,%ebp,1),%ecx @@ -336,7 +336,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 31 + # R1 31 xorl %ecx,%edi andl %eax,%edi leal 2368359562(%ebx,%ebp,1),%ebx @@ -347,8 +347,8 @@ L000start: roll $20,%ebx addl %ecx,%ebx - # R2 section - # R2 32 + # R2 section + # R2 32 xorl %edx,%edi xorl %ebx,%edi leal 4294588738(%eax,%ebp,1),%eax @@ -356,7 +356,7 @@ L000start: movl 32(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 33 + # R2 33 addl %ebx,%eax xorl %ecx,%edi leal 2272392833(%edx,%ebp,1),%edx @@ -366,7 +366,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 34 + # R2 34 xorl %ebx,%edi xorl %edx,%edi leal 1839030562(%ecx,%ebp,1),%ecx @@ -374,7 +374,7 @@ L000start: movl 56(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 35 + # R2 35 addl %edx,%ecx xorl %eax,%edi leal 4259657740(%ebx,%ebp,1),%ebx @@ -384,7 +384,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 36 + # R2 36 xorl %edx,%edi xorl %ebx,%edi leal 2763975236(%eax,%ebp,1),%eax @@ -392,7 +392,7 @@ L000start: movl 16(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 37 + # R2 37 addl %ebx,%eax xorl %ecx,%edi leal 1272893353(%edx,%ebp,1),%edx @@ -402,7 +402,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 38 + # R2 38 xorl %ebx,%edi xorl %edx,%edi leal 4139469664(%ecx,%ebp,1),%ecx @@ -410,7 +410,7 @@ L000start: movl 40(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 39 + # R2 39 addl %edx,%ecx xorl %eax,%edi leal 3200236656(%ebx,%ebp,1),%ebx @@ -420,7 +420,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 40 + # R2 40 xorl %edx,%edi xorl %ebx,%edi leal 681279174(%eax,%ebp,1),%eax @@ -428,7 +428,7 @@ L000start: movl (%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 41 + # R2 41 addl %ebx,%eax xorl %ecx,%edi leal 3936430074(%edx,%ebp,1),%edx @@ -438,7 +438,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 42 + # R2 42 xorl %ebx,%edi xorl %edx,%edi leal 3572445317(%ecx,%ebp,1),%ecx @@ -446,7 +446,7 @@ L000start: movl 24(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 43 + # R2 43 addl %edx,%ecx xorl %eax,%edi leal 76029189(%ebx,%ebp,1),%ebx @@ -456,7 +456,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 44 + # R2 44 xorl %edx,%edi xorl %ebx,%edi leal 3654602809(%eax,%ebp,1),%eax @@ -464,7 +464,7 @@ L000start: movl 48(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 45 + # R2 45 addl %ebx,%eax xorl %ecx,%edi leal 3873151461(%edx,%ebp,1),%edx @@ -474,7 +474,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 46 + # R2 46 xorl %ebx,%edi xorl %edx,%edi leal 530742520(%ecx,%ebp,1),%ecx @@ -482,7 +482,7 @@ L000start: movl 8(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 47 + # R2 47 addl %edx,%ecx xorl %eax,%edi leal 3299628645(%ebx,%ebp,1),%ebx @@ -493,8 +493,8 @@ L000start: roll $23,%ebx addl %ecx,%ebx - # R3 section - # R3 48 + # R3 section + # R3 48 xorl %edx,%edi orl %ebx,%edi leal 4096336452(%eax,%ebp,1),%eax @@ -505,7 +505,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 49 + # R3 49 orl %eax,%edi leal 1126891415(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -515,7 +515,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 50 + # R3 50 orl %edx,%edi leal 2878612391(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -525,7 +525,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 51 + # R3 51 orl %ecx,%edi leal 4237533241(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -535,7 +535,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 52 + # R3 52 orl %ebx,%edi leal 1700485571(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -545,7 +545,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 53 + # R3 53 orl %eax,%edi leal 2399980690(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -555,7 +555,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 54 + # R3 54 orl %edx,%edi leal 4293915773(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -565,7 +565,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 55 + # R3 55 orl %ecx,%edi leal 2240044497(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -575,7 +575,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 56 + # R3 56 orl %ebx,%edi leal 1873313359(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -585,7 +585,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 57 + # R3 57 orl %eax,%edi leal 4264355552(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -595,7 +595,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 58 + # R3 58 orl %edx,%edi leal 2734768916(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -605,7 +605,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 59 + # R3 59 orl %ecx,%edi leal 1309151649(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -615,7 +615,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 60 + # R3 60 orl %ebx,%edi leal 4149444226(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -625,7 +625,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 61 + # R3 61 orl %eax,%edi leal 3174756917(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -635,7 +635,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 62 + # R3 62 orl %edx,%edi leal 718787259(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -645,7 +645,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 63 + # R3 63 orl %ecx,%edi leal 3951481745(%ebx,%ebp,1),%ebx xorl %edx,%edi diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s index 9484963b97fdc7..17603e38536262 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/ripemd/rmd-586.s @@ -51,7 +51,7 @@ L000start: movl %edi,%eax movl 12(%edx),%ebx movl 16(%edx),%ebp - # 0 + # 0 xorl %ebx,%eax movl (%esp),%edx xorl %esi,%eax @@ -61,7 +61,7 @@ L000start: movl %esi,%eax roll $11,%ecx addl %ebp,%ecx - # 1 + # 1 xorl %edi,%eax movl 4(%esp),%edx xorl %ecx,%eax @@ -72,7 +72,7 @@ L000start: xorl %esi,%eax roll $14,%ebp addl %ebx,%ebp - # 2 + # 2 movl 8(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -81,7 +81,7 @@ L000start: movl %ebp,%eax roll $15,%ebx addl %edi,%ebx - # 3 + # 3 xorl %ecx,%eax movl 12(%esp),%edx xorl %ebx,%eax @@ -92,7 +92,7 @@ L000start: xorl %ebp,%eax roll $12,%edi addl %esi,%edi - # 4 + # 4 movl 16(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -101,7 +101,7 @@ L000start: movl %edi,%eax roll $5,%esi addl %ecx,%esi - # 5 + # 5 xorl %ebx,%eax movl 20(%esp),%edx xorl %esi,%eax @@ -112,7 +112,7 @@ L000start: xorl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 6 + # 6 movl 24(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -121,7 +121,7 @@ L000start: movl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 7 + # 7 xorl %esi,%eax movl 28(%esp),%edx xorl %ebp,%eax @@ -132,7 +132,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 8 + # 8 movl 32(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -141,7 +141,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 9 + # 9 xorl %ebp,%eax movl 36(%esp),%edx xorl %edi,%eax @@ -152,7 +152,7 @@ L000start: xorl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 10 + # 10 movl 40(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -161,7 +161,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 11 + # 11 xorl %edi,%eax movl 44(%esp),%edx xorl %ecx,%eax @@ -172,7 +172,7 @@ L000start: xorl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 12 + # 12 movl 48(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -181,7 +181,7 @@ L000start: movl %ebp,%eax roll $6,%ebx addl %edi,%ebx - # 13 + # 13 xorl %ecx,%eax movl 52(%esp),%edx xorl %ebx,%eax @@ -192,7 +192,7 @@ L000start: xorl %ebp,%eax roll $7,%edi addl %esi,%edi - # 14 + # 14 movl 56(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -201,7 +201,7 @@ L000start: movl %edi,%eax roll $9,%esi addl %ecx,%esi - # 15 + # 15 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -212,7 +212,7 @@ L000start: movl 28(%esp),%edx roll $8,%ecx addl %ebp,%ecx - # 16 + # 16 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -225,7 +225,7 @@ L000start: movl $-1,%edx roll $7,%ebp addl %ebx,%ebp - # 17 + # 17 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -238,7 +238,7 @@ L000start: movl $-1,%eax roll $6,%ebx addl %edi,%ebx - # 18 + # 18 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -251,7 +251,7 @@ L000start: movl $-1,%edx roll $8,%edi addl %esi,%edi - # 19 + # 19 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -264,7 +264,7 @@ L000start: movl $-1,%eax roll $13,%esi addl %ecx,%esi - # 20 + # 20 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -277,7 +277,7 @@ L000start: movl $-1,%edx roll $11,%ecx addl %ebp,%ecx - # 21 + # 21 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -290,7 +290,7 @@ L000start: movl $-1,%eax roll $9,%ebp addl %ebx,%ebp - # 22 + # 22 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -303,7 +303,7 @@ L000start: movl $-1,%edx roll $7,%ebx addl %edi,%ebx - # 23 + # 23 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -316,7 +316,7 @@ L000start: movl $-1,%eax roll $15,%edi addl %esi,%edi - # 24 + # 24 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -329,7 +329,7 @@ L000start: movl $-1,%edx roll $7,%esi addl %ecx,%esi - # 25 + # 25 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -342,7 +342,7 @@ L000start: movl $-1,%eax roll $12,%ecx addl %ebp,%ecx - # 26 + # 26 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -355,7 +355,7 @@ L000start: movl $-1,%edx roll $15,%ebp addl %ebx,%ebp - # 27 + # 27 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -368,7 +368,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 28 + # 28 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -381,7 +381,7 @@ L000start: movl $-1,%edx roll $11,%edi addl %esi,%edi - # 29 + # 29 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -394,7 +394,7 @@ L000start: movl $-1,%eax roll $7,%esi addl %ecx,%esi - # 30 + # 30 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -407,7 +407,7 @@ L000start: movl $-1,%edx roll $13,%ecx addl %ebp,%ecx - # 31 + # 31 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -420,7 +420,7 @@ L000start: subl %ecx,%edx roll $12,%ebp addl %ebx,%ebp - # 32 + # 32 movl 12(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -431,7 +431,7 @@ L000start: subl %ebp,%eax roll $11,%ebx addl %edi,%ebx - # 33 + # 33 movl 40(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -442,7 +442,7 @@ L000start: subl %ebx,%edx roll $13,%edi addl %esi,%edi - # 34 + # 34 movl 56(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -453,7 +453,7 @@ L000start: subl %edi,%eax roll $6,%esi addl %ecx,%esi - # 35 + # 35 movl 16(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -464,7 +464,7 @@ L000start: subl %esi,%edx roll $7,%ecx addl %ebp,%ecx - # 36 + # 36 movl 36(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -475,7 +475,7 @@ L000start: subl %ecx,%eax roll $14,%ebp addl %ebx,%ebp - # 37 + # 37 movl 60(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -486,7 +486,7 @@ L000start: subl %ebp,%edx roll $9,%ebx addl %edi,%ebx - # 38 + # 38 movl 32(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -497,7 +497,7 @@ L000start: subl %ebx,%eax roll $13,%edi addl %esi,%edi - # 39 + # 39 movl 4(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -508,7 +508,7 @@ L000start: subl %edi,%edx roll $15,%esi addl %ecx,%esi - # 40 + # 40 movl 8(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -519,7 +519,7 @@ L000start: subl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 41 + # 41 movl 28(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -530,7 +530,7 @@ L000start: subl %ecx,%edx roll $8,%ebp addl %ebx,%ebp - # 42 + # 42 movl (%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -541,7 +541,7 @@ L000start: subl %ebp,%eax roll $13,%ebx addl %edi,%ebx - # 43 + # 43 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -552,7 +552,7 @@ L000start: subl %ebx,%edx roll $6,%edi addl %esi,%edi - # 44 + # 44 movl 52(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -563,7 +563,7 @@ L000start: subl %edi,%eax roll $5,%esi addl %ecx,%esi - # 45 + # 45 movl 44(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -574,7 +574,7 @@ L000start: subl %esi,%edx roll $12,%ecx addl %ebp,%ecx - # 46 + # 46 movl 20(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -585,7 +585,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 47 + # 47 movl 48(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -596,7 +596,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 48 + # 48 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -609,7 +609,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 49 + # 49 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -622,7 +622,7 @@ L000start: movl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 50 + # 50 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -635,7 +635,7 @@ L000start: movl %edi,%eax roll $14,%ecx addl %ebp,%ecx - # 51 + # 51 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -648,7 +648,7 @@ L000start: movl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 52 + # 52 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -661,7 +661,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 53 + # 53 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -674,7 +674,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 54 + # 54 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -687,7 +687,7 @@ L000start: movl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 55 + # 55 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -700,7 +700,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 56 + # 56 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -713,7 +713,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 57 + # 57 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -726,7 +726,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 58 + # 58 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -739,7 +739,7 @@ L000start: movl %ebp,%eax roll $5,%edi addl %esi,%edi - # 59 + # 59 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -752,7 +752,7 @@ L000start: movl %ebx,%eax roll $6,%esi addl %ecx,%esi - # 60 + # 60 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -765,7 +765,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 61 + # 61 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -778,7 +778,7 @@ L000start: movl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 62 + # 62 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -791,7 +791,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 63 + # 63 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -804,7 +804,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 64 + # 64 movl 16(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -815,7 +815,7 @@ L000start: subl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 65 + # 65 movl (%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -826,7 +826,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 66 + # 66 movl 20(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -837,7 +837,7 @@ L000start: subl %esi,%eax roll $5,%ebp addl %ebx,%ebp - # 67 + # 67 movl 36(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -848,7 +848,7 @@ L000start: subl %ecx,%edx roll $11,%ebx addl %edi,%ebx - # 68 + # 68 movl 28(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -859,7 +859,7 @@ L000start: subl %ebp,%eax roll $6,%edi addl %esi,%edi - # 69 + # 69 movl 48(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -870,7 +870,7 @@ L000start: subl %ebx,%edx roll $8,%esi addl %ecx,%esi - # 70 + # 70 movl 8(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -881,7 +881,7 @@ L000start: subl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 71 + # 71 movl 40(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -892,7 +892,7 @@ L000start: subl %esi,%edx roll $12,%ebp addl %ebx,%ebp - # 72 + # 72 movl 56(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -903,7 +903,7 @@ L000start: subl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 73 + # 73 movl 4(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -914,7 +914,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 74 + # 74 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -925,7 +925,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 75 + # 75 movl 32(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -936,7 +936,7 @@ L000start: subl %edi,%edx roll $14,%ecx addl %ebp,%ecx - # 76 + # 76 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -947,7 +947,7 @@ L000start: subl %esi,%eax roll $11,%ebp addl %ebx,%ebp - # 77 + # 77 movl 24(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -958,7 +958,7 @@ L000start: subl %ecx,%edx roll $8,%ebx addl %edi,%ebx - # 78 + # 78 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -969,7 +969,7 @@ L000start: subl %ebp,%eax roll $5,%edi addl %esi,%edi - # 79 + # 79 movl 52(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -989,7 +989,7 @@ L000start: movl %ebp,80(%esp) movl 12(%edx),%ebx movl 16(%edx),%ebp - # 80 + # 80 movl $-1,%edx subl %ebx,%edx movl 20(%esp),%eax @@ -1002,7 +1002,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 81 + # 81 movl 56(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1013,7 +1013,7 @@ L000start: subl %esi,%edx roll $9,%ebp addl %ebx,%ebp - # 82 + # 82 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1024,7 +1024,7 @@ L000start: subl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 83 + # 83 movl (%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1035,7 +1035,7 @@ L000start: subl %ebp,%edx roll $11,%edi addl %esi,%edi - # 84 + # 84 movl 36(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1046,7 +1046,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 85 + # 85 movl 8(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1057,7 +1057,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 86 + # 86 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -1068,7 +1068,7 @@ L000start: subl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 87 + # 87 movl 16(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -1079,7 +1079,7 @@ L000start: subl %ecx,%edx roll $5,%ebx addl %edi,%ebx - # 88 + # 88 movl 52(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -1090,7 +1090,7 @@ L000start: subl %ebp,%eax roll $7,%edi addl %esi,%edi - # 89 + # 89 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -1101,7 +1101,7 @@ L000start: subl %ebx,%edx roll $7,%esi addl %ecx,%esi - # 90 + # 90 movl 60(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -1112,7 +1112,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 91 + # 91 movl 32(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1123,7 +1123,7 @@ L000start: subl %esi,%edx roll $11,%ebp addl %ebx,%ebp - # 92 + # 92 movl 4(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1134,7 +1134,7 @@ L000start: subl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 93 + # 93 movl 40(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1145,7 +1145,7 @@ L000start: subl %ebp,%edx roll $14,%edi addl %esi,%edi - # 94 + # 94 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1156,7 +1156,7 @@ L000start: subl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 95 + # 95 movl 48(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1167,7 +1167,7 @@ L000start: movl %edi,%eax roll $6,%ecx addl %ebp,%ecx - # 96 + # 96 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1180,7 +1180,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 97 + # 97 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1193,7 +1193,7 @@ L000start: movl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 98 + # 98 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1206,7 +1206,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 99 + # 99 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1219,7 +1219,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 100 + # 100 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1232,7 +1232,7 @@ L000start: movl %edi,%eax roll $12,%ecx addl %ebp,%ecx - # 101 + # 101 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1245,7 +1245,7 @@ L000start: movl %esi,%eax roll $8,%ebp addl %ebx,%ebp - # 102 + # 102 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1258,7 +1258,7 @@ L000start: movl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 103 + # 103 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1271,7 +1271,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 104 + # 104 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1284,7 +1284,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 105 + # 105 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1297,7 +1297,7 @@ L000start: movl %edi,%eax roll $7,%ecx addl %ebp,%ecx - # 106 + # 106 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1310,7 +1310,7 @@ L000start: movl %esi,%eax roll $12,%ebp addl %ebx,%ebp - # 107 + # 107 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1323,7 +1323,7 @@ L000start: movl %ecx,%eax roll $7,%ebx addl %edi,%ebx - # 108 + # 108 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1336,7 +1336,7 @@ L000start: movl %ebp,%eax roll $6,%edi addl %esi,%edi - # 109 + # 109 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1349,7 +1349,7 @@ L000start: movl %ebx,%eax roll $15,%esi addl %ecx,%esi - # 110 + # 110 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1362,7 +1362,7 @@ L000start: movl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 111 + # 111 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1375,7 +1375,7 @@ L000start: subl %ecx,%edx roll $11,%ebp addl %ebx,%ebp - # 112 + # 112 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1386,7 +1386,7 @@ L000start: subl %ebp,%eax roll $9,%ebx addl %edi,%ebx - # 113 + # 113 movl 20(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1397,7 +1397,7 @@ L000start: subl %ebx,%edx roll $7,%edi addl %esi,%edi - # 114 + # 114 movl 4(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1408,7 +1408,7 @@ L000start: subl %edi,%eax roll $15,%esi addl %ecx,%esi - # 115 + # 115 movl 12(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1419,7 +1419,7 @@ L000start: subl %esi,%edx roll $11,%ecx addl %ebp,%ecx - # 116 + # 116 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1430,7 +1430,7 @@ L000start: subl %ecx,%eax roll $8,%ebp addl %ebx,%ebp - # 117 + # 117 movl 56(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1441,7 +1441,7 @@ L000start: subl %ebp,%edx roll $6,%ebx addl %edi,%ebx - # 118 + # 118 movl 24(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -1452,7 +1452,7 @@ L000start: subl %ebx,%eax roll $6,%edi addl %esi,%edi - # 119 + # 119 movl 36(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -1463,7 +1463,7 @@ L000start: subl %edi,%edx roll $14,%esi addl %ecx,%esi - # 120 + # 120 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -1474,7 +1474,7 @@ L000start: subl %esi,%eax roll $12,%ecx addl %ebp,%ecx - # 121 + # 121 movl 32(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -1485,7 +1485,7 @@ L000start: subl %ecx,%edx roll $13,%ebp addl %ebx,%ebp - # 122 + # 122 movl 48(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1496,7 +1496,7 @@ L000start: subl %ebp,%eax roll $5,%ebx addl %edi,%ebx - # 123 + # 123 movl 8(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1507,7 +1507,7 @@ L000start: subl %ebx,%edx roll $14,%edi addl %esi,%edi - # 124 + # 124 movl 40(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1518,7 +1518,7 @@ L000start: subl %edi,%eax roll $13,%esi addl %ecx,%esi - # 125 + # 125 movl (%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1529,7 +1529,7 @@ L000start: subl %esi,%edx roll $13,%ecx addl %ebp,%ecx - # 126 + # 126 movl 16(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1540,7 +1540,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 127 + # 127 movl 52(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1551,7 +1551,7 @@ L000start: movl $-1,%eax roll $5,%ebx addl %edi,%ebx - # 128 + # 128 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1564,7 +1564,7 @@ L000start: movl $-1,%edx roll $15,%edi addl %esi,%edi - # 129 + # 129 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1577,7 +1577,7 @@ L000start: movl $-1,%eax roll $5,%esi addl %ecx,%esi - # 130 + # 130 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1590,7 +1590,7 @@ L000start: movl $-1,%edx roll $8,%ecx addl %ebp,%ecx - # 131 + # 131 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1603,7 +1603,7 @@ L000start: movl $-1,%eax roll $11,%ebp addl %ebx,%ebp - # 132 + # 132 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1616,7 +1616,7 @@ L000start: movl $-1,%edx roll $14,%ebx addl %edi,%ebx - # 133 + # 133 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1629,7 +1629,7 @@ L000start: movl $-1,%eax roll $14,%edi addl %esi,%edi - # 134 + # 134 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -1642,7 +1642,7 @@ L000start: movl $-1,%edx roll $6,%esi addl %ecx,%esi - # 135 + # 135 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -1655,7 +1655,7 @@ L000start: movl $-1,%eax roll $14,%ecx addl %ebp,%ecx - # 136 + # 136 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -1668,7 +1668,7 @@ L000start: movl $-1,%edx roll $6,%ebp addl %ebx,%ebp - # 137 + # 137 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -1681,7 +1681,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 138 + # 138 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1694,7 +1694,7 @@ L000start: movl $-1,%edx roll $12,%edi addl %esi,%edi - # 139 + # 139 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1707,7 +1707,7 @@ L000start: movl $-1,%eax roll $9,%esi addl %ecx,%esi - # 140 + # 140 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1720,7 +1720,7 @@ L000start: movl $-1,%edx roll $12,%ecx addl %ebp,%ecx - # 141 + # 141 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1733,7 +1733,7 @@ L000start: movl $-1,%eax roll $5,%ebp addl %ebx,%ebp - # 142 + # 142 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1746,7 +1746,7 @@ L000start: movl $-1,%edx roll $15,%ebx addl %edi,%ebx - # 143 + # 143 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1759,7 +1759,7 @@ L000start: xorl %ebp,%eax roll $8,%edi addl %esi,%edi - # 144 + # 144 movl 48(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1768,7 +1768,7 @@ L000start: movl %edi,%eax roll $8,%esi addl %ecx,%esi - # 145 + # 145 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -1779,7 +1779,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 146 + # 146 movl 40(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1788,7 +1788,7 @@ L000start: movl %ecx,%eax roll $12,%ebp addl %ebx,%ebp - # 147 + # 147 xorl %esi,%eax movl 16(%esp),%edx xorl %ebp,%eax @@ -1799,7 +1799,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 148 + # 148 movl 4(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1808,7 +1808,7 @@ L000start: movl %ebx,%eax roll $12,%edi addl %esi,%edi - # 149 + # 149 xorl %ebp,%eax movl 20(%esp),%edx xorl %edi,%eax @@ -1819,7 +1819,7 @@ L000start: xorl %ebx,%eax roll $5,%esi addl %ecx,%esi - # 150 + # 150 movl 32(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -1828,7 +1828,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 151 + # 151 xorl %edi,%eax movl 28(%esp),%edx xorl %ecx,%eax @@ -1839,7 +1839,7 @@ L000start: xorl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 152 + # 152 movl 24(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -1848,7 +1848,7 @@ L000start: movl %ebp,%eax roll $8,%ebx addl %edi,%ebx - # 153 + # 153 xorl %ecx,%eax movl 8(%esp),%edx xorl %ebx,%eax @@ -1859,7 +1859,7 @@ L000start: xorl %ebp,%eax roll $13,%edi addl %esi,%edi - # 154 + # 154 movl 52(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1868,7 +1868,7 @@ L000start: movl %edi,%eax roll $6,%esi addl %ecx,%esi - # 155 + # 155 xorl %ebx,%eax movl 56(%esp),%edx xorl %esi,%eax @@ -1879,7 +1879,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 156 + # 156 movl (%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1888,7 +1888,7 @@ L000start: movl %ecx,%eax roll $15,%ebp addl %ebx,%ebp - # 157 + # 157 xorl %esi,%eax movl 12(%esp),%edx xorl %ebp,%eax @@ -1899,7 +1899,7 @@ L000start: xorl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 158 + # 158 movl 36(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1908,7 +1908,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 159 + # 159 xorl %ebp,%eax movl 44(%esp),%edx xorl %edi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s b/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s index 48860a65b70a61..42b2788199f6dc 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm/crypto/sha/sha1-586.s @@ -95,7 +95,7 @@ L002loop: movl 4(%ebp),%ebx movl 8(%ebp),%ecx movl 12(%ebp),%edx - # 00_15 0 + # 00_15 0 movl %ecx,%esi movl %eax,%ebp roll $5,%ebp @@ -107,7 +107,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 1 + # 00_15 1 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -119,7 +119,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 2 + # 00_15 2 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -131,7 +131,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 3 + # 00_15 3 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -143,7 +143,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 4 + # 00_15 4 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -155,7 +155,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 5 + # 00_15 5 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -167,7 +167,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 6 + # 00_15 6 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -179,7 +179,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 7 + # 00_15 7 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -191,7 +191,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 8 + # 00_15 8 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -203,7 +203,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 9 + # 00_15 9 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -215,7 +215,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 10 + # 00_15 10 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -227,7 +227,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 11 + # 00_15 11 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -239,7 +239,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 12 + # 00_15 12 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -251,7 +251,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 13 + # 00_15 13 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -263,7 +263,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 14 + # 00_15 14 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -275,7 +275,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 15 + # 00_15 15 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -288,7 +288,7 @@ L002loop: leal 1518500249(%ebp,%ebx,1),%ebp movl (%esp),%ebx addl %ebp,%ecx - # 16_19 16 + # 16_19 16 movl %edi,%ebp xorl 8(%esp),%ebx xorl %esi,%ebp @@ -305,7 +305,7 @@ L002loop: leal 1518500249(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 16_19 17 + # 16_19 17 movl %edx,%ebp xorl 12(%esp),%eax xorl %edi,%ebp @@ -322,7 +322,7 @@ L002loop: leal 1518500249(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 16_19 18 + # 16_19 18 movl %ecx,%ebp xorl 16(%esp),%esi xorl %edx,%ebp @@ -339,7 +339,7 @@ L002loop: leal 1518500249(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 16_19 19 + # 16_19 19 movl %ebx,%ebp xorl 20(%esp),%edi xorl %ecx,%ebp @@ -356,7 +356,7 @@ L002loop: leal 1518500249(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 20 + # 20_39 20 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -372,7 +372,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 21 + # 20_39 21 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -388,7 +388,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 22 + # 20_39 22 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -404,7 +404,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 23 + # 20_39 23 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -420,7 +420,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 24 + # 20_39 24 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -436,7 +436,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 25 + # 20_39 25 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -452,7 +452,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 26 + # 20_39 26 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -468,7 +468,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 27 + # 20_39 27 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -484,7 +484,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 28 + # 20_39 28 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -500,7 +500,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 29 + # 20_39 29 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -516,7 +516,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 30 + # 20_39 30 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -532,7 +532,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 31 + # 20_39 31 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp @@ -548,7 +548,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl (%esp),%edx addl %ebp,%edi - # 20_39 32 + # 20_39 32 movl %esi,%ebp xorl 8(%esp),%edx xorl %eax,%ebp @@ -564,7 +564,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 4(%esp),%ecx addl %ebp,%edx - # 20_39 33 + # 20_39 33 movl %edi,%ebp xorl 12(%esp),%ecx xorl %esi,%ebp @@ -580,7 +580,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 8(%esp),%ebx addl %ebp,%ecx - # 20_39 34 + # 20_39 34 movl %edx,%ebp xorl 16(%esp),%ebx xorl %edi,%ebp @@ -596,7 +596,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 12(%esp),%eax addl %ebp,%ebx - # 20_39 35 + # 20_39 35 movl %ecx,%ebp xorl 20(%esp),%eax xorl %edx,%ebp @@ -612,7 +612,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 16(%esp),%esi addl %ebp,%eax - # 20_39 36 + # 20_39 36 movl %ebx,%ebp xorl 24(%esp),%esi xorl %ecx,%ebp @@ -628,7 +628,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 20(%esp),%edi addl %ebp,%esi - # 20_39 37 + # 20_39 37 movl %eax,%ebp xorl 28(%esp),%edi xorl %ebx,%ebp @@ -644,7 +644,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 24(%esp),%edx addl %ebp,%edi - # 20_39 38 + # 20_39 38 movl %esi,%ebp xorl 32(%esp),%edx xorl %eax,%ebp @@ -660,7 +660,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 28(%esp),%ecx addl %ebp,%edx - # 20_39 39 + # 20_39 39 movl %edi,%ebp xorl 36(%esp),%ecx xorl %esi,%ebp @@ -676,7 +676,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 32(%esp),%ebx addl %ebp,%ecx - # 40_59 40 + # 40_59 40 movl %edi,%ebp xorl 40(%esp),%ebx xorl %esi,%ebp @@ -695,7 +695,7 @@ L002loop: andl %esi,%ebp movl 36(%esp),%eax addl %ebp,%ebx - # 40_59 41 + # 40_59 41 movl %edx,%ebp xorl 44(%esp),%eax xorl %edi,%ebp @@ -714,7 +714,7 @@ L002loop: andl %edi,%ebp movl 40(%esp),%esi addl %ebp,%eax - # 40_59 42 + # 40_59 42 movl %ecx,%ebp xorl 48(%esp),%esi xorl %edx,%ebp @@ -733,7 +733,7 @@ L002loop: andl %edx,%ebp movl 44(%esp),%edi addl %ebp,%esi - # 40_59 43 + # 40_59 43 movl %ebx,%ebp xorl 52(%esp),%edi xorl %ecx,%ebp @@ -752,7 +752,7 @@ L002loop: andl %ecx,%ebp movl 48(%esp),%edx addl %ebp,%edi - # 40_59 44 + # 40_59 44 movl %eax,%ebp xorl 56(%esp),%edx xorl %ebx,%ebp @@ -771,7 +771,7 @@ L002loop: andl %ebx,%ebp movl 52(%esp),%ecx addl %ebp,%edx - # 40_59 45 + # 40_59 45 movl %esi,%ebp xorl 60(%esp),%ecx xorl %eax,%ebp @@ -790,7 +790,7 @@ L002loop: andl %eax,%ebp movl 56(%esp),%ebx addl %ebp,%ecx - # 40_59 46 + # 40_59 46 movl %edi,%ebp xorl (%esp),%ebx xorl %esi,%ebp @@ -809,7 +809,7 @@ L002loop: andl %esi,%ebp movl 60(%esp),%eax addl %ebp,%ebx - # 40_59 47 + # 40_59 47 movl %edx,%ebp xorl 4(%esp),%eax xorl %edi,%ebp @@ -828,7 +828,7 @@ L002loop: andl %edi,%ebp movl (%esp),%esi addl %ebp,%eax - # 40_59 48 + # 40_59 48 movl %ecx,%ebp xorl 8(%esp),%esi xorl %edx,%ebp @@ -847,7 +847,7 @@ L002loop: andl %edx,%ebp movl 4(%esp),%edi addl %ebp,%esi - # 40_59 49 + # 40_59 49 movl %ebx,%ebp xorl 12(%esp),%edi xorl %ecx,%ebp @@ -866,7 +866,7 @@ L002loop: andl %ecx,%ebp movl 8(%esp),%edx addl %ebp,%edi - # 40_59 50 + # 40_59 50 movl %eax,%ebp xorl 16(%esp),%edx xorl %ebx,%ebp @@ -885,7 +885,7 @@ L002loop: andl %ebx,%ebp movl 12(%esp),%ecx addl %ebp,%edx - # 40_59 51 + # 40_59 51 movl %esi,%ebp xorl 20(%esp),%ecx xorl %eax,%ebp @@ -904,7 +904,7 @@ L002loop: andl %eax,%ebp movl 16(%esp),%ebx addl %ebp,%ecx - # 40_59 52 + # 40_59 52 movl %edi,%ebp xorl 24(%esp),%ebx xorl %esi,%ebp @@ -923,7 +923,7 @@ L002loop: andl %esi,%ebp movl 20(%esp),%eax addl %ebp,%ebx - # 40_59 53 + # 40_59 53 movl %edx,%ebp xorl 28(%esp),%eax xorl %edi,%ebp @@ -942,7 +942,7 @@ L002loop: andl %edi,%ebp movl 24(%esp),%esi addl %ebp,%eax - # 40_59 54 + # 40_59 54 movl %ecx,%ebp xorl 32(%esp),%esi xorl %edx,%ebp @@ -961,7 +961,7 @@ L002loop: andl %edx,%ebp movl 28(%esp),%edi addl %ebp,%esi - # 40_59 55 + # 40_59 55 movl %ebx,%ebp xorl 36(%esp),%edi xorl %ecx,%ebp @@ -980,7 +980,7 @@ L002loop: andl %ecx,%ebp movl 32(%esp),%edx addl %ebp,%edi - # 40_59 56 + # 40_59 56 movl %eax,%ebp xorl 40(%esp),%edx xorl %ebx,%ebp @@ -999,7 +999,7 @@ L002loop: andl %ebx,%ebp movl 36(%esp),%ecx addl %ebp,%edx - # 40_59 57 + # 40_59 57 movl %esi,%ebp xorl 44(%esp),%ecx xorl %eax,%ebp @@ -1018,7 +1018,7 @@ L002loop: andl %eax,%ebp movl 40(%esp),%ebx addl %ebp,%ecx - # 40_59 58 + # 40_59 58 movl %edi,%ebp xorl 48(%esp),%ebx xorl %esi,%ebp @@ -1037,7 +1037,7 @@ L002loop: andl %esi,%ebp movl 44(%esp),%eax addl %ebp,%ebx - # 40_59 59 + # 40_59 59 movl %edx,%ebp xorl 52(%esp),%eax xorl %edi,%ebp @@ -1056,7 +1056,7 @@ L002loop: andl %edi,%ebp movl 48(%esp),%esi addl %ebp,%eax - # 20_39 60 + # 20_39 60 movl %ebx,%ebp xorl 56(%esp),%esi xorl %ecx,%ebp @@ -1072,7 +1072,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 52(%esp),%edi addl %ebp,%esi - # 20_39 61 + # 20_39 61 movl %eax,%ebp xorl 60(%esp),%edi xorl %ebx,%ebp @@ -1088,7 +1088,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 56(%esp),%edx addl %ebp,%edi - # 20_39 62 + # 20_39 62 movl %esi,%ebp xorl (%esp),%edx xorl %eax,%ebp @@ -1104,7 +1104,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 60(%esp),%ecx addl %ebp,%edx - # 20_39 63 + # 20_39 63 movl %edi,%ebp xorl 4(%esp),%ecx xorl %esi,%ebp @@ -1120,7 +1120,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl (%esp),%ebx addl %ebp,%ecx - # 20_39 64 + # 20_39 64 movl %edx,%ebp xorl 8(%esp),%ebx xorl %edi,%ebp @@ -1136,7 +1136,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 20_39 65 + # 20_39 65 movl %ecx,%ebp xorl 12(%esp),%eax xorl %edx,%ebp @@ -1152,7 +1152,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 20_39 66 + # 20_39 66 movl %ebx,%ebp xorl 16(%esp),%esi xorl %ecx,%ebp @@ -1168,7 +1168,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 20_39 67 + # 20_39 67 movl %eax,%ebp xorl 20(%esp),%edi xorl %ebx,%ebp @@ -1184,7 +1184,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 68 + # 20_39 68 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -1200,7 +1200,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 69 + # 20_39 69 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -1216,7 +1216,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 70 + # 20_39 70 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -1232,7 +1232,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 71 + # 20_39 71 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -1248,7 +1248,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 72 + # 20_39 72 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -1264,7 +1264,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 73 + # 20_39 73 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -1280,7 +1280,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 74 + # 20_39 74 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -1296,7 +1296,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 75 + # 20_39 75 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -1312,7 +1312,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 76 + # 20_39 76 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -1328,7 +1328,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 77 + # 20_39 77 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -1343,7 +1343,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 78 + # 20_39 78 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -1358,7 +1358,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 79 + # 20_39 79 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm/include/progs.h b/deps/openssl/config/archs/BSD-x86/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi index 5e6af396479e3a..d637dd5190a9af 100644 --- a/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm b/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm index 15ac34550bee59..9466b5181be011 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9170,6 +9173,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9235,6 +9243,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14013,6 +14026,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14026,6 +14040,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14133,6 +14148,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14185,6 +14204,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -16086,3 +16109,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s index db384026d9d2d6..3e834e9b7b05a4 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bf/bf-586.s @@ -11,7 +11,7 @@ L_BF_encrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -19,7 +19,7 @@ L_BF_encrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 0 + # Round 0 movl 4(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -39,7 +39,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 8(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -59,7 +59,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 12(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -79,7 +79,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 16(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -99,7 +99,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 20(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -119,7 +119,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 24(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -139,7 +139,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 28(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -159,7 +159,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 32(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -179,7 +179,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 36(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -199,7 +199,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 40(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -219,7 +219,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 44(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -239,7 +239,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 48(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -259,7 +259,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 52(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -279,7 +279,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 56(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -299,7 +299,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 60(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -319,7 +319,7 @@ L_BF_encrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 64(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -336,7 +336,7 @@ L_BF_encrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (16) enc=1 + # Load parameter 0 (16) enc=1 movl 20(%esp),%eax xorl %ebx,%edi movl 68(%ebp),%edx @@ -360,7 +360,7 @@ L_BF_decrypt_begin: movl 16(%esp),%ebp pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl (%ebx),%edi movl 4(%ebx),%esi xorl %eax,%eax @@ -368,7 +368,7 @@ L_BF_decrypt_begin: xorl %ecx,%ecx xorl %ebx,%edi - # Round 16 + # Round 16 movl 64(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -388,7 +388,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 15 + # Round 15 movl 60(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -408,7 +408,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 14 + # Round 14 movl 56(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -428,7 +428,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 13 + # Round 13 movl 52(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -448,7 +448,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 12 + # Round 12 movl 48(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -468,7 +468,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 11 + # Round 11 movl 44(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -488,7 +488,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 10 + # Round 10 movl 40(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -508,7 +508,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 9 + # Round 9 movl 36(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -528,7 +528,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 8 + # Round 8 movl 32(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -548,7 +548,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 7 + # Round 7 movl 28(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -568,7 +568,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 6 + # Round 6 movl 24(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -588,7 +588,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 5 + # Round 5 movl 20(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -608,7 +608,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 4 + # Round 4 movl 16(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -628,7 +628,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 3 + # Round 3 movl 12(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -648,7 +648,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%edi - # Round 2 + # Round 2 movl 8(%ebp),%edx movl %edi,%ebx xorl %edx,%esi @@ -668,7 +668,7 @@ L_BF_decrypt_begin: xorl %eax,%eax xorl %ebx,%esi - # Round 1 + # Round 1 movl 4(%ebp),%edx movl %esi,%ebx xorl %edx,%edi @@ -685,7 +685,7 @@ L_BF_decrypt_begin: xorl %eax,%ebx movl 3144(%ebp,%edx,4),%edx addl %edx,%ebx - # Load parameter 0 (1) enc=0 + # Load parameter 0 (1) enc=0 movl 20(%esp),%eax xorl %ebx,%edi movl (%ebp),%edx @@ -708,7 +708,7 @@ L_BF_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -719,9 +719,9 @@ L_BF_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 3 + # get and push parameter 3 movl 48(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s index 80c8db4d292bc9..c7c0a81c38b98a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/bn-586.s @@ -116,7 +116,7 @@ L001maw_non_sse2: jz L006maw_finish .align 4,0x90 L007maw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -125,7 +125,7 @@ L007maw_loop: adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -134,7 +134,7 @@ L007maw_loop: adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -143,7 +143,7 @@ L007maw_loop: adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -152,7 +152,7 @@ L007maw_loop: adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -161,7 +161,7 @@ L007maw_loop: adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -170,7 +170,7 @@ L007maw_loop: adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -179,7 +179,7 @@ L007maw_loop: adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ebp addl %esi,%eax @@ -199,7 +199,7 @@ L006maw_finish: jnz L008maw_finish2 jmp L009maw_end L008maw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ebp addl %esi,%eax @@ -210,7 +210,7 @@ L008maw_finish2: movl %eax,(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ebp addl %esi,%eax @@ -221,7 +221,7 @@ L008maw_finish2: movl %eax,4(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ebp addl %esi,%eax @@ -232,7 +232,7 @@ L008maw_finish2: movl %eax,8(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ebp addl %esi,%eax @@ -243,7 +243,7 @@ L008maw_finish2: movl %eax,12(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ebp addl %esi,%eax @@ -254,7 +254,7 @@ L008maw_finish2: movl %eax,16(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ebp addl %esi,%eax @@ -265,7 +265,7 @@ L008maw_finish2: movl %eax,20(%edi) movl %edx,%esi jz L009maw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ebp addl %esi,%eax @@ -328,56 +328,56 @@ L011mw_non_sse2: andl $4294967288,%ebp jz L013mw_finish L014mw_loop: - # Round 0 + # Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,(%edi) movl %edx,%esi - # Round 4 + # Round 4 movl 4(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,4(%edi) movl %edx,%esi - # Round 8 + # Round 8 movl 8(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,8(%edi) movl %edx,%esi - # Round 12 + # Round 12 movl 12(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,12(%edi) movl %edx,%esi - # Round 16 + # Round 16 movl 16(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,16(%edi) movl %edx,%esi - # Round 20 + # Round 20 movl 20(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,20(%edi) movl %edx,%esi - # Round 24 + # Round 24 movl 24(%ebx),%eax mull %ecx addl %esi,%eax adcl $0,%edx movl %eax,24(%edi) movl %edx,%esi - # Round 28 + # Round 28 movl 28(%ebx),%eax mull %ecx addl %esi,%eax @@ -396,7 +396,7 @@ L013mw_finish: jnz L015mw_finish2 jmp L016mw_end L015mw_finish2: - # Tail Round 0 + # Tail Round 0 movl (%ebx),%eax mull %ecx addl %esi,%eax @@ -405,7 +405,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 1 + # Tail Round 1 movl 4(%ebx),%eax mull %ecx addl %esi,%eax @@ -414,7 +414,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 2 + # Tail Round 2 movl 8(%ebx),%eax mull %ecx addl %esi,%eax @@ -423,7 +423,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 3 + # Tail Round 3 movl 12(%ebx),%eax mull %ecx addl %esi,%eax @@ -432,7 +432,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 4 + # Tail Round 4 movl 16(%ebx),%eax mull %ecx addl %esi,%eax @@ -441,7 +441,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 5 + # Tail Round 5 movl 20(%ebx),%eax mull %ecx addl %esi,%eax @@ -450,7 +450,7 @@ L015mw_finish2: movl %edx,%esi decl %ebp jz L016mw_end - # Tail Round 6 + # Tail Round 6 movl 24(%ebx),%eax mull %ecx addl %esi,%eax @@ -503,42 +503,42 @@ L018sqr_non_sse2: andl $4294967288,%ebx jz L020sw_finish L021sw_loop: - # Round 0 + # Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) movl %edx,4(%esi) - # Round 4 + # Round 4 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) movl %edx,12(%esi) - # Round 8 + # Round 8 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) movl %edx,20(%esi) - # Round 12 + # Round 12 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) movl %edx,28(%esi) - # Round 16 + # Round 16 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) movl %edx,36(%esi) - # Round 20 + # Round 20 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) movl %edx,44(%esi) - # Round 24 + # Round 24 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) movl %edx,52(%esi) - # Round 28 + # Round 28 movl 28(%edi),%eax mull %eax movl %eax,56(%esi) @@ -552,49 +552,49 @@ L020sw_finish: movl 28(%esp),%ebx andl $7,%ebx jz L022sw_end - # Tail Round 0 + # Tail Round 0 movl (%edi),%eax mull %eax movl %eax,(%esi) decl %ebx movl %edx,4(%esi) jz L022sw_end - # Tail Round 1 + # Tail Round 1 movl 4(%edi),%eax mull %eax movl %eax,8(%esi) decl %ebx movl %edx,12(%esi) jz L022sw_end - # Tail Round 2 + # Tail Round 2 movl 8(%edi),%eax mull %eax movl %eax,16(%esi) decl %ebx movl %edx,20(%esi) jz L022sw_end - # Tail Round 3 + # Tail Round 3 movl 12(%edi),%eax mull %eax movl %eax,24(%esi) decl %ebx movl %edx,28(%esi) jz L022sw_end - # Tail Round 4 + # Tail Round 4 movl 16(%edi),%eax mull %eax movl %eax,32(%esi) decl %ebx movl %edx,36(%esi) jz L022sw_end - # Tail Round 5 + # Tail Round 5 movl 20(%edi),%eax mull %eax movl %eax,40(%esi) decl %ebx movl %edx,44(%esi) jz L022sw_end - # Tail Round 6 + # Tail Round 6 movl 24(%edi),%eax mull %eax movl %eax,48(%esi) @@ -633,7 +633,7 @@ L_bn_add_words_begin: andl $4294967288,%ebp jz L023aw_finish L024aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -642,7 +642,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -651,7 +651,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -660,7 +660,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -669,7 +669,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -678,7 +678,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -687,7 +687,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -696,7 +696,7 @@ L024aw_loop: addl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx addl %eax,%ecx @@ -715,7 +715,7 @@ L023aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L025aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx addl %eax,%ecx @@ -726,7 +726,7 @@ L023aw_finish: decl %ebp movl %ecx,(%ebx) jz L025aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx addl %eax,%ecx @@ -737,7 +737,7 @@ L023aw_finish: decl %ebp movl %ecx,4(%ebx) jz L025aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx addl %eax,%ecx @@ -748,7 +748,7 @@ L023aw_finish: decl %ebp movl %ecx,8(%ebx) jz L025aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx addl %eax,%ecx @@ -759,7 +759,7 @@ L023aw_finish: decl %ebp movl %ecx,12(%ebx) jz L025aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx addl %eax,%ecx @@ -770,7 +770,7 @@ L023aw_finish: decl %ebp movl %ecx,16(%ebx) jz L025aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx addl %eax,%ecx @@ -781,7 +781,7 @@ L023aw_finish: decl %ebp movl %ecx,20(%ebx) jz L025aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx addl %eax,%ecx @@ -814,7 +814,7 @@ L_bn_sub_words_begin: andl $4294967288,%ebp jz L026aw_finish L027aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -823,7 +823,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -832,7 +832,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -841,7 +841,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -850,7 +850,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -859,7 +859,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -868,7 +868,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -877,7 +877,7 @@ L027aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -896,7 +896,7 @@ L026aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L028aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -907,7 +907,7 @@ L026aw_finish: decl %ebp movl %ecx,(%ebx) jz L028aw_end - # Tail Round 1 + # Tail Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -918,7 +918,7 @@ L026aw_finish: decl %ebp movl %ecx,4(%ebx) jz L028aw_end - # Tail Round 2 + # Tail Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -929,7 +929,7 @@ L026aw_finish: decl %ebp movl %ecx,8(%ebx) jz L028aw_end - # Tail Round 3 + # Tail Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -940,7 +940,7 @@ L026aw_finish: decl %ebp movl %ecx,12(%ebx) jz L028aw_end - # Tail Round 4 + # Tail Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -951,7 +951,7 @@ L026aw_finish: decl %ebp movl %ecx,16(%ebx) jz L028aw_end - # Tail Round 5 + # Tail Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -962,7 +962,7 @@ L026aw_finish: decl %ebp movl %ecx,20(%ebx) jz L028aw_end - # Tail Round 6 + # Tail Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -995,7 +995,7 @@ L_bn_sub_part_words_begin: andl $4294967288,%ebp jz L029aw_finish L030aw_loop: - # Round 0 + # Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1004,7 +1004,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # Round 1 + # Round 1 movl 4(%esi),%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1013,7 +1013,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # Round 2 + # Round 2 movl 8(%esi),%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1022,7 +1022,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # Round 3 + # Round 3 movl 12(%esi),%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1031,7 +1031,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # Round 4 + # Round 4 movl 16(%esi),%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1040,7 +1040,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # Round 5 + # Round 5 movl 20(%esi),%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1049,7 +1049,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # Round 6 + # Round 6 movl 24(%esi),%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1058,7 +1058,7 @@ L030aw_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # Round 7 + # Round 7 movl 28(%esi),%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1077,7 +1077,7 @@ L029aw_finish: movl 32(%esp),%ebp andl $7,%ebp jz L031aw_end - # Tail Round 0 + # Tail Round 0 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1091,7 +1091,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 1 + # Tail Round 1 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1105,7 +1105,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 2 + # Tail Round 2 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1119,7 +1119,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 3 + # Tail Round 3 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1133,7 +1133,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 4 + # Tail Round 4 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1147,7 +1147,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 5 + # Tail Round 5 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1161,7 +1161,7 @@ L029aw_finish: addl $4,%ebx decl %ebp jz L031aw_end - # Tail Round 6 + # Tail Round 6 movl (%esi),%ecx movl (%edi),%edx subl %eax,%ecx @@ -1180,14 +1180,14 @@ L031aw_end: cmpl $0,%ebp je L032pw_end jge L033pw_pos - # pw_neg + # pw_neg movl $0,%edx subl %ebp,%edx movl %edx,%ebp andl $4294967288,%ebp jz L034pw_neg_finish L035pw_neg_loop: - # dl<0 Round 0 + # dl<0 Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1196,7 +1196,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,(%ebx) - # dl<0 Round 1 + # dl<0 Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1205,7 +1205,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,4(%ebx) - # dl<0 Round 2 + # dl<0 Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1214,7 +1214,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,8(%ebx) - # dl<0 Round 3 + # dl<0 Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1223,7 +1223,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,12(%ebx) - # dl<0 Round 4 + # dl<0 Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1232,7 +1232,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,16(%ebx) - # dl<0 Round 5 + # dl<0 Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1241,7 +1241,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,20(%ebx) - # dl<0 Round 6 + # dl<0 Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1250,7 +1250,7 @@ L035pw_neg_loop: subl %edx,%ecx adcl $0,%eax movl %ecx,24(%ebx) - # dl<0 Round 7 + # dl<0 Round 7 movl $0,%ecx movl 28(%edi),%edx subl %eax,%ecx @@ -1270,7 +1270,7 @@ L034pw_neg_finish: subl %edx,%ebp andl $7,%ebp jz L032pw_end - # dl<0 Tail Round 0 + # dl<0 Tail Round 0 movl $0,%ecx movl (%edi),%edx subl %eax,%ecx @@ -1281,7 +1281,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,(%ebx) jz L032pw_end - # dl<0 Tail Round 1 + # dl<0 Tail Round 1 movl $0,%ecx movl 4(%edi),%edx subl %eax,%ecx @@ -1292,7 +1292,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,4(%ebx) jz L032pw_end - # dl<0 Tail Round 2 + # dl<0 Tail Round 2 movl $0,%ecx movl 8(%edi),%edx subl %eax,%ecx @@ -1303,7 +1303,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,8(%ebx) jz L032pw_end - # dl<0 Tail Round 3 + # dl<0 Tail Round 3 movl $0,%ecx movl 12(%edi),%edx subl %eax,%ecx @@ -1314,7 +1314,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,12(%ebx) jz L032pw_end - # dl<0 Tail Round 4 + # dl<0 Tail Round 4 movl $0,%ecx movl 16(%edi),%edx subl %eax,%ecx @@ -1325,7 +1325,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,16(%ebx) jz L032pw_end - # dl<0 Tail Round 5 + # dl<0 Tail Round 5 movl $0,%ecx movl 20(%edi),%edx subl %eax,%ecx @@ -1336,7 +1336,7 @@ L034pw_neg_finish: decl %ebp movl %ecx,20(%ebx) jz L032pw_end - # dl<0 Tail Round 6 + # dl<0 Tail Round 6 movl $0,%ecx movl 24(%edi),%edx subl %eax,%ecx @@ -1350,42 +1350,42 @@ L033pw_pos: andl $4294967288,%ebp jz L036pw_pos_finish L037pw_pos_loop: - # dl>0 Round 0 + # dl>0 Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L038pw_nc0 - # dl>0 Round 1 + # dl>0 Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L039pw_nc1 - # dl>0 Round 2 + # dl>0 Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L040pw_nc2 - # dl>0 Round 3 + # dl>0 Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L041pw_nc3 - # dl>0 Round 4 + # dl>0 Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L042pw_nc4 - # dl>0 Round 5 + # dl>0 Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L043pw_nc5 - # dl>0 Round 6 + # dl>0 Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) jnc L044pw_nc6 - # dl>0 Round 7 + # dl>0 Round 7 movl 28(%esi),%ecx subl %eax,%ecx movl %ecx,28(%ebx) @@ -1399,49 +1399,49 @@ L036pw_pos_finish: movl 36(%esp),%ebp andl $7,%ebp jz L032pw_end - # dl>0 Tail Round 0 + # dl>0 Tail Round 0 movl (%esi),%ecx subl %eax,%ecx movl %ecx,(%ebx) jnc L046pw_tail_nc0 decl %ebp jz L032pw_end - # dl>0 Tail Round 1 + # dl>0 Tail Round 1 movl 4(%esi),%ecx subl %eax,%ecx movl %ecx,4(%ebx) jnc L047pw_tail_nc1 decl %ebp jz L032pw_end - # dl>0 Tail Round 2 + # dl>0 Tail Round 2 movl 8(%esi),%ecx subl %eax,%ecx movl %ecx,8(%ebx) jnc L048pw_tail_nc2 decl %ebp jz L032pw_end - # dl>0 Tail Round 3 + # dl>0 Tail Round 3 movl 12(%esi),%ecx subl %eax,%ecx movl %ecx,12(%ebx) jnc L049pw_tail_nc3 decl %ebp jz L032pw_end - # dl>0 Tail Round 4 + # dl>0 Tail Round 4 movl 16(%esi),%ecx subl %eax,%ecx movl %ecx,16(%ebx) jnc L050pw_tail_nc4 decl %ebp jz L032pw_end - # dl>0 Tail Round 5 + # dl>0 Tail Round 5 movl 20(%esi),%ecx subl %eax,%ecx movl %ecx,20(%ebx) jnc L051pw_tail_nc5 decl %ebp jz L032pw_end - # dl>0 Tail Round 6 + # dl>0 Tail Round 6 movl 24(%esi),%ecx subl %eax,%ecx movl %ecx,24(%ebx) diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s index 0196f42b789164..d2608b28564f5a 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/bn/co-586.s @@ -14,9 +14,9 @@ L_bn_mul_comba8_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -25,17 +25,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -44,24 +44,24 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -70,31 +70,31 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -103,38 +103,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 16(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[4]*b[0] + # mul a[4]*b[0] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[0]*b[4] + # mul a[0]*b[4] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -143,45 +143,45 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 20(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[5]*b[0] + # mul a[5]*b[0] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[4]*b[1] + # mul a[4]*b[1] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[1]*b[4] + # mul a[1]*b[4] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[0]*b[5] + # mul a[0]*b[5] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -190,52 +190,52 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 24(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[6]*b[0] + # mul a[6]*b[0] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[5]*b[1] + # mul a[5]*b[1] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[4]*b[2] + # mul a[4]*b[2] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[2]*b[4] + # mul a[2]*b[4] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[1]*b[5] + # mul a[1]*b[5] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[0]*b[6] + # mul a[0]*b[6] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -244,59 +244,59 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,24(%eax) movl 28(%esi),%eax - # saved r[6] - # ################## Calculate word 7 + # saved r[6] + # ################## Calculate word 7 xorl %ebx,%ebx - # mul a[7]*b[0] + # mul a[7]*b[0] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[6]*b[1] + # mul a[6]*b[1] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[5]*b[2] + # mul a[5]*b[2] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[4]*b[3] + # mul a[4]*b[3] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[3]*b[4] + # mul a[3]*b[4] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[2]*b[5] + # mul a[2]*b[5] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[1]*b[6] + # mul a[1]*b[6] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[0]*b[7] + # mul a[0]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -305,52 +305,52 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,28(%eax) movl 28(%esi),%eax - # saved r[7] - # ################## Calculate word 8 + # saved r[7] + # ################## Calculate word 8 xorl %ecx,%ecx - # mul a[7]*b[1] + # mul a[7]*b[1] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[6]*b[2] + # mul a[6]*b[2] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[5]*b[3] + # mul a[5]*b[3] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 16(%edi),%edx adcl $0,%ecx - # mul a[4]*b[4] + # mul a[4]*b[4] mull %edx addl %eax,%ebp movl 12(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[3]*b[5] + # mul a[3]*b[5] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[2]*b[6] + # mul a[2]*b[6] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[1]*b[7] + # mul a[1]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -359,45 +359,45 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,32(%eax) movl 28(%esi),%eax - # saved r[8] - # ################## Calculate word 9 + # saved r[8] + # ################## Calculate word 9 xorl %ebp,%ebp - # mul a[7]*b[2] + # mul a[7]*b[2] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[6]*b[3] + # mul a[6]*b[3] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 16(%edi),%edx adcl $0,%ebp - # mul a[5]*b[4] + # mul a[5]*b[4] mull %edx addl %eax,%ebx movl 16(%esi),%eax adcl %edx,%ecx movl 20(%edi),%edx adcl $0,%ebp - # mul a[4]*b[5] + # mul a[4]*b[5] mull %edx addl %eax,%ebx movl 12(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[3]*b[6] + # mul a[3]*b[6] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[2]*b[7] + # mul a[2]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -406,38 +406,38 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,36(%eax) movl 28(%esi),%eax - # saved r[9] - # ################## Calculate word 10 + # saved r[9] + # ################## Calculate word 10 xorl %ebx,%ebx - # mul a[7]*b[3] + # mul a[7]*b[3] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 16(%edi),%edx adcl $0,%ebx - # mul a[6]*b[4] + # mul a[6]*b[4] mull %edx addl %eax,%ecx movl 20(%esi),%eax adcl %edx,%ebp movl 20(%edi),%edx adcl $0,%ebx - # mul a[5]*b[5] + # mul a[5]*b[5] mull %edx addl %eax,%ecx movl 16(%esi),%eax adcl %edx,%ebp movl 24(%edi),%edx adcl $0,%ebx - # mul a[4]*b[6] + # mul a[4]*b[6] mull %edx addl %eax,%ecx movl 12(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[3]*b[7] + # mul a[3]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -446,31 +446,31 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,40(%eax) movl 28(%esi),%eax - # saved r[10] - # ################## Calculate word 11 + # saved r[10] + # ################## Calculate word 11 xorl %ecx,%ecx - # mul a[7]*b[4] + # mul a[7]*b[4] mull %edx addl %eax,%ebp movl 24(%esi),%eax adcl %edx,%ebx movl 20(%edi),%edx adcl $0,%ecx - # mul a[6]*b[5] + # mul a[6]*b[5] mull %edx addl %eax,%ebp movl 20(%esi),%eax adcl %edx,%ebx movl 24(%edi),%edx adcl $0,%ecx - # mul a[5]*b[6] + # mul a[5]*b[6] mull %edx addl %eax,%ebp movl 16(%esi),%eax adcl %edx,%ebx movl 28(%edi),%edx adcl $0,%ecx - # mul a[4]*b[7] + # mul a[4]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -479,24 +479,24 @@ L_bn_mul_comba8_begin: adcl $0,%ecx movl %ebp,44(%eax) movl 28(%esi),%eax - # saved r[11] - # ################## Calculate word 12 + # saved r[11] + # ################## Calculate word 12 xorl %ebp,%ebp - # mul a[7]*b[5] + # mul a[7]*b[5] mull %edx addl %eax,%ebx movl 24(%esi),%eax adcl %edx,%ecx movl 24(%edi),%edx adcl $0,%ebp - # mul a[6]*b[6] + # mul a[6]*b[6] mull %edx addl %eax,%ebx movl 20(%esi),%eax adcl %edx,%ecx movl 28(%edi),%edx adcl $0,%ebp - # mul a[5]*b[7] + # mul a[5]*b[7] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -505,17 +505,17 @@ L_bn_mul_comba8_begin: adcl $0,%ebp movl %ebx,48(%eax) movl 28(%esi),%eax - # saved r[12] - # ################## Calculate word 13 + # saved r[12] + # ################## Calculate word 13 xorl %ebx,%ebx - # mul a[7]*b[6] + # mul a[7]*b[6] mull %edx addl %eax,%ecx movl 24(%esi),%eax adcl %edx,%ebp movl 28(%edi),%edx adcl $0,%ebx - # mul a[6]*b[7] + # mul a[6]*b[7] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -524,18 +524,18 @@ L_bn_mul_comba8_begin: adcl $0,%ebx movl %ecx,52(%eax) movl 28(%esi),%eax - # saved r[13] - # ################## Calculate word 14 + # saved r[13] + # ################## Calculate word 14 xorl %ecx,%ecx - # mul a[7]*b[7] + # mul a[7]*b[7] mull %edx addl %eax,%ebp movl 20(%esp),%eax adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%eax) - # saved r[14] - # save r[15] + # saved r[14] + # save r[15] movl %ebx,60(%eax) popl %ebx popl %ebp @@ -557,9 +557,9 @@ L_bn_mul_comba4_begin: movl (%esi),%eax xorl %ecx,%ecx movl (%edi),%edx - # ################## Calculate word 0 + # ################## Calculate word 0 xorl %ebp,%ebp - # mul a[0]*b[0] + # mul a[0]*b[0] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -568,17 +568,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,(%eax) movl 4(%esi),%eax - # saved r[0] - # ################## Calculate word 1 + # saved r[0] + # ################## Calculate word 1 xorl %ebx,%ebx - # mul a[1]*b[0] + # mul a[1]*b[0] mull %edx addl %eax,%ecx movl (%esi),%eax adcl %edx,%ebp movl 4(%edi),%edx adcl $0,%ebx - # mul a[0]*b[1] + # mul a[0]*b[1] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -587,24 +587,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,4(%eax) movl 8(%esi),%eax - # saved r[1] - # ################## Calculate word 2 + # saved r[1] + # ################## Calculate word 2 xorl %ecx,%ecx - # mul a[2]*b[0] + # mul a[2]*b[0] mull %edx addl %eax,%ebp movl 4(%esi),%eax adcl %edx,%ebx movl 4(%edi),%edx adcl $0,%ecx - # mul a[1]*b[1] + # mul a[1]*b[1] mull %edx addl %eax,%ebp movl (%esi),%eax adcl %edx,%ebx movl 8(%edi),%edx adcl $0,%ecx - # mul a[0]*b[2] + # mul a[0]*b[2] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -613,31 +613,31 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,8(%eax) movl 12(%esi),%eax - # saved r[2] - # ################## Calculate word 3 + # saved r[2] + # ################## Calculate word 3 xorl %ebp,%ebp - # mul a[3]*b[0] + # mul a[3]*b[0] mull %edx addl %eax,%ebx movl 8(%esi),%eax adcl %edx,%ecx movl 4(%edi),%edx adcl $0,%ebp - # mul a[2]*b[1] + # mul a[2]*b[1] mull %edx addl %eax,%ebx movl 4(%esi),%eax adcl %edx,%ecx movl 8(%edi),%edx adcl $0,%ebp - # mul a[1]*b[2] + # mul a[1]*b[2] mull %edx addl %eax,%ebx movl (%esi),%eax adcl %edx,%ecx movl 12(%edi),%edx adcl $0,%ebp - # mul a[0]*b[3] + # mul a[0]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax @@ -646,24 +646,24 @@ L_bn_mul_comba4_begin: adcl $0,%ebp movl %ebx,12(%eax) movl 12(%esi),%eax - # saved r[3] - # ################## Calculate word 4 + # saved r[3] + # ################## Calculate word 4 xorl %ebx,%ebx - # mul a[3]*b[1] + # mul a[3]*b[1] mull %edx addl %eax,%ecx movl 8(%esi),%eax adcl %edx,%ebp movl 8(%edi),%edx adcl $0,%ebx - # mul a[2]*b[2] + # mul a[2]*b[2] mull %edx addl %eax,%ecx movl 4(%esi),%eax adcl %edx,%ebp movl 12(%edi),%edx adcl $0,%ebx - # mul a[1]*b[3] + # mul a[1]*b[3] mull %edx addl %eax,%ecx movl 20(%esp),%eax @@ -672,17 +672,17 @@ L_bn_mul_comba4_begin: adcl $0,%ebx movl %ecx,16(%eax) movl 12(%esi),%eax - # saved r[4] - # ################## Calculate word 5 + # saved r[4] + # ################## Calculate word 5 xorl %ecx,%ecx - # mul a[3]*b[2] + # mul a[3]*b[2] mull %edx addl %eax,%ebp movl 8(%esi),%eax adcl %edx,%ebx movl 12(%edi),%edx adcl $0,%ecx - # mul a[2]*b[3] + # mul a[2]*b[3] mull %edx addl %eax,%ebp movl 20(%esp),%eax @@ -691,18 +691,18 @@ L_bn_mul_comba4_begin: adcl $0,%ecx movl %ebp,20(%eax) movl 12(%esi),%eax - # saved r[5] - # ################## Calculate word 6 + # saved r[5] + # ################## Calculate word 6 xorl %ebp,%ebp - # mul a[3]*b[3] + # mul a[3]*b[3] mull %edx addl %eax,%ebx movl 20(%esp),%eax adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%eax) - # saved r[6] - # save r[7] + # saved r[6] + # save r[7] movl %ecx,28(%eax) popl %ebx popl %ebp @@ -723,9 +723,9 @@ L_bn_sqr_comba8_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -733,10 +733,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -747,10 +747,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -759,7 +759,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -767,10 +767,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -780,7 +780,7 @@ L_bn_sqr_comba8_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -791,10 +791,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,12(%edi) movl (%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[4]*a[0] + # sqr a[4]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -804,7 +804,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -813,7 +813,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -821,10 +821,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 20(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[5]*a[0] + # sqr a[5]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -834,7 +834,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ecx movl 4(%esi),%edx - # sqr a[4]*a[1] + # sqr a[4]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -844,7 +844,7 @@ L_bn_sqr_comba8_begin: movl 12(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -855,10 +855,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,20(%edi) movl (%esi),%edx - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[6]*a[0] + # sqr a[6]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -868,7 +868,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[5]*a[1] + # sqr a[5]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -878,7 +878,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebp movl 8(%esi),%edx - # sqr a[4]*a[2] + # sqr a[4]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -887,7 +887,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 12(%esi),%eax adcl $0,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -895,10 +895,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,24(%edi) movl 28(%esi),%eax - # saved r[6] - # ############### Calculate word 7 + # saved r[6] + # ############### Calculate word 7 xorl %ebx,%ebx - # sqr a[7]*a[0] + # sqr a[7]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -908,7 +908,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 4(%esi),%edx - # sqr a[6]*a[1] + # sqr a[6]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -918,7 +918,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebx movl 8(%esi),%edx - # sqr a[5]*a[2] + # sqr a[5]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -928,7 +928,7 @@ L_bn_sqr_comba8_begin: movl 16(%esi),%eax adcl $0,%ebx movl 12(%esi),%edx - # sqr a[4]*a[3] + # sqr a[4]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -939,10 +939,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,28(%edi) movl 4(%esi),%edx - # saved r[7] - # ############### Calculate word 8 + # saved r[7] + # ############### Calculate word 8 xorl %ecx,%ecx - # sqr a[7]*a[1] + # sqr a[7]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -952,7 +952,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 8(%esi),%edx - # sqr a[6]*a[2] + # sqr a[6]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -962,7 +962,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ecx movl 12(%esi),%edx - # sqr a[5]*a[3] + # sqr a[5]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -971,7 +971,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebx movl 16(%esi),%eax adcl $0,%ecx - # sqr a[4]*a[4] + # sqr a[4]*a[4] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -979,10 +979,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,32(%edi) movl 28(%esi),%eax - # saved r[8] - # ############### Calculate word 9 + # saved r[8] + # ############### Calculate word 9 xorl %ebp,%ebp - # sqr a[7]*a[2] + # sqr a[7]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -992,7 +992,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebp movl 12(%esi),%edx - # sqr a[6]*a[3] + # sqr a[6]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1002,7 +1002,7 @@ L_bn_sqr_comba8_begin: movl 20(%esi),%eax adcl $0,%ebp movl 16(%esi),%edx - # sqr a[5]*a[4] + # sqr a[5]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1013,10 +1013,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,36(%edi) movl 12(%esi),%edx - # saved r[9] - # ############### Calculate word 10 + # saved r[9] + # ############### Calculate word 10 xorl %ebx,%ebx - # sqr a[7]*a[3] + # sqr a[7]*a[3] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1026,7 +1026,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ebx movl 16(%esi),%edx - # sqr a[6]*a[4] + # sqr a[6]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1035,7 +1035,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ebp movl 20(%esi),%eax adcl $0,%ebx - # sqr a[5]*a[5] + # sqr a[5]*a[5] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1043,10 +1043,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebx movl %ecx,40(%edi) movl 28(%esi),%eax - # saved r[10] - # ############### Calculate word 11 + # saved r[10] + # ############### Calculate word 11 xorl %ecx,%ecx - # sqr a[7]*a[4] + # sqr a[7]*a[4] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1056,7 +1056,7 @@ L_bn_sqr_comba8_begin: movl 24(%esi),%eax adcl $0,%ecx movl 20(%esi),%edx - # sqr a[6]*a[5] + # sqr a[6]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1067,10 +1067,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ecx movl %ebp,44(%edi) movl 20(%esi),%edx - # saved r[11] - # ############### Calculate word 12 + # saved r[11] + # ############### Calculate word 12 xorl %ebp,%ebp - # sqr a[7]*a[5] + # sqr a[7]*a[5] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1079,7 +1079,7 @@ L_bn_sqr_comba8_begin: adcl %edx,%ecx movl 24(%esi),%eax adcl $0,%ebp - # sqr a[6]*a[6] + # sqr a[6]*a[6] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1087,10 +1087,10 @@ L_bn_sqr_comba8_begin: adcl $0,%ebp movl %ebx,48(%edi) movl 28(%esi),%eax - # saved r[12] - # ############### Calculate word 13 + # saved r[12] + # ############### Calculate word 13 xorl %ebx,%ebx - # sqr a[7]*a[6] + # sqr a[7]*a[6] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1100,16 +1100,16 @@ L_bn_sqr_comba8_begin: movl 28(%esi),%eax adcl $0,%ebx movl %ecx,52(%edi) - # saved r[13] - # ############### Calculate word 14 + # saved r[13] + # ############### Calculate word 14 xorl %ecx,%ecx - # sqr a[7]*a[7] + # sqr a[7]*a[7] mull %eax addl %eax,%ebp adcl %edx,%ebx adcl $0,%ecx movl %ebp,56(%edi) - # saved r[14] + # saved r[14] movl %ebx,60(%edi) popl %ebx popl %ebp @@ -1130,9 +1130,9 @@ L_bn_sqr_comba4_begin: xorl %ebx,%ebx xorl %ecx,%ecx movl (%esi),%eax - # ############### Calculate word 0 + # ############### Calculate word 0 xorl %ebp,%ebp - # sqr a[0]*a[0] + # sqr a[0]*a[0] mull %eax addl %eax,%ebx adcl %edx,%ecx @@ -1140,10 +1140,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,(%edi) movl 4(%esi),%eax - # saved r[0] - # ############### Calculate word 1 + # saved r[0] + # ############### Calculate word 1 xorl %ebx,%ebx - # sqr a[1]*a[0] + # sqr a[1]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1154,10 +1154,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,4(%edi) movl (%esi),%edx - # saved r[1] - # ############### Calculate word 2 + # saved r[1] + # ############### Calculate word 2 xorl %ecx,%ecx - # sqr a[2]*a[0] + # sqr a[2]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1166,7 +1166,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebx movl 4(%esi),%eax adcl $0,%ecx - # sqr a[1]*a[1] + # sqr a[1]*a[1] mull %eax addl %eax,%ebp adcl %edx,%ebx @@ -1174,10 +1174,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ecx movl %ebp,8(%edi) movl 12(%esi),%eax - # saved r[2] - # ############### Calculate word 3 + # saved r[2] + # ############### Calculate word 3 xorl %ebp,%ebp - # sqr a[3]*a[0] + # sqr a[3]*a[0] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1187,7 +1187,7 @@ L_bn_sqr_comba4_begin: movl 8(%esi),%eax adcl $0,%ebp movl 4(%esi),%edx - # sqr a[2]*a[1] + # sqr a[2]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1198,10 +1198,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebp movl %ebx,12(%edi) movl 4(%esi),%edx - # saved r[3] - # ############### Calculate word 4 + # saved r[3] + # ############### Calculate word 4 xorl %ebx,%ebx - # sqr a[3]*a[1] + # sqr a[3]*a[1] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1210,7 +1210,7 @@ L_bn_sqr_comba4_begin: adcl %edx,%ebp movl 8(%esi),%eax adcl $0,%ebx - # sqr a[2]*a[2] + # sqr a[2]*a[2] mull %eax addl %eax,%ecx adcl %edx,%ebp @@ -1218,10 +1218,10 @@ L_bn_sqr_comba4_begin: adcl $0,%ebx movl %ecx,16(%edi) movl 12(%esi),%eax - # saved r[4] - # ############### Calculate word 5 + # saved r[4] + # ############### Calculate word 5 xorl %ecx,%ecx - # sqr a[3]*a[2] + # sqr a[3]*a[2] mull %edx addl %eax,%eax adcl %edx,%edx @@ -1231,16 +1231,16 @@ L_bn_sqr_comba4_begin: movl 12(%esi),%eax adcl $0,%ecx movl %ebp,20(%edi) - # saved r[5] - # ############### Calculate word 6 + # saved r[5] + # ############### Calculate word 6 xorl %ebp,%ebp - # sqr a[3]*a[3] + # sqr a[3]*a[3] mull %eax addl %eax,%ebx adcl %edx,%ecx adcl $0,%ebp movl %ebx,24(%edi) - # saved r[6] + # saved r[6] movl %ecx,28(%edi) popl %ebx popl %ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h index e3bc6a9e02edd8..c5d780344ac85c 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:35 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s index e4f05f09e3e588..d2c370231dfa7f 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/crypt586.s @@ -9,7 +9,7 @@ L_fcrypt_body_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words xorl %edi,%edi xorl %esi,%esi call L000PIC_me_up @@ -22,7 +22,7 @@ L000PIC_me_up: pushl $25 L001start: - # Round 0 + # Round 0 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -72,7 +72,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 1 + # Round 1 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -122,7 +122,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 2 + # Round 2 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -172,7 +172,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 3 + # Round 3 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -222,7 +222,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 4 + # Round 4 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -272,7 +272,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 5 + # Round 5 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -322,7 +322,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 6 + # Round 6 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -372,7 +372,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 7 + # Round 7 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -422,7 +422,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 8 + # Round 8 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -472,7 +472,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 9 + # Round 9 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -522,7 +522,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 10 + # Round 10 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -572,7 +572,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 11 + # Round 11 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -622,7 +622,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 12 + # Round 12 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -672,7 +672,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 13 + # Round 13 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -722,7 +722,7 @@ L001start: xorl %ebx,%esi movl 32(%esp),%ebp - # Round 14 + # Round 14 movl 36(%esp),%eax movl %esi,%edx shrl $16,%edx @@ -772,7 +772,7 @@ L001start: xorl %ebx,%edi movl 32(%esp),%ebp - # Round 15 + # Round 15 movl 36(%esp),%eax movl %edi,%edx shrl $16,%edx @@ -829,7 +829,7 @@ L001start: movl %ebx,(%esp) jnz L001start - # FP + # FP movl 28(%esp),%edx rorl $1,%edi movl %esi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s index 14d61fda5f9598..5ddd0ed7311ec1 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/des/des-586.s @@ -4,7 +4,7 @@ .align 4 __x86_DES_encrypt: pushl %ecx - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -33,7 +33,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -62,7 +62,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -91,7 +91,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -120,7 +120,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -149,7 +149,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -178,7 +178,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -207,7 +207,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -236,7 +236,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -265,7 +265,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -294,7 +294,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -323,7 +323,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -352,7 +352,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -381,7 +381,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -410,7 +410,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -439,7 +439,7 @@ __x86_DES_encrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -474,7 +474,7 @@ __x86_DES_encrypt: .align 4 __x86_DES_decrypt: pushl %ecx - # Round 15 + # Round 15 movl 120(%ecx),%eax xorl %ebx,%ebx movl 124(%ecx),%edx @@ -503,7 +503,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 14 + # Round 14 movl 112(%ecx),%eax xorl %ebx,%ebx movl 116(%ecx),%edx @@ -532,7 +532,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 13 + # Round 13 movl 104(%ecx),%eax xorl %ebx,%ebx movl 108(%ecx),%edx @@ -561,7 +561,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 12 + # Round 12 movl 96(%ecx),%eax xorl %ebx,%ebx movl 100(%ecx),%edx @@ -590,7 +590,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 11 + # Round 11 movl 88(%ecx),%eax xorl %ebx,%ebx movl 92(%ecx),%edx @@ -619,7 +619,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 10 + # Round 10 movl 80(%ecx),%eax xorl %ebx,%ebx movl 84(%ecx),%edx @@ -648,7 +648,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 9 + # Round 9 movl 72(%ecx),%eax xorl %ebx,%ebx movl 76(%ecx),%edx @@ -677,7 +677,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 8 + # Round 8 movl 64(%ecx),%eax xorl %ebx,%ebx movl 68(%ecx),%edx @@ -706,7 +706,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 7 + # Round 7 movl 56(%ecx),%eax xorl %ebx,%ebx movl 60(%ecx),%edx @@ -735,7 +735,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 6 + # Round 6 movl 48(%ecx),%eax xorl %ebx,%ebx movl 52(%ecx),%edx @@ -764,7 +764,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 5 + # Round 5 movl 40(%ecx),%eax xorl %ebx,%ebx movl 44(%ecx),%edx @@ -793,7 +793,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 4 + # Round 4 movl 32(%ecx),%eax xorl %ebx,%ebx movl 36(%ecx),%edx @@ -822,7 +822,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 3 + # Round 3 movl 24(%ecx),%eax xorl %ebx,%ebx movl 28(%ecx),%edx @@ -851,7 +851,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 2 + # Round 2 movl 16(%ecx),%eax xorl %ebx,%ebx movl 20(%ecx),%edx @@ -880,7 +880,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%esi xorl 0x500(%ebp,%edx,1),%esi - # Round 1 + # Round 1 movl 8(%ecx),%eax xorl %ebx,%ebx movl 12(%ecx),%edx @@ -909,7 +909,7 @@ __x86_DES_decrypt: movl (%esp),%ecx xorl 0x400(%ebp,%eax,1),%edi xorl 0x500(%ebp,%edx,1),%edi - # Round 0 + # Round 0 movl (%ecx),%eax xorl %ebx,%ebx movl 4(%ecx),%edx @@ -948,7 +948,7 @@ L_DES_encrypt1_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%esi xorl %ecx,%ecx pushl %ebx @@ -957,7 +957,7 @@ L_DES_encrypt1_begin: movl 28(%esp),%ebx movl 4(%esi),%edi - # IP + # IP roll $4,%eax movl %eax,%esi xorl %edi,%eax @@ -1007,7 +1007,7 @@ L001decrypt: call __x86_DES_decrypt L002done: - # FP + # FP movl 20(%esp),%edx rorl $1,%esi movl %edi,%eax @@ -1060,7 +1060,7 @@ L_DES_encrypt2_begin: pushl %esi pushl %edi - # Load the 2 words + # Load the 2 words movl 12(%esp),%eax xorl %ecx,%ecx pushl %ebx @@ -1083,7 +1083,7 @@ L004decrypt: call __x86_DES_decrypt L005done: - # Fixup + # Fixup rorl $3,%edi movl 20(%esp),%eax rorl $3,%esi @@ -1105,12 +1105,12 @@ L_DES_encrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1169,7 +1169,7 @@ L_DES_encrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1225,12 +1225,12 @@ L_DES_decrypt3_begin: pushl %esi pushl %edi - # Load the data words + # Load the data words movl (%ebx),%edi movl 4(%ebx),%esi subl $12,%esp - # IP + # IP roll $4,%edi movl %edi,%edx xorl %esi,%edi @@ -1289,7 +1289,7 @@ L_DES_decrypt3_begin: movl (%ebx),%edi movl 4(%ebx),%esi - # FP + # FP roll $2,%esi roll $3,%edi movl %edi,%eax @@ -1345,7 +1345,7 @@ L_DES_ncbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 4 + # getting iv ptr from parameter 4 movl 36(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1356,11 +1356,11 @@ L_DES_ncbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 5 + # getting encrypt flag from parameter 5 movl 56(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 pushl %ecx - # get and push parameter 3 + # get and push parameter 3 movl 52(%esp),%eax pushl %eax pushl %ebx @@ -1524,7 +1524,7 @@ L_DES_ede3_cbc_encrypt_begin: pushl %esi pushl %edi movl 28(%esp),%ebp - # getting iv ptr from parameter 6 + # getting iv ptr from parameter 6 movl 44(%esp),%ebx movl (%ebx),%esi movl 4(%ebx),%edi @@ -1535,15 +1535,15 @@ L_DES_ede3_cbc_encrypt_begin: movl %esp,%ebx movl 36(%esp),%esi movl 40(%esp),%edi - # getting encrypt flag from parameter 7 + # getting encrypt flag from parameter 7 movl 64(%esp),%ecx - # get and push parameter 5 + # get and push parameter 5 movl 56(%esp),%eax pushl %eax - # get and push parameter 4 + # get and push parameter 4 movl 56(%esp),%eax pushl %eax - # get and push parameter 3 + # get and push parameter 3 movl 56(%esp),%eax pushl %eax pushl %ebx diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s index 91e941d1b41b3a..2f4efe555caebb 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/md5/md5-586.s @@ -21,10 +21,10 @@ L_md5_block_asm_data_order_begin: movl 12(%edi),%edx L000start: - # R0 section + # R0 section movl %ecx,%edi movl (%esi),%ebp - # R0 0 + # R0 0 xorl %edx,%edi andl %ebx,%edi leal 3614090360(%eax,%ebp,1),%eax @@ -34,7 +34,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 1 + # R0 1 xorl %ecx,%edi andl %eax,%edi leal 3905402710(%edx,%ebp,1),%edx @@ -44,7 +44,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 2 + # R0 2 xorl %ebx,%edi andl %edx,%edi leal 606105819(%ecx,%ebp,1),%ecx @@ -54,7 +54,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 3 + # R0 3 xorl %eax,%edi andl %ecx,%edi leal 3250441966(%ebx,%ebp,1),%ebx @@ -64,7 +64,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 4 + # R0 4 xorl %edx,%edi andl %ebx,%edi leal 4118548399(%eax,%ebp,1),%eax @@ -74,7 +74,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 5 + # R0 5 xorl %ecx,%edi andl %eax,%edi leal 1200080426(%edx,%ebp,1),%edx @@ -84,7 +84,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 6 + # R0 6 xorl %ebx,%edi andl %edx,%edi leal 2821735955(%ecx,%ebp,1),%ecx @@ -94,7 +94,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 7 + # R0 7 xorl %eax,%edi andl %ecx,%edi leal 4249261313(%ebx,%ebp,1),%ebx @@ -104,7 +104,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 8 + # R0 8 xorl %edx,%edi andl %ebx,%edi leal 1770035416(%eax,%ebp,1),%eax @@ -114,7 +114,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 9 + # R0 9 xorl %ecx,%edi andl %eax,%edi leal 2336552879(%edx,%ebp,1),%edx @@ -124,7 +124,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 10 + # R0 10 xorl %ebx,%edi andl %edx,%edi leal 4294925233(%ecx,%ebp,1),%ecx @@ -134,7 +134,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 11 + # R0 11 xorl %eax,%edi andl %ecx,%edi leal 2304563134(%ebx,%ebp,1),%ebx @@ -144,7 +144,7 @@ L000start: roll $22,%ebx movl %ecx,%edi addl %ecx,%ebx - # R0 12 + # R0 12 xorl %edx,%edi andl %ebx,%edi leal 1804603682(%eax,%ebp,1),%eax @@ -154,7 +154,7 @@ L000start: roll $7,%eax movl %ebx,%edi addl %ebx,%eax - # R0 13 + # R0 13 xorl %ecx,%edi andl %eax,%edi leal 4254626195(%edx,%ebp,1),%edx @@ -164,7 +164,7 @@ L000start: roll $12,%edx movl %eax,%edi addl %eax,%edx - # R0 14 + # R0 14 xorl %ebx,%edi andl %edx,%edi leal 2792965006(%ecx,%ebp,1),%ecx @@ -174,7 +174,7 @@ L000start: roll $17,%ecx movl %edx,%edi addl %edx,%ecx - # R0 15 + # R0 15 xorl %eax,%edi andl %ecx,%edi leal 1236535329(%ebx,%ebp,1),%ebx @@ -185,8 +185,8 @@ L000start: movl %ecx,%edi addl %ecx,%ebx - # R1 section - # R1 16 + # R1 section + # R1 16 xorl %ebx,%edi andl %edx,%edi leal 4129170786(%eax,%ebp,1),%eax @@ -196,7 +196,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 17 + # R1 17 xorl %eax,%edi andl %ecx,%edi leal 3225465664(%edx,%ebp,1),%edx @@ -206,7 +206,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 18 + # R1 18 xorl %edx,%edi andl %ebx,%edi leal 643717713(%ecx,%ebp,1),%ecx @@ -216,7 +216,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 19 + # R1 19 xorl %ecx,%edi andl %eax,%edi leal 3921069994(%ebx,%ebp,1),%ebx @@ -226,7 +226,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 20 + # R1 20 xorl %ebx,%edi andl %edx,%edi leal 3593408605(%eax,%ebp,1),%eax @@ -236,7 +236,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 21 + # R1 21 xorl %eax,%edi andl %ecx,%edi leal 38016083(%edx,%ebp,1),%edx @@ -246,7 +246,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 22 + # R1 22 xorl %edx,%edi andl %ebx,%edi leal 3634488961(%ecx,%ebp,1),%ecx @@ -256,7 +256,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 23 + # R1 23 xorl %ecx,%edi andl %eax,%edi leal 3889429448(%ebx,%ebp,1),%ebx @@ -266,7 +266,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 24 + # R1 24 xorl %ebx,%edi andl %edx,%edi leal 568446438(%eax,%ebp,1),%eax @@ -276,7 +276,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 25 + # R1 25 xorl %eax,%edi andl %ecx,%edi leal 3275163606(%edx,%ebp,1),%edx @@ -286,7 +286,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 26 + # R1 26 xorl %edx,%edi andl %ebx,%edi leal 4107603335(%ecx,%ebp,1),%ecx @@ -296,7 +296,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 27 + # R1 27 xorl %ecx,%edi andl %eax,%edi leal 1163531501(%ebx,%ebp,1),%ebx @@ -306,7 +306,7 @@ L000start: movl %ecx,%edi roll $20,%ebx addl %ecx,%ebx - # R1 28 + # R1 28 xorl %ebx,%edi andl %edx,%edi leal 2850285829(%eax,%ebp,1),%eax @@ -316,7 +316,7 @@ L000start: movl %ebx,%edi roll $5,%eax addl %ebx,%eax - # R1 29 + # R1 29 xorl %eax,%edi andl %ecx,%edi leal 4243563512(%edx,%ebp,1),%edx @@ -326,7 +326,7 @@ L000start: movl %eax,%edi roll $9,%edx addl %eax,%edx - # R1 30 + # R1 30 xorl %edx,%edi andl %ebx,%edi leal 1735328473(%ecx,%ebp,1),%ecx @@ -336,7 +336,7 @@ L000start: movl %edx,%edi roll $14,%ecx addl %edx,%ecx - # R1 31 + # R1 31 xorl %ecx,%edi andl %eax,%edi leal 2368359562(%ebx,%ebp,1),%ebx @@ -347,8 +347,8 @@ L000start: roll $20,%ebx addl %ecx,%ebx - # R2 section - # R2 32 + # R2 section + # R2 32 xorl %edx,%edi xorl %ebx,%edi leal 4294588738(%eax,%ebp,1),%eax @@ -356,7 +356,7 @@ L000start: movl 32(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 33 + # R2 33 addl %ebx,%eax xorl %ecx,%edi leal 2272392833(%edx,%ebp,1),%edx @@ -366,7 +366,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 34 + # R2 34 xorl %ebx,%edi xorl %edx,%edi leal 1839030562(%ecx,%ebp,1),%ecx @@ -374,7 +374,7 @@ L000start: movl 56(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 35 + # R2 35 addl %edx,%ecx xorl %eax,%edi leal 4259657740(%ebx,%ebp,1),%ebx @@ -384,7 +384,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 36 + # R2 36 xorl %edx,%edi xorl %ebx,%edi leal 2763975236(%eax,%ebp,1),%eax @@ -392,7 +392,7 @@ L000start: movl 16(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 37 + # R2 37 addl %ebx,%eax xorl %ecx,%edi leal 1272893353(%edx,%ebp,1),%edx @@ -402,7 +402,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 38 + # R2 38 xorl %ebx,%edi xorl %edx,%edi leal 4139469664(%ecx,%ebp,1),%ecx @@ -410,7 +410,7 @@ L000start: movl 40(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 39 + # R2 39 addl %edx,%ecx xorl %eax,%edi leal 3200236656(%ebx,%ebp,1),%ebx @@ -420,7 +420,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 40 + # R2 40 xorl %edx,%edi xorl %ebx,%edi leal 681279174(%eax,%ebp,1),%eax @@ -428,7 +428,7 @@ L000start: movl (%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 41 + # R2 41 addl %ebx,%eax xorl %ecx,%edi leal 3936430074(%edx,%ebp,1),%edx @@ -438,7 +438,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 42 + # R2 42 xorl %ebx,%edi xorl %edx,%edi leal 3572445317(%ecx,%ebp,1),%ecx @@ -446,7 +446,7 @@ L000start: movl 24(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 43 + # R2 43 addl %edx,%ecx xorl %eax,%edi leal 76029189(%ebx,%ebp,1),%ebx @@ -456,7 +456,7 @@ L000start: movl %ecx,%edi roll $23,%ebx addl %ecx,%ebx - # R2 44 + # R2 44 xorl %edx,%edi xorl %ebx,%edi leal 3654602809(%eax,%ebp,1),%eax @@ -464,7 +464,7 @@ L000start: movl 48(%esi),%ebp roll $4,%eax movl %ebx,%edi - # R2 45 + # R2 45 addl %ebx,%eax xorl %ecx,%edi leal 3873151461(%edx,%ebp,1),%edx @@ -474,7 +474,7 @@ L000start: movl %eax,%edi roll $11,%edx addl %eax,%edx - # R2 46 + # R2 46 xorl %ebx,%edi xorl %edx,%edi leal 530742520(%ecx,%ebp,1),%ecx @@ -482,7 +482,7 @@ L000start: movl 8(%esi),%ebp roll $16,%ecx movl %edx,%edi - # R2 47 + # R2 47 addl %edx,%ecx xorl %eax,%edi leal 3299628645(%ebx,%ebp,1),%ebx @@ -493,8 +493,8 @@ L000start: roll $23,%ebx addl %ecx,%ebx - # R3 section - # R3 48 + # R3 section + # R3 48 xorl %edx,%edi orl %ebx,%edi leal 4096336452(%eax,%ebp,1),%eax @@ -505,7 +505,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 49 + # R3 49 orl %eax,%edi leal 1126891415(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -515,7 +515,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 50 + # R3 50 orl %edx,%edi leal 2878612391(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -525,7 +525,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 51 + # R3 51 orl %ecx,%edi leal 4237533241(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -535,7 +535,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 52 + # R3 52 orl %ebx,%edi leal 1700485571(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -545,7 +545,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 53 + # R3 53 orl %eax,%edi leal 2399980690(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -555,7 +555,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 54 + # R3 54 orl %edx,%edi leal 4293915773(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -565,7 +565,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 55 + # R3 55 orl %ecx,%edi leal 2240044497(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -575,7 +575,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 56 + # R3 56 orl %ebx,%edi leal 1873313359(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -585,7 +585,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 57 + # R3 57 orl %eax,%edi leal 4264355552(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -595,7 +595,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 58 + # R3 58 orl %edx,%edi leal 2734768916(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -605,7 +605,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 59 + # R3 59 orl %ecx,%edi leal 1309151649(%ebx,%ebp,1),%ebx xorl %edx,%edi @@ -615,7 +615,7 @@ L000start: roll $21,%ebx xorl %edx,%edi addl %ecx,%ebx - # R3 60 + # R3 60 orl %ebx,%edi leal 4149444226(%eax,%ebp,1),%eax xorl %ecx,%edi @@ -625,7 +625,7 @@ L000start: roll $6,%eax xorl %ecx,%edi addl %ebx,%eax - # R3 61 + # R3 61 orl %eax,%edi leal 3174756917(%edx,%ebp,1),%edx xorl %ebx,%edi @@ -635,7 +635,7 @@ L000start: roll $10,%edx xorl %ebx,%edi addl %eax,%edx - # R3 62 + # R3 62 orl %edx,%edi leal 718787259(%ecx,%ebp,1),%ecx xorl %eax,%edi @@ -645,7 +645,7 @@ L000start: roll $15,%ecx xorl %eax,%edi addl %edx,%ecx - # R3 63 + # R3 63 orl %ecx,%edi leal 3951481745(%ebx,%ebp,1),%ebx xorl %edx,%edi diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s index 9484963b97fdc7..17603e38536262 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/ripemd/rmd-586.s @@ -51,7 +51,7 @@ L000start: movl %edi,%eax movl 12(%edx),%ebx movl 16(%edx),%ebp - # 0 + # 0 xorl %ebx,%eax movl (%esp),%edx xorl %esi,%eax @@ -61,7 +61,7 @@ L000start: movl %esi,%eax roll $11,%ecx addl %ebp,%ecx - # 1 + # 1 xorl %edi,%eax movl 4(%esp),%edx xorl %ecx,%eax @@ -72,7 +72,7 @@ L000start: xorl %esi,%eax roll $14,%ebp addl %ebx,%ebp - # 2 + # 2 movl 8(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -81,7 +81,7 @@ L000start: movl %ebp,%eax roll $15,%ebx addl %edi,%ebx - # 3 + # 3 xorl %ecx,%eax movl 12(%esp),%edx xorl %ebx,%eax @@ -92,7 +92,7 @@ L000start: xorl %ebp,%eax roll $12,%edi addl %esi,%edi - # 4 + # 4 movl 16(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -101,7 +101,7 @@ L000start: movl %edi,%eax roll $5,%esi addl %ecx,%esi - # 5 + # 5 xorl %ebx,%eax movl 20(%esp),%edx xorl %esi,%eax @@ -112,7 +112,7 @@ L000start: xorl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 6 + # 6 movl 24(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -121,7 +121,7 @@ L000start: movl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 7 + # 7 xorl %esi,%eax movl 28(%esp),%edx xorl %ebp,%eax @@ -132,7 +132,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 8 + # 8 movl 32(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -141,7 +141,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 9 + # 9 xorl %ebp,%eax movl 36(%esp),%edx xorl %edi,%eax @@ -152,7 +152,7 @@ L000start: xorl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 10 + # 10 movl 40(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -161,7 +161,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 11 + # 11 xorl %edi,%eax movl 44(%esp),%edx xorl %ecx,%eax @@ -172,7 +172,7 @@ L000start: xorl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 12 + # 12 movl 48(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -181,7 +181,7 @@ L000start: movl %ebp,%eax roll $6,%ebx addl %edi,%ebx - # 13 + # 13 xorl %ecx,%eax movl 52(%esp),%edx xorl %ebx,%eax @@ -192,7 +192,7 @@ L000start: xorl %ebp,%eax roll $7,%edi addl %esi,%edi - # 14 + # 14 movl 56(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -201,7 +201,7 @@ L000start: movl %edi,%eax roll $9,%esi addl %ecx,%esi - # 15 + # 15 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -212,7 +212,7 @@ L000start: movl 28(%esp),%edx roll $8,%ecx addl %ebp,%ecx - # 16 + # 16 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -225,7 +225,7 @@ L000start: movl $-1,%edx roll $7,%ebp addl %ebx,%ebp - # 17 + # 17 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -238,7 +238,7 @@ L000start: movl $-1,%eax roll $6,%ebx addl %edi,%ebx - # 18 + # 18 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -251,7 +251,7 @@ L000start: movl $-1,%edx roll $8,%edi addl %esi,%edi - # 19 + # 19 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -264,7 +264,7 @@ L000start: movl $-1,%eax roll $13,%esi addl %ecx,%esi - # 20 + # 20 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -277,7 +277,7 @@ L000start: movl $-1,%edx roll $11,%ecx addl %ebp,%ecx - # 21 + # 21 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -290,7 +290,7 @@ L000start: movl $-1,%eax roll $9,%ebp addl %ebx,%ebp - # 22 + # 22 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -303,7 +303,7 @@ L000start: movl $-1,%edx roll $7,%ebx addl %edi,%ebx - # 23 + # 23 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -316,7 +316,7 @@ L000start: movl $-1,%eax roll $15,%edi addl %esi,%edi - # 24 + # 24 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -329,7 +329,7 @@ L000start: movl $-1,%edx roll $7,%esi addl %ecx,%esi - # 25 + # 25 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -342,7 +342,7 @@ L000start: movl $-1,%eax roll $12,%ecx addl %ebp,%ecx - # 26 + # 26 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -355,7 +355,7 @@ L000start: movl $-1,%edx roll $15,%ebp addl %ebx,%ebp - # 27 + # 27 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -368,7 +368,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 28 + # 28 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -381,7 +381,7 @@ L000start: movl $-1,%edx roll $11,%edi addl %esi,%edi - # 29 + # 29 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -394,7 +394,7 @@ L000start: movl $-1,%eax roll $7,%esi addl %ecx,%esi - # 30 + # 30 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -407,7 +407,7 @@ L000start: movl $-1,%edx roll $13,%ecx addl %ebp,%ecx - # 31 + # 31 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -420,7 +420,7 @@ L000start: subl %ecx,%edx roll $12,%ebp addl %ebx,%ebp - # 32 + # 32 movl 12(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -431,7 +431,7 @@ L000start: subl %ebp,%eax roll $11,%ebx addl %edi,%ebx - # 33 + # 33 movl 40(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -442,7 +442,7 @@ L000start: subl %ebx,%edx roll $13,%edi addl %esi,%edi - # 34 + # 34 movl 56(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -453,7 +453,7 @@ L000start: subl %edi,%eax roll $6,%esi addl %ecx,%esi - # 35 + # 35 movl 16(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -464,7 +464,7 @@ L000start: subl %esi,%edx roll $7,%ecx addl %ebp,%ecx - # 36 + # 36 movl 36(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -475,7 +475,7 @@ L000start: subl %ecx,%eax roll $14,%ebp addl %ebx,%ebp - # 37 + # 37 movl 60(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -486,7 +486,7 @@ L000start: subl %ebp,%edx roll $9,%ebx addl %edi,%ebx - # 38 + # 38 movl 32(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -497,7 +497,7 @@ L000start: subl %ebx,%eax roll $13,%edi addl %esi,%edi - # 39 + # 39 movl 4(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -508,7 +508,7 @@ L000start: subl %edi,%edx roll $15,%esi addl %ecx,%esi - # 40 + # 40 movl 8(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -519,7 +519,7 @@ L000start: subl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 41 + # 41 movl 28(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -530,7 +530,7 @@ L000start: subl %ecx,%edx roll $8,%ebp addl %ebx,%ebp - # 42 + # 42 movl (%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -541,7 +541,7 @@ L000start: subl %ebp,%eax roll $13,%ebx addl %edi,%ebx - # 43 + # 43 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -552,7 +552,7 @@ L000start: subl %ebx,%edx roll $6,%edi addl %esi,%edi - # 44 + # 44 movl 52(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -563,7 +563,7 @@ L000start: subl %edi,%eax roll $5,%esi addl %ecx,%esi - # 45 + # 45 movl 44(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -574,7 +574,7 @@ L000start: subl %esi,%edx roll $12,%ecx addl %ebp,%ecx - # 46 + # 46 movl 20(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -585,7 +585,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 47 + # 47 movl 48(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -596,7 +596,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 48 + # 48 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -609,7 +609,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 49 + # 49 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -622,7 +622,7 @@ L000start: movl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 50 + # 50 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -635,7 +635,7 @@ L000start: movl %edi,%eax roll $14,%ecx addl %ebp,%ecx - # 51 + # 51 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -648,7 +648,7 @@ L000start: movl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 52 + # 52 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -661,7 +661,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 53 + # 53 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -674,7 +674,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 54 + # 54 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -687,7 +687,7 @@ L000start: movl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 55 + # 55 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -700,7 +700,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 56 + # 56 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -713,7 +713,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 57 + # 57 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -726,7 +726,7 @@ L000start: movl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 58 + # 58 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -739,7 +739,7 @@ L000start: movl %ebp,%eax roll $5,%edi addl %esi,%edi - # 59 + # 59 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -752,7 +752,7 @@ L000start: movl %ebx,%eax roll $6,%esi addl %ecx,%esi - # 60 + # 60 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -765,7 +765,7 @@ L000start: movl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 61 + # 61 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -778,7 +778,7 @@ L000start: movl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 62 + # 62 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -791,7 +791,7 @@ L000start: movl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 63 + # 63 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -804,7 +804,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 64 + # 64 movl 16(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -815,7 +815,7 @@ L000start: subl %ebx,%eax roll $9,%esi addl %ecx,%esi - # 65 + # 65 movl (%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -826,7 +826,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 66 + # 66 movl 20(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -837,7 +837,7 @@ L000start: subl %esi,%eax roll $5,%ebp addl %ebx,%ebp - # 67 + # 67 movl 36(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -848,7 +848,7 @@ L000start: subl %ecx,%edx roll $11,%ebx addl %edi,%ebx - # 68 + # 68 movl 28(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -859,7 +859,7 @@ L000start: subl %ebp,%eax roll $6,%edi addl %esi,%edi - # 69 + # 69 movl 48(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -870,7 +870,7 @@ L000start: subl %ebx,%edx roll $8,%esi addl %ecx,%esi - # 70 + # 70 movl 8(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -881,7 +881,7 @@ L000start: subl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 71 + # 71 movl 40(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -892,7 +892,7 @@ L000start: subl %esi,%edx roll $12,%ebp addl %ebx,%ebp - # 72 + # 72 movl 56(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -903,7 +903,7 @@ L000start: subl %ecx,%eax roll $5,%ebx addl %edi,%ebx - # 73 + # 73 movl 4(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -914,7 +914,7 @@ L000start: subl %ebp,%edx roll $12,%edi addl %esi,%edi - # 74 + # 74 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -925,7 +925,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 75 + # 75 movl 32(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -936,7 +936,7 @@ L000start: subl %edi,%edx roll $14,%ecx addl %ebp,%ecx - # 76 + # 76 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -947,7 +947,7 @@ L000start: subl %esi,%eax roll $11,%ebp addl %ebx,%ebp - # 77 + # 77 movl 24(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -958,7 +958,7 @@ L000start: subl %ecx,%edx roll $8,%ebx addl %edi,%ebx - # 78 + # 78 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -969,7 +969,7 @@ L000start: subl %ebp,%eax roll $5,%edi addl %esi,%edi - # 79 + # 79 movl 52(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -989,7 +989,7 @@ L000start: movl %ebp,80(%esp) movl 12(%edx),%ebx movl 16(%edx),%ebp - # 80 + # 80 movl $-1,%edx subl %ebx,%edx movl 20(%esp),%eax @@ -1002,7 +1002,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 81 + # 81 movl 56(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1013,7 +1013,7 @@ L000start: subl %esi,%edx roll $9,%ebp addl %ebx,%ebp - # 82 + # 82 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1024,7 +1024,7 @@ L000start: subl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 83 + # 83 movl (%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1035,7 +1035,7 @@ L000start: subl %ebp,%edx roll $11,%edi addl %esi,%edi - # 84 + # 84 movl 36(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1046,7 +1046,7 @@ L000start: subl %ebx,%eax roll $13,%esi addl %ecx,%esi - # 85 + # 85 movl 8(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1057,7 +1057,7 @@ L000start: subl %edi,%edx roll $15,%ecx addl %ebp,%ecx - # 86 + # 86 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ebp @@ -1068,7 +1068,7 @@ L000start: subl %esi,%eax roll $15,%ebp addl %ebx,%ebp - # 87 + # 87 movl 16(%esp),%edx orl %ecx,%eax addl %edx,%ebx @@ -1079,7 +1079,7 @@ L000start: subl %ecx,%edx roll $5,%ebx addl %edi,%ebx - # 88 + # 88 movl 52(%esp),%eax orl %ebp,%edx addl %eax,%edi @@ -1090,7 +1090,7 @@ L000start: subl %ebp,%eax roll $7,%edi addl %esi,%edi - # 89 + # 89 movl 24(%esp),%edx orl %ebx,%eax addl %edx,%esi @@ -1101,7 +1101,7 @@ L000start: subl %ebx,%edx roll $7,%esi addl %ecx,%esi - # 90 + # 90 movl 60(%esp),%eax orl %edi,%edx addl %eax,%ecx @@ -1112,7 +1112,7 @@ L000start: subl %edi,%eax roll $8,%ecx addl %ebp,%ecx - # 91 + # 91 movl 32(%esp),%edx orl %esi,%eax addl %edx,%ebp @@ -1123,7 +1123,7 @@ L000start: subl %esi,%edx roll $11,%ebp addl %ebx,%ebp - # 92 + # 92 movl 4(%esp),%eax orl %ecx,%edx addl %eax,%ebx @@ -1134,7 +1134,7 @@ L000start: subl %ecx,%eax roll $14,%ebx addl %edi,%ebx - # 93 + # 93 movl 40(%esp),%edx orl %ebp,%eax addl %edx,%edi @@ -1145,7 +1145,7 @@ L000start: subl %ebp,%edx roll $14,%edi addl %esi,%edi - # 94 + # 94 movl 12(%esp),%eax orl %ebx,%edx addl %eax,%esi @@ -1156,7 +1156,7 @@ L000start: subl %ebx,%eax roll $12,%esi addl %ecx,%esi - # 95 + # 95 movl 48(%esp),%edx orl %edi,%eax addl %edx,%ecx @@ -1167,7 +1167,7 @@ L000start: movl %edi,%eax roll $6,%ecx addl %ebp,%ecx - # 96 + # 96 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1180,7 +1180,7 @@ L000start: movl %esi,%eax roll $9,%ebp addl %ebx,%ebp - # 97 + # 97 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1193,7 +1193,7 @@ L000start: movl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 98 + # 98 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1206,7 +1206,7 @@ L000start: movl %ebp,%eax roll $15,%edi addl %esi,%edi - # 99 + # 99 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1219,7 +1219,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 100 + # 100 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1232,7 +1232,7 @@ L000start: movl %edi,%eax roll $12,%ecx addl %ebp,%ecx - # 101 + # 101 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1245,7 +1245,7 @@ L000start: movl %esi,%eax roll $8,%ebp addl %ebx,%ebp - # 102 + # 102 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1258,7 +1258,7 @@ L000start: movl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 103 + # 103 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1271,7 +1271,7 @@ L000start: movl %ebp,%eax roll $11,%edi addl %esi,%edi - # 104 + # 104 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1284,7 +1284,7 @@ L000start: movl %ebx,%eax roll $7,%esi addl %ecx,%esi - # 105 + # 105 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1297,7 +1297,7 @@ L000start: movl %edi,%eax roll $7,%ecx addl %ebp,%ecx - # 106 + # 106 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1310,7 +1310,7 @@ L000start: movl %esi,%eax roll $12,%ebp addl %ebx,%ebp - # 107 + # 107 subl %esi,%edx andl %ebp,%eax andl %ecx,%edx @@ -1323,7 +1323,7 @@ L000start: movl %ecx,%eax roll $7,%ebx addl %edi,%ebx - # 108 + # 108 subl %ecx,%edx andl %ebx,%eax andl %ebp,%edx @@ -1336,7 +1336,7 @@ L000start: movl %ebp,%eax roll $6,%edi addl %esi,%edi - # 109 + # 109 subl %ebp,%edx andl %edi,%eax andl %ebx,%edx @@ -1349,7 +1349,7 @@ L000start: movl %ebx,%eax roll $15,%esi addl %ecx,%esi - # 110 + # 110 subl %ebx,%edx andl %esi,%eax andl %edi,%edx @@ -1362,7 +1362,7 @@ L000start: movl %edi,%eax roll $13,%ecx addl %ebp,%ecx - # 111 + # 111 subl %edi,%edx andl %ecx,%eax andl %esi,%edx @@ -1375,7 +1375,7 @@ L000start: subl %ecx,%edx roll $11,%ebp addl %ebx,%ebp - # 112 + # 112 movl 60(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1386,7 +1386,7 @@ L000start: subl %ebp,%eax roll $9,%ebx addl %edi,%ebx - # 113 + # 113 movl 20(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1397,7 +1397,7 @@ L000start: subl %ebx,%edx roll $7,%edi addl %esi,%edi - # 114 + # 114 movl 4(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1408,7 +1408,7 @@ L000start: subl %edi,%eax roll $15,%esi addl %ecx,%esi - # 115 + # 115 movl 12(%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1419,7 +1419,7 @@ L000start: subl %esi,%edx roll $11,%ecx addl %ebp,%ecx - # 116 + # 116 movl 28(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1430,7 +1430,7 @@ L000start: subl %ecx,%eax roll $8,%ebp addl %ebx,%ebp - # 117 + # 117 movl 56(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1441,7 +1441,7 @@ L000start: subl %ebp,%edx roll $6,%ebx addl %edi,%ebx - # 118 + # 118 movl 24(%esp),%eax orl %ebx,%edx addl %eax,%edi @@ -1452,7 +1452,7 @@ L000start: subl %ebx,%eax roll $6,%edi addl %esi,%edi - # 119 + # 119 movl 36(%esp),%edx orl %edi,%eax addl %edx,%esi @@ -1463,7 +1463,7 @@ L000start: subl %edi,%edx roll $14,%esi addl %ecx,%esi - # 120 + # 120 movl 44(%esp),%eax orl %esi,%edx addl %eax,%ecx @@ -1474,7 +1474,7 @@ L000start: subl %esi,%eax roll $12,%ecx addl %ebp,%ecx - # 121 + # 121 movl 32(%esp),%edx orl %ecx,%eax addl %edx,%ebp @@ -1485,7 +1485,7 @@ L000start: subl %ecx,%edx roll $13,%ebp addl %ebx,%ebp - # 122 + # 122 movl 48(%esp),%eax orl %ebp,%edx addl %eax,%ebx @@ -1496,7 +1496,7 @@ L000start: subl %ebp,%eax roll $5,%ebx addl %edi,%ebx - # 123 + # 123 movl 8(%esp),%edx orl %ebx,%eax addl %edx,%edi @@ -1507,7 +1507,7 @@ L000start: subl %ebx,%edx roll $14,%edi addl %esi,%edi - # 124 + # 124 movl 40(%esp),%eax orl %edi,%edx addl %eax,%esi @@ -1518,7 +1518,7 @@ L000start: subl %edi,%eax roll $13,%esi addl %ecx,%esi - # 125 + # 125 movl (%esp),%edx orl %esi,%eax addl %edx,%ecx @@ -1529,7 +1529,7 @@ L000start: subl %esi,%edx roll $13,%ecx addl %ebp,%ecx - # 126 + # 126 movl 16(%esp),%eax orl %ecx,%edx addl %eax,%ebp @@ -1540,7 +1540,7 @@ L000start: subl %ecx,%eax roll $7,%ebp addl %ebx,%ebp - # 127 + # 127 movl 52(%esp),%edx orl %ebp,%eax addl %edx,%ebx @@ -1551,7 +1551,7 @@ L000start: movl $-1,%eax roll $5,%ebx addl %edi,%ebx - # 128 + # 128 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1564,7 +1564,7 @@ L000start: movl $-1,%edx roll $15,%edi addl %esi,%edi - # 129 + # 129 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1577,7 +1577,7 @@ L000start: movl $-1,%eax roll $5,%esi addl %ecx,%esi - # 130 + # 130 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1590,7 +1590,7 @@ L000start: movl $-1,%edx roll $8,%ecx addl %ebp,%ecx - # 131 + # 131 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1603,7 +1603,7 @@ L000start: movl $-1,%eax roll $11,%ebp addl %ebx,%ebp - # 132 + # 132 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1616,7 +1616,7 @@ L000start: movl $-1,%edx roll $14,%ebx addl %edi,%ebx - # 133 + # 133 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1629,7 +1629,7 @@ L000start: movl $-1,%eax roll $14,%edi addl %esi,%edi - # 134 + # 134 addl %edx,%esi movl %ebx,%edx subl %edi,%eax @@ -1642,7 +1642,7 @@ L000start: movl $-1,%edx roll $6,%esi addl %ecx,%esi - # 135 + # 135 addl %eax,%ecx movl %edi,%eax subl %esi,%edx @@ -1655,7 +1655,7 @@ L000start: movl $-1,%eax roll $14,%ecx addl %ebp,%ecx - # 136 + # 136 addl %edx,%ebp movl %esi,%edx subl %ecx,%eax @@ -1668,7 +1668,7 @@ L000start: movl $-1,%edx roll $6,%ebp addl %ebx,%ebp - # 137 + # 137 addl %eax,%ebx movl %ecx,%eax subl %ebp,%edx @@ -1681,7 +1681,7 @@ L000start: movl $-1,%eax roll $9,%ebx addl %edi,%ebx - # 138 + # 138 addl %edx,%edi movl %ebp,%edx subl %ebx,%eax @@ -1694,7 +1694,7 @@ L000start: movl $-1,%edx roll $12,%edi addl %esi,%edi - # 139 + # 139 addl %eax,%esi movl %ebx,%eax subl %edi,%edx @@ -1707,7 +1707,7 @@ L000start: movl $-1,%eax roll $9,%esi addl %ecx,%esi - # 140 + # 140 addl %edx,%ecx movl %edi,%edx subl %esi,%eax @@ -1720,7 +1720,7 @@ L000start: movl $-1,%edx roll $12,%ecx addl %ebp,%ecx - # 141 + # 141 addl %eax,%ebp movl %esi,%eax subl %ecx,%edx @@ -1733,7 +1733,7 @@ L000start: movl $-1,%eax roll $5,%ebp addl %ebx,%ebp - # 142 + # 142 addl %edx,%ebx movl %ecx,%edx subl %ebp,%eax @@ -1746,7 +1746,7 @@ L000start: movl $-1,%edx roll $15,%ebx addl %edi,%ebx - # 143 + # 143 addl %eax,%edi movl %ebp,%eax subl %ebx,%edx @@ -1759,7 +1759,7 @@ L000start: xorl %ebp,%eax roll $8,%edi addl %esi,%edi - # 144 + # 144 movl 48(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1768,7 +1768,7 @@ L000start: movl %edi,%eax roll $8,%esi addl %ecx,%esi - # 145 + # 145 xorl %ebx,%eax movl 60(%esp),%edx xorl %esi,%eax @@ -1779,7 +1779,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 146 + # 146 movl 40(%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1788,7 +1788,7 @@ L000start: movl %ecx,%eax roll $12,%ebp addl %ebx,%ebp - # 147 + # 147 xorl %esi,%eax movl 16(%esp),%edx xorl %ebp,%eax @@ -1799,7 +1799,7 @@ L000start: xorl %ecx,%eax roll $9,%ebx addl %edi,%ebx - # 148 + # 148 movl 4(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1808,7 +1808,7 @@ L000start: movl %ebx,%eax roll $12,%edi addl %esi,%edi - # 149 + # 149 xorl %ebp,%eax movl 20(%esp),%edx xorl %edi,%eax @@ -1819,7 +1819,7 @@ L000start: xorl %ebx,%eax roll $5,%esi addl %ecx,%esi - # 150 + # 150 movl 32(%esp),%edx xorl %esi,%eax addl %edx,%ecx @@ -1828,7 +1828,7 @@ L000start: movl %esi,%eax roll $14,%ecx addl %ebp,%ecx - # 151 + # 151 xorl %edi,%eax movl 28(%esp),%edx xorl %ecx,%eax @@ -1839,7 +1839,7 @@ L000start: xorl %esi,%eax roll $6,%ebp addl %ebx,%ebp - # 152 + # 152 movl 24(%esp),%edx xorl %ebp,%eax addl %edx,%ebx @@ -1848,7 +1848,7 @@ L000start: movl %ebp,%eax roll $8,%ebx addl %edi,%ebx - # 153 + # 153 xorl %ecx,%eax movl 8(%esp),%edx xorl %ebx,%eax @@ -1859,7 +1859,7 @@ L000start: xorl %ebp,%eax roll $13,%edi addl %esi,%edi - # 154 + # 154 movl 52(%esp),%edx xorl %edi,%eax addl %edx,%esi @@ -1868,7 +1868,7 @@ L000start: movl %edi,%eax roll $6,%esi addl %ecx,%esi - # 155 + # 155 xorl %ebx,%eax movl 56(%esp),%edx xorl %esi,%eax @@ -1879,7 +1879,7 @@ L000start: xorl %edi,%eax roll $5,%ecx addl %ebp,%ecx - # 156 + # 156 movl (%esp),%edx xorl %ecx,%eax addl %edx,%ebp @@ -1888,7 +1888,7 @@ L000start: movl %ecx,%eax roll $15,%ebp addl %ebx,%ebp - # 157 + # 157 xorl %esi,%eax movl 12(%esp),%edx xorl %ebp,%eax @@ -1899,7 +1899,7 @@ L000start: xorl %ecx,%eax roll $13,%ebx addl %edi,%ebx - # 158 + # 158 movl 36(%esp),%edx xorl %ebx,%eax addl %edx,%edi @@ -1908,7 +1908,7 @@ L000start: movl %ebx,%eax roll $11,%edi addl %esi,%edi - # 159 + # 159 xorl %ebp,%eax movl 44(%esp),%edx xorl %edi,%eax diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s index 48860a65b70a61..42b2788199f6dc 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/crypto/sha/sha1-586.s @@ -95,7 +95,7 @@ L002loop: movl 4(%ebp),%ebx movl 8(%ebp),%ecx movl 12(%ebp),%edx - # 00_15 0 + # 00_15 0 movl %ecx,%esi movl %eax,%ebp roll $5,%ebp @@ -107,7 +107,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 1 + # 00_15 1 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -119,7 +119,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 2 + # 00_15 2 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -131,7 +131,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 3 + # 00_15 3 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -143,7 +143,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 4 + # 00_15 4 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -155,7 +155,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 5 + # 00_15 5 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -167,7 +167,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 6 + # 00_15 6 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -179,7 +179,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 7 + # 00_15 7 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -191,7 +191,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 8 + # 00_15 8 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -203,7 +203,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 9 + # 00_15 9 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -215,7 +215,7 @@ L002loop: xorl %eax,%ecx leal 1518500249(%ebp,%ebx,1),%ebp addl %ecx,%ebp - # 00_15 10 + # 00_15 10 movl %edi,%ebx movl %ebp,%ecx roll $5,%ebp @@ -227,7 +227,7 @@ L002loop: xorl %esi,%ebx leal 1518500249(%ebp,%eax,1),%ebp addl %ebx,%ebp - # 00_15 11 + # 00_15 11 movl %edx,%eax movl %ebp,%ebx roll $5,%ebp @@ -239,7 +239,7 @@ L002loop: xorl %edi,%eax leal 1518500249(%ebp,%esi,1),%ebp addl %eax,%ebp - # 00_15 12 + # 00_15 12 movl %ecx,%esi movl %ebp,%eax roll $5,%ebp @@ -251,7 +251,7 @@ L002loop: xorl %edx,%esi leal 1518500249(%ebp,%edi,1),%ebp addl %esi,%ebp - # 00_15 13 + # 00_15 13 movl %ebx,%edi movl %ebp,%esi roll $5,%ebp @@ -263,7 +263,7 @@ L002loop: xorl %ecx,%edi leal 1518500249(%ebp,%edx,1),%ebp addl %edi,%ebp - # 00_15 14 + # 00_15 14 movl %eax,%edx movl %ebp,%edi roll $5,%ebp @@ -275,7 +275,7 @@ L002loop: xorl %ebx,%edx leal 1518500249(%ebp,%ecx,1),%ebp addl %edx,%ebp - # 00_15 15 + # 00_15 15 movl %esi,%ecx movl %ebp,%edx roll $5,%ebp @@ -288,7 +288,7 @@ L002loop: leal 1518500249(%ebp,%ebx,1),%ebp movl (%esp),%ebx addl %ebp,%ecx - # 16_19 16 + # 16_19 16 movl %edi,%ebp xorl 8(%esp),%ebx xorl %esi,%ebp @@ -305,7 +305,7 @@ L002loop: leal 1518500249(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 16_19 17 + # 16_19 17 movl %edx,%ebp xorl 12(%esp),%eax xorl %edi,%ebp @@ -322,7 +322,7 @@ L002loop: leal 1518500249(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 16_19 18 + # 16_19 18 movl %ecx,%ebp xorl 16(%esp),%esi xorl %edx,%ebp @@ -339,7 +339,7 @@ L002loop: leal 1518500249(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 16_19 19 + # 16_19 19 movl %ebx,%ebp xorl 20(%esp),%edi xorl %ecx,%ebp @@ -356,7 +356,7 @@ L002loop: leal 1518500249(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 20 + # 20_39 20 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -372,7 +372,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 21 + # 20_39 21 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -388,7 +388,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 22 + # 20_39 22 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -404,7 +404,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 23 + # 20_39 23 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -420,7 +420,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 24 + # 20_39 24 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -436,7 +436,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 25 + # 20_39 25 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -452,7 +452,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 26 + # 20_39 26 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -468,7 +468,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 27 + # 20_39 27 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -484,7 +484,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 28 + # 20_39 28 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -500,7 +500,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 29 + # 20_39 29 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -516,7 +516,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 30 + # 20_39 30 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -532,7 +532,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 31 + # 20_39 31 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp @@ -548,7 +548,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl (%esp),%edx addl %ebp,%edi - # 20_39 32 + # 20_39 32 movl %esi,%ebp xorl 8(%esp),%edx xorl %eax,%ebp @@ -564,7 +564,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 4(%esp),%ecx addl %ebp,%edx - # 20_39 33 + # 20_39 33 movl %edi,%ebp xorl 12(%esp),%ecx xorl %esi,%ebp @@ -580,7 +580,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 8(%esp),%ebx addl %ebp,%ecx - # 20_39 34 + # 20_39 34 movl %edx,%ebp xorl 16(%esp),%ebx xorl %edi,%ebp @@ -596,7 +596,7 @@ L002loop: leal 1859775393(%ebx,%eax,1),%ebx movl 12(%esp),%eax addl %ebp,%ebx - # 20_39 35 + # 20_39 35 movl %ecx,%ebp xorl 20(%esp),%eax xorl %edx,%ebp @@ -612,7 +612,7 @@ L002loop: leal 1859775393(%eax,%esi,1),%eax movl 16(%esp),%esi addl %ebp,%eax - # 20_39 36 + # 20_39 36 movl %ebx,%ebp xorl 24(%esp),%esi xorl %ecx,%ebp @@ -628,7 +628,7 @@ L002loop: leal 1859775393(%esi,%edi,1),%esi movl 20(%esp),%edi addl %ebp,%esi - # 20_39 37 + # 20_39 37 movl %eax,%ebp xorl 28(%esp),%edi xorl %ebx,%ebp @@ -644,7 +644,7 @@ L002loop: leal 1859775393(%edi,%edx,1),%edi movl 24(%esp),%edx addl %ebp,%edi - # 20_39 38 + # 20_39 38 movl %esi,%ebp xorl 32(%esp),%edx xorl %eax,%ebp @@ -660,7 +660,7 @@ L002loop: leal 1859775393(%edx,%ecx,1),%edx movl 28(%esp),%ecx addl %ebp,%edx - # 20_39 39 + # 20_39 39 movl %edi,%ebp xorl 36(%esp),%ecx xorl %esi,%ebp @@ -676,7 +676,7 @@ L002loop: leal 1859775393(%ecx,%ebx,1),%ecx movl 32(%esp),%ebx addl %ebp,%ecx - # 40_59 40 + # 40_59 40 movl %edi,%ebp xorl 40(%esp),%ebx xorl %esi,%ebp @@ -695,7 +695,7 @@ L002loop: andl %esi,%ebp movl 36(%esp),%eax addl %ebp,%ebx - # 40_59 41 + # 40_59 41 movl %edx,%ebp xorl 44(%esp),%eax xorl %edi,%ebp @@ -714,7 +714,7 @@ L002loop: andl %edi,%ebp movl 40(%esp),%esi addl %ebp,%eax - # 40_59 42 + # 40_59 42 movl %ecx,%ebp xorl 48(%esp),%esi xorl %edx,%ebp @@ -733,7 +733,7 @@ L002loop: andl %edx,%ebp movl 44(%esp),%edi addl %ebp,%esi - # 40_59 43 + # 40_59 43 movl %ebx,%ebp xorl 52(%esp),%edi xorl %ecx,%ebp @@ -752,7 +752,7 @@ L002loop: andl %ecx,%ebp movl 48(%esp),%edx addl %ebp,%edi - # 40_59 44 + # 40_59 44 movl %eax,%ebp xorl 56(%esp),%edx xorl %ebx,%ebp @@ -771,7 +771,7 @@ L002loop: andl %ebx,%ebp movl 52(%esp),%ecx addl %ebp,%edx - # 40_59 45 + # 40_59 45 movl %esi,%ebp xorl 60(%esp),%ecx xorl %eax,%ebp @@ -790,7 +790,7 @@ L002loop: andl %eax,%ebp movl 56(%esp),%ebx addl %ebp,%ecx - # 40_59 46 + # 40_59 46 movl %edi,%ebp xorl (%esp),%ebx xorl %esi,%ebp @@ -809,7 +809,7 @@ L002loop: andl %esi,%ebp movl 60(%esp),%eax addl %ebp,%ebx - # 40_59 47 + # 40_59 47 movl %edx,%ebp xorl 4(%esp),%eax xorl %edi,%ebp @@ -828,7 +828,7 @@ L002loop: andl %edi,%ebp movl (%esp),%esi addl %ebp,%eax - # 40_59 48 + # 40_59 48 movl %ecx,%ebp xorl 8(%esp),%esi xorl %edx,%ebp @@ -847,7 +847,7 @@ L002loop: andl %edx,%ebp movl 4(%esp),%edi addl %ebp,%esi - # 40_59 49 + # 40_59 49 movl %ebx,%ebp xorl 12(%esp),%edi xorl %ecx,%ebp @@ -866,7 +866,7 @@ L002loop: andl %ecx,%ebp movl 8(%esp),%edx addl %ebp,%edi - # 40_59 50 + # 40_59 50 movl %eax,%ebp xorl 16(%esp),%edx xorl %ebx,%ebp @@ -885,7 +885,7 @@ L002loop: andl %ebx,%ebp movl 12(%esp),%ecx addl %ebp,%edx - # 40_59 51 + # 40_59 51 movl %esi,%ebp xorl 20(%esp),%ecx xorl %eax,%ebp @@ -904,7 +904,7 @@ L002loop: andl %eax,%ebp movl 16(%esp),%ebx addl %ebp,%ecx - # 40_59 52 + # 40_59 52 movl %edi,%ebp xorl 24(%esp),%ebx xorl %esi,%ebp @@ -923,7 +923,7 @@ L002loop: andl %esi,%ebp movl 20(%esp),%eax addl %ebp,%ebx - # 40_59 53 + # 40_59 53 movl %edx,%ebp xorl 28(%esp),%eax xorl %edi,%ebp @@ -942,7 +942,7 @@ L002loop: andl %edi,%ebp movl 24(%esp),%esi addl %ebp,%eax - # 40_59 54 + # 40_59 54 movl %ecx,%ebp xorl 32(%esp),%esi xorl %edx,%ebp @@ -961,7 +961,7 @@ L002loop: andl %edx,%ebp movl 28(%esp),%edi addl %ebp,%esi - # 40_59 55 + # 40_59 55 movl %ebx,%ebp xorl 36(%esp),%edi xorl %ecx,%ebp @@ -980,7 +980,7 @@ L002loop: andl %ecx,%ebp movl 32(%esp),%edx addl %ebp,%edi - # 40_59 56 + # 40_59 56 movl %eax,%ebp xorl 40(%esp),%edx xorl %ebx,%ebp @@ -999,7 +999,7 @@ L002loop: andl %ebx,%ebp movl 36(%esp),%ecx addl %ebp,%edx - # 40_59 57 + # 40_59 57 movl %esi,%ebp xorl 44(%esp),%ecx xorl %eax,%ebp @@ -1018,7 +1018,7 @@ L002loop: andl %eax,%ebp movl 40(%esp),%ebx addl %ebp,%ecx - # 40_59 58 + # 40_59 58 movl %edi,%ebp xorl 48(%esp),%ebx xorl %esi,%ebp @@ -1037,7 +1037,7 @@ L002loop: andl %esi,%ebp movl 44(%esp),%eax addl %ebp,%ebx - # 40_59 59 + # 40_59 59 movl %edx,%ebp xorl 52(%esp),%eax xorl %edi,%ebp @@ -1056,7 +1056,7 @@ L002loop: andl %edi,%ebp movl 48(%esp),%esi addl %ebp,%eax - # 20_39 60 + # 20_39 60 movl %ebx,%ebp xorl 56(%esp),%esi xorl %ecx,%ebp @@ -1072,7 +1072,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 52(%esp),%edi addl %ebp,%esi - # 20_39 61 + # 20_39 61 movl %eax,%ebp xorl 60(%esp),%edi xorl %ebx,%ebp @@ -1088,7 +1088,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 56(%esp),%edx addl %ebp,%edi - # 20_39 62 + # 20_39 62 movl %esi,%ebp xorl (%esp),%edx xorl %eax,%ebp @@ -1104,7 +1104,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 60(%esp),%ecx addl %ebp,%edx - # 20_39 63 + # 20_39 63 movl %edi,%ebp xorl 4(%esp),%ecx xorl %esi,%ebp @@ -1120,7 +1120,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl (%esp),%ebx addl %ebp,%ecx - # 20_39 64 + # 20_39 64 movl %edx,%ebp xorl 8(%esp),%ebx xorl %edi,%ebp @@ -1136,7 +1136,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 4(%esp),%eax addl %ebp,%ebx - # 20_39 65 + # 20_39 65 movl %ecx,%ebp xorl 12(%esp),%eax xorl %edx,%ebp @@ -1152,7 +1152,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 8(%esp),%esi addl %ebp,%eax - # 20_39 66 + # 20_39 66 movl %ebx,%ebp xorl 16(%esp),%esi xorl %ecx,%ebp @@ -1168,7 +1168,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 12(%esp),%edi addl %ebp,%esi - # 20_39 67 + # 20_39 67 movl %eax,%ebp xorl 20(%esp),%edi xorl %ebx,%ebp @@ -1184,7 +1184,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 16(%esp),%edx addl %ebp,%edi - # 20_39 68 + # 20_39 68 movl %esi,%ebp xorl 24(%esp),%edx xorl %eax,%ebp @@ -1200,7 +1200,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 20(%esp),%ecx addl %ebp,%edx - # 20_39 69 + # 20_39 69 movl %edi,%ebp xorl 28(%esp),%ecx xorl %esi,%ebp @@ -1216,7 +1216,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 24(%esp),%ebx addl %ebp,%ecx - # 20_39 70 + # 20_39 70 movl %edx,%ebp xorl 32(%esp),%ebx xorl %edi,%ebp @@ -1232,7 +1232,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 28(%esp),%eax addl %ebp,%ebx - # 20_39 71 + # 20_39 71 movl %ecx,%ebp xorl 36(%esp),%eax xorl %edx,%ebp @@ -1248,7 +1248,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 32(%esp),%esi addl %ebp,%eax - # 20_39 72 + # 20_39 72 movl %ebx,%ebp xorl 40(%esp),%esi xorl %ecx,%ebp @@ -1264,7 +1264,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 36(%esp),%edi addl %ebp,%esi - # 20_39 73 + # 20_39 73 movl %eax,%ebp xorl 44(%esp),%edi xorl %ebx,%ebp @@ -1280,7 +1280,7 @@ L002loop: leal 3395469782(%edi,%edx,1),%edi movl 40(%esp),%edx addl %ebp,%edi - # 20_39 74 + # 20_39 74 movl %esi,%ebp xorl 48(%esp),%edx xorl %eax,%ebp @@ -1296,7 +1296,7 @@ L002loop: leal 3395469782(%edx,%ecx,1),%edx movl 44(%esp),%ecx addl %ebp,%edx - # 20_39 75 + # 20_39 75 movl %edi,%ebp xorl 52(%esp),%ecx xorl %esi,%ebp @@ -1312,7 +1312,7 @@ L002loop: leal 3395469782(%ecx,%ebx,1),%ecx movl 48(%esp),%ebx addl %ebp,%ecx - # 20_39 76 + # 20_39 76 movl %edx,%ebp xorl 56(%esp),%ebx xorl %edi,%ebp @@ -1328,7 +1328,7 @@ L002loop: leal 3395469782(%ebx,%eax,1),%ebx movl 52(%esp),%eax addl %ebp,%ebx - # 20_39 77 + # 20_39 77 movl %ecx,%ebp xorl 60(%esp),%eax xorl %edx,%ebp @@ -1343,7 +1343,7 @@ L002loop: leal 3395469782(%eax,%esi,1),%eax movl 56(%esp),%esi addl %ebp,%eax - # 20_39 78 + # 20_39 78 movl %ebx,%ebp xorl (%esp),%esi xorl %ecx,%ebp @@ -1358,7 +1358,7 @@ L002loop: leal 3395469782(%esi,%edi,1),%esi movl 60(%esp),%edi addl %ebp,%esi - # 20_39 79 + # 20_39 79 movl %eax,%ebp xorl 4(%esp),%edi xorl %ebx,%ebp diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h b/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi b/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi index 0645934ea717a8..0d74f2cfcc8f66 100644 --- a/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm b/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm index c406c0cc9873b8..18ca9db55ad5ce 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "BSD-x86" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9077,6 +9080,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9142,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13855,6 +13868,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13868,6 +13882,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13975,6 +13990,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14027,6 +14046,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15931,3 +15954,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h index c1c537b3fb0604..caec1eb1bfdfd4 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86" -#define DATE "built on: Fri Sep 13 15:57:29 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:38 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h b/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi index 004701ad37a61d..a91d1a33e59103 100644 --- a/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm index 958b30cc6a1612..e5c5c6cf79fd74 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3188,6 +3189,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3238,6 +3240,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9274,6 +9277,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9339,6 +9347,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14167,6 +14180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14180,6 +14194,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14287,6 +14302,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14339,6 +14358,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h index 3eb8d7ca6bad4a..691147c977d4ed 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:57:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:40 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi index 08b438900a5857..c1d3e86d5f0e55 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm index bf9459acade3c8..895915b7f1b7ee 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3188,6 +3189,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3238,6 +3240,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9274,6 +9277,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9339,6 +9347,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14167,6 +14180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14180,6 +14194,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14287,6 +14302,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14339,6 +14358,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h index c9ffd0c3c093bf..2c69f50e9139d3 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:57:48 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:49 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi index e9ccff96476c79..ff8709f91fc26e 100644 --- a/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm b/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm index 84e3b187535590..afe466409f85df 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "BSD-x86_64" ], perlenv => { "AR" => undef, @@ -265,6 +265,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3150,6 +3151,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3200,6 +3202,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9098,6 +9101,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9163,6 +9171,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13876,6 +13889,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13889,6 +13903,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13996,6 +14011,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14048,6 +14067,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h index 7702df5969293b..77b91b36b42dcb 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: BSD-x86_64" -#define DATE "built on: Fri Sep 13 15:58:03 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:57 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h b/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi b/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi index cbd749521e3dac..280cabe538963b 100644 --- a/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/BSD-x86_64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm b/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm index ecd5ebea5fdb19..b217afb0b75bc3 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/asm/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x56535777ac98)", + RANLIB => "CODE(0x55d79573e6c0)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86.s aesni-x86.s", @@ -289,6 +289,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3186,6 +3187,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3236,6 +3238,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9216,6 +9219,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9281,6 +9289,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14032,6 +14045,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14045,6 +14059,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14152,6 +14167,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14204,6 +14223,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h index b0dfdc7a561045..509dafd58ce656 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:03 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h b/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi index a718f985ec9427..0028ff60aef07b 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm b/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm index f0ae44934c40be..47fdbbbc65c9f4 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55a934dbe798)", + RANLIB => "CODE(0x5624002a8d50)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86.s aesni-x86.s", @@ -289,6 +289,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3186,6 +3187,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3236,6 +3238,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9216,6 +9219,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9281,6 +9289,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14032,6 +14045,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14045,6 +14059,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14152,6 +14167,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14204,6 +14223,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h index 6aeb27ba400834..e73ba40561498b 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:39 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:06 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h b/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi index a5bb5f58ee45b3..e275311fc86c18 100644 --- a/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm index 16ff6d982004a3..492ca88e3f4e38 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/configdata.pm @@ -65,7 +65,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN32" ], perlenv => { "AR" => undef, @@ -131,7 +131,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55ddfff979c8)", + RANLIB => "CODE(0x563440a67888)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -286,6 +286,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9123,6 +9126,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9188,6 +9196,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13874,6 +13887,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13887,6 +13901,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13994,6 +14009,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14046,6 +14065,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h index 916cdacd502875..78537c713e9d9b 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:44 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:10 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi index 2c843d99020ec1..5645409c76b050 100644 --- a/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN32/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm index 1d006b56d6bc9e..6eaceedda05f37 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/configdata.pm @@ -64,7 +64,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN64-ARM" ], perlenv => { "AR" => undef, @@ -128,7 +128,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x55cd56acf3f8)", + RANLIB => "CODE(0x56190a16c608)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/50-win-onecore.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -280,6 +280,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9117,6 +9120,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9182,6 +9190,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13868,6 +13881,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13881,6 +13895,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13988,6 +14003,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14040,6 +14059,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h index 3e790eb5d64363..ac69eb5a2e51c5 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: VC-WIN64-ARM" -#define DATE "built on: Fri Sep 13 16:03:47 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:12 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi index 1b9795cdd47c79..b1bc8c1b97f277 100644 --- a/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64-ARM/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm index fd72727b52092d..6004a6783ed894 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/asm/configdata.pm @@ -67,7 +67,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -133,7 +133,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x560295a39668)", + RANLIB => "CODE(0x559637c70c60)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86_64.s aesni-x86_64.s aesni-sha1-x86_64.s aesni-sha256-x86_64.s aesni-mb-x86_64.s", @@ -291,6 +291,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3198,6 +3199,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3248,6 +3250,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9288,6 +9291,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9353,6 +9361,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14154,6 +14167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14167,6 +14181,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14274,6 +14289,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14326,6 +14345,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h index eba30a9c2857ac..9069afb43160de 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:02:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:43 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi index 20ef5e5d4b3a08..f274761feb5543 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm index 1a22e6b6ed13c0..8f0002f44b84b4 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/configdata.pm @@ -67,7 +67,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -133,7 +133,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x562305dbe5d8)", + RANLIB => "CODE(0x563fbb64b740)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c vpaes-x86_64.s aesni-x86_64.s aesni-sha1-x86_64.s aesni-sha256-x86_64.s aesni-mb-x86_64.s", @@ -291,6 +291,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3198,6 +3199,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3248,6 +3250,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9288,6 +9291,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9353,6 +9361,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14154,6 +14167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14167,6 +14181,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14274,6 +14289,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14326,6 +14345,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h index 8b7c50ce9a5416..88f52dfa3c7acb 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:52 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi index 53d15ad06c0ecb..9b32fcd8e765b8 100644 --- a/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm b/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm index fdebb6f709d6b6..da5a29bdebc5e9 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/configdata.pm @@ -66,7 +66,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "VC-WIN64A" ], perlenv => { "AR" => undef, @@ -132,7 +132,7 @@ our %target = ( LDFLAGS => "/nologo /debug", MT => "mt", MTFLAGS => "-nologo", - RANLIB => "CODE(0x557a48128d28)", + RANLIB => "CODE(0x55ebe61c3c68)", RC => "rc", _conf_fname_int => [ "Configurations/00-base-templates.conf", "Configurations/00-base-templates.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/10-main.conf", "Configurations/shared-info.pl" ], aes_asm_src => "aes_core.c aes_cbc.c", @@ -288,6 +288,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3173,6 +3174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3223,6 +3225,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9125,6 +9128,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9190,6 +9198,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13876,6 +13889,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13889,6 +13903,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13996,6 +14011,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14048,6 +14067,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h index fae2f1183e9277..2d32047ab1837d 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: " -#define DATE "built on: Fri Sep 13 16:03:30 2019 UTC" +#define DATE "built on: Thu Feb 20 00:05:01 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h b/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi b/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi index a5000c82601f3a..2cb69099ec5eb9 100644 --- a/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/VC-WIN64A/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/asm/configdata.pm b/deps/openssl/config/archs/aix-gcc/asm/configdata.pm index b79cebd14c344b..9b31fcfdef7826 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h index 23bf3c386089ea..d909ea67378c8e 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:47 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:14 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/asm/include/progs.h b/deps/openssl/config/archs/aix-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi b/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi index 272dfa47b0f03b..be4b22e052186c 100644 --- a/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm index 52c1848ab07914..95c7b6b9a15087 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h index 553eaaf94c54aa..d9c3b3e2c97d49 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:17 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi index 724ae8a38f8407..1b04259393ddf8 100644 --- a/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm index 48bc45df709b75..1dfbe27c3144d4 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/aix-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "aix-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h index a3c2c5a66f7e4e..845ddc87259c1b 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix-gcc" -#define DATE "built on: Fri Sep 13 15:56:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/aix-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi index 41095d9eb21597..e5dbb2911117a8 100644 --- a/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/aix-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm b/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm index a91a3c0878db81..94bb10b317502f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9206,6 +9209,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9271,6 +9279,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14059,6 +14072,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14072,6 +14086,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14179,6 +14194,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14231,6 +14250,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h index 4ec1ec8283c301..de2eea406e1019 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:01 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:22 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h b/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi index 321b86afd425ed..d9278603b7c88f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm index 56f615362d9a45..a004ff416477db 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9206,6 +9209,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9271,6 +9279,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14059,6 +14072,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14072,6 +14086,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14179,6 +14194,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14231,6 +14250,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h index fe20db38c0347d..8bf5cab038dc2f 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:07 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi index f92a885321bffd..20c46d1e90fa45 100644 --- a/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm index d37f8ad8b04179..ca9df45239ede0 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "aix64-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h index 533bf0b9e3e4a7..56272f3ef3bfbb 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: aix64-gcc" -#define DATE "built on: Fri Sep 13 15:57:13 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:29 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi index 89752b18296f81..4e6b451e9f9811 100644 --- a/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/aix64-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm index d9ceb02bb7bc17..6b88364842c928 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9164,6 +9167,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9229,6 +9237,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14002,6 +14015,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14015,6 +14029,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14122,6 +14137,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14174,6 +14193,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h index abd541341f17ee..f8c7fa6d519ce2 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:40 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:17 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi index 38d1fdcc00e68c..9295a344dd43af 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm index e6aed64bed36d2..d4fd7bf0609ec9 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3156,6 +3157,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3206,6 +3208,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9164,6 +9167,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9229,6 +9237,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14002,6 +14015,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14015,6 +14029,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14122,6 +14137,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14174,6 +14193,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h index a78ae310fc883b..9ccca4838d64d3 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:46 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:21 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi index c1f15b01a15b5a..ba6404162b2746 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm b/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm index a40cf9d26d3bd6..a90861f26fce5d 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "darwin-i386-cc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9071,6 +9074,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9136,6 +9144,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13844,6 +13857,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13857,6 +13871,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13964,6 +13979,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14016,6 +14035,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h index 5a6d99c66de2c9..14e06dae01099b 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin-i386-cc" -#define DATE "built on: Fri Sep 13 15:58:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h b/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi b/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi index c62bcfabf4dd06..ba06c3d630cc8a 100644 --- a/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin-i386-cc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm index 667d891890ef1c..c62730f9f208b0 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3179,6 +3180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3229,6 +3231,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9247,6 +9250,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9312,6 +9320,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14135,6 +14148,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14148,6 +14162,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14255,6 +14270,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14307,6 +14326,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h index a6d2dadb30280a..bfdf9e4a15528b 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:07 2019 UTC" +#define DATE "built on: Thu Feb 20 00:01:59 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi index f87225df694da8..638b3d84930690 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm index 32bcc2c46627da..05b69a40c14056 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -267,6 +267,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3179,6 +3180,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3229,6 +3231,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9247,6 +9250,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9312,6 +9320,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14135,6 +14148,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14148,6 +14162,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14255,6 +14270,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14307,6 +14326,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h index da2e3d51505ac4..52954dde5f06cd 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:21 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:07 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi index be4971efb1cbbf..ebb6c820367276 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm index 02290abc08f011..b3555c4582d96d 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "darwin64-x86_64-cc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3141,6 +3142,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3191,6 +3193,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9071,6 +9074,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9136,6 +9144,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13844,6 +13857,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13857,6 +13871,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13964,6 +13979,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14016,6 +14035,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h index a2a8835315fef3..c6c8939166740a 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: darwin64-x86_64-cc" -#define DATE "built on: Fri Sep 13 15:58:36 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:15 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi index ae6905b4dbb510..234a0c651e5bcb 100644 --- a/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/darwin64-x86_64-cc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm b/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm index 832a223ee3ea9c..089f6dde9ddd45 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3166,6 +3167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3216,6 +3218,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9147,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9212,6 +9220,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13975,6 +13988,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13988,6 +14002,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14095,6 +14110,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14147,6 +14166,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h index 6046c7d5fd9777..c3c2471993b75c 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:58:56 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:27 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h b/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi index 01678f13026be3..f33588b637f9dd 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm index bfc02733dc2706..714f75b4b23a66 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3166,6 +3167,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3216,6 +3218,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9147,6 +9150,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9212,6 +9220,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13975,6 +13988,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13988,6 +14002,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14095,6 +14110,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14147,6 +14166,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h index 566cfdfd842a9a..0fa826d96613c9 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:59:02 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:30 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi index b531611f3a51a9..b9a36817a55c5b 100644 --- a/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm index 320833b90223fa..fdf67718092f36 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-aarch64" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h index ed237c3102f71e..abbd1c49bc3352 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-aarch64" -#define DATE "built on: Fri Sep 13 15:59:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:33 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi index 05351ffa3de54d..ac3e33a41bbb5d 100644 --- a/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-aarch64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/asm/configdata.pm b/deps/openssl/config/archs/linux-armv4/asm/configdata.pm index c62115205baa7e..18d57e707865b9 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-armv4" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9143,6 +9146,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9208,6 +9216,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13981,6 +13994,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13994,6 +14008,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14101,6 +14116,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14153,6 +14172,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h index ef86bdcf892a82..316d565ef68f49 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:11 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:35 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/asm/include/progs.h b/deps/openssl/config/archs/linux-armv4/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi b/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi index 8d6124fa1876bb..628bb5d5908f08 100644 --- a/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm index 41e52a0bd5590d..5fdb51c9c7911c 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-armv4" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9143,6 +9146,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9208,6 +9216,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13981,6 +13994,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13994,6 +14008,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14101,6 +14116,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14153,6 +14172,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h index 3b9d482a1ab1ba..fe0e9746ab82a9 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:17 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:39 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi index 6358ee980cfa04..9898fba73aa21f 100644 --- a/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm b/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm index 9603f342e70290..a163462fb34790 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-armv4/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-armv4" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h index 7cf872d12e9d2a..077f4eba380b20 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-armv4/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-armv4" -#define DATE "built on: Fri Sep 13 15:59:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:42 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h b/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-armv4/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi index ad58dc4d8d07cd..070531fcea58e7 100644 --- a/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-armv4/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/asm/configdata.pm b/deps/openssl/config/archs/linux-elf/asm/configdata.pm index 2e07e0f655cb5d..29723efdffbc5b 100644 --- a/deps/openssl/config/archs/linux-elf/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-elf" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3169,6 +3170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3219,6 +3221,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9189,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9254,6 +9262,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14027,6 +14040,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14040,6 +14054,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14147,6 +14162,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14199,6 +14218,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h index d35d80a968c59e..f9914abd41bafd 100644 --- a/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:27 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:44 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/asm/include/progs.h b/deps/openssl/config/archs/linux-elf/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-elf/asm/openssl.gypi b/deps/openssl/config/archs/linux-elf/asm/openssl.gypi index 19fe9eac77262f..4846cf63f83029 100644 --- a/deps/openssl/config/archs/linux-elf/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm index 9ddf36af0f5672..cd346875c753eb 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-elf" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3169,6 +3170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3219,6 +3221,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9189,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9254,6 +9262,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14027,6 +14040,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14040,6 +14054,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14147,6 +14162,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14199,6 +14218,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h index 153225532e4c1c..07be64138a042a 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:48 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi index 5cfdf4bbe688ae..d06fed49316efd 100644 --- a/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm b/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm index 0c657bee8b914c..53c234555ca984 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-elf/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-elf" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3153,6 +3154,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3203,6 +3205,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9095,6 +9098,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9160,6 +9168,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13868,6 +13881,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13881,6 +13895,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13988,6 +14003,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14040,6 +14059,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h index 39469e8298ae17..86532eadb4341e 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-elf/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-elf" -#define DATE "built on: Fri Sep 13 15:59:39 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:51 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h b/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-elf/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi index 90dd351ecee489..6e1be370e52b46 100644 --- a/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-elf/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc/asm/configdata.pm index 47234ab9177f5b..91a584ba4b6174 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9188,6 +9191,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9253,6 +9261,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14026,6 +14039,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14039,6 +14053,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14146,6 +14161,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14198,6 +14217,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h index af759c6bdab883..3fbe352e945aca 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:48 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:30 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi index 7e5c6bb10968b2..16bb142326aaab 100644 --- a/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm index 65b50577c3673c..235caf55ada01f 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3168,6 +3169,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3218,6 +3220,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9188,6 +9191,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9253,6 +9261,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14026,6 +14039,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14039,6 +14053,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14146,6 +14161,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14198,6 +14217,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h index d91d124d0d95f7..dbe3ab2ea7a8be 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:33 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi index 439c2022b18ce7..12b0757d0a638d 100644 --- a/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm index 41afbcc4445a44..f1ef21dc885cac 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h index feccd6e409e5d0..34b7e873739f45 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc" -#define DATE "built on: Fri Sep 13 16:00:59 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:36 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi index 7801ded0cf12d9..b171a2c5fbf116 100644 --- a/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm index a8105b72abd10d..7588af12601d6a 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3172,6 +3173,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3222,6 +3224,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9210,6 +9213,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9275,6 +9283,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14063,6 +14076,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14076,6 +14090,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14183,6 +14198,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14235,6 +14254,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h index 2c9f24fdee2e25..cf2d57e41ad93b 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:02 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:38 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi index a050df1969e166..0801d695d269f2 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm index fa53549253d37e..14a47d4c434a7c 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3172,6 +3173,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3222,6 +3224,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9210,6 +9213,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9275,6 +9283,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14063,6 +14076,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14076,6 +14090,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14183,6 +14198,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14235,6 +14254,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h index 0e70ae12285ec6..47c478688719ed 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:42 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi index e0fea38173d967..abe76346265130 100644 --- a/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm index 42b7f02251e94b..8acc6c533fa397 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h index 50c645ff13a868..bdcd72046f9b15 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64" -#define DATE "built on: Fri Sep 13 16:01:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:45 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi index 7580161fa0bfee..ac8494f3acb93c 100644 --- a/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm index aff8df2a584ca8..1a39c845b002f3 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9209,6 +9212,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9274,6 +9282,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14062,6 +14075,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14075,6 +14089,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14182,6 +14197,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14234,6 +14253,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h index e7003954bb1a42..d1935581086970 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:17 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:47 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi index 7f228ce30357cc..947f5d69fed623 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm index a92bb250672e06..e33537179d65c4 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -271,6 +271,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3171,6 +3172,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3221,6 +3223,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9209,6 +9212,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9274,6 +9282,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14062,6 +14075,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14075,6 +14089,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14182,6 +14197,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14234,6 +14253,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h index e8e073c0d22a9f..23884fe1e45088 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:23 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:50 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi index 548429f9f799f2..117de2efe50dc4 100644 --- a/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm b/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm index 9d91dea1de5354..5601b0032a5c26 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-ppc64le" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9096,6 +9099,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9161,6 +9169,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13869,6 +13882,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13882,6 +13896,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13989,6 +14004,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14041,6 +14060,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h index aebc739df4d2c0..15b242772a88fd 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-ppc64le" -#define DATE "built on: Fri Sep 13 16:01:29 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:54 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h b/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi index e6afc59dc7338a..a8b845c3413ed5 100644 --- a/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-ppc64le/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/asm/configdata.pm b/deps/openssl/config/archs/linux-x32/asm/configdata.pm index 58a473be95a3e6..01666b76435fde 100644 --- a/deps/openssl/config/archs/linux-x32/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x32" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h index c9b1f198949916..a068758ad82591 100644 --- a/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 15:59:43 2019 UTC" +#define DATE "built on: Thu Feb 20 00:02:54 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/asm/include/progs.h b/deps/openssl/config/archs/linux-x32/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x32/asm/openssl.gypi b/deps/openssl/config/archs/linux-x32/asm/openssl.gypi index 5faef9c0e48f51..2dd7c27e8c80ae 100644 --- a/deps/openssl/config/archs/linux-x32/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm index c85073cdc5ea14..cd2ab1fa0760b9 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x32" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h index f567041169936f..ec5b5b8d0ef72d 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 15:59:58 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:02 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi index 7dd6ee5dda657f..0624421d8740e1 100644 --- a/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm b/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm index b9a8bebaa20a64..b263c7ea4e229b 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x32/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-x32" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h index 369ee613964bf0..e40f0c6a77237a 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x32/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x32" -#define DATE "built on: Fri Sep 13 16:00:12 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:10 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h b/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x32/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi index 96a69a8a9a0129..bd2bc30038a6bc 100644 --- a/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x32/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm b/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm index 6eac1af414f413..9304830a31bfc7 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/asm/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h index 17e89914852fb5..1cb5afe3fddb30 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:16 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:12 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h b/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi index 92491dbd9f1334..623e6153877f7b 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm index b2a66729ebcce6..e46f8af05da8ba 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/configdata.pm @@ -63,7 +63,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -273,6 +273,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3193,6 +3194,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3243,6 +3245,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9273,6 +9276,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9338,6 +9346,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14161,6 +14174,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14174,6 +14188,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14281,6 +14296,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14333,6 +14352,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h index 51e776d7cb1ade..5c643a2af84bab 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:30 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi index 05ee0df141731d..429dfc002fbec2 100644 --- a/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm b/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm index 93da6e96905edd..56e61c0e2efd6e 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux-x86_64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h index df3083b539c028..ff0106c7f2a1c0 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux-x86_64" -#define DATE "built on: Fri Sep 13 16:00:45 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:28 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h b/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi index 11d3aa6e1486e3..d473731e5e4798 100644 --- a/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux-x86_64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm b/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm index 02768931a01019..1d5b687bd73293 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3162,6 +3163,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3212,6 +3214,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9119,6 +9122,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9184,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13922,6 +13935,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13935,6 +13949,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14042,6 +14057,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14094,6 +14113,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h index e5da8365c7a61d..7da494ac5b3540 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:33 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:56 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h b/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi index d70096ff56f805..40f777626d5b29 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm index 96ab247e83743d..dd8228346364d5 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3162,6 +3163,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3212,6 +3214,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9119,6 +9122,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9184,6 +9192,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13922,6 +13935,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13935,6 +13949,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14042,6 +14057,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14094,6 +14113,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h index 71628ed04c6c4f..bf2d4bac853c43 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:37 2019 UTC" +#define DATE "built on: Thu Feb 20 00:03:58 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi index 3f09a9306ca936..5fe0ff0ef418e4 100644 --- a/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm b/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm index 1fcc3a092d63ba..ab3bf5783c4635 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux32-s390x" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3154,6 +3155,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3204,6 +3206,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9090,6 +9093,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9155,6 +9163,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13858,6 +13871,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13871,6 +13885,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13978,6 +13993,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14030,6 +14049,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h index e7256e3f12adc9..235b4ff88d8b86 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux32-s390x" -#define DATE "built on: Fri Sep 13 16:01:41 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:00 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h b/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi b/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi index f1ab15205bd125..d306c74be0cbd7 100644 --- a/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux32-s390x/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm b/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm index 6d77d25fe39da0..45332dee3b27f0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3161,6 +3162,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3211,6 +3213,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9112,6 +9115,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9177,6 +9185,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13910,6 +13923,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13923,6 +13937,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14030,6 +14045,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14082,6 +14101,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15986,3 +16009,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h index 865115208dc742..a07f7ebed69d68 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:01:56 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:09 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S index 0459f23f81b1ad..2906baaf6e155f 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha256-mips.S @@ -3034,3 +3034,4 @@ K256: .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 .asciiz "SHA256 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S index 538bd9e459332d..6318b5a40f9bd0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm/crypto/sha/sha512-mips.S @@ -3202,3 +3202,4 @@ K512: .dword 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 .asciiz "SHA512 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h b/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi index bc83b68da5f125..5fab88a675dda5 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm index 8324964ca7936e..01e8255c451294 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3161,6 +3162,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3211,6 +3213,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9112,6 +9115,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9177,6 +9185,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13910,6 +13923,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13923,6 +13937,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14030,6 +14045,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14082,6 +14101,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15986,3 +16009,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h index f6a9d770f1eb3e..af29142c9ef9cd 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:02:00 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:11 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S index 0459f23f81b1ad..2906baaf6e155f 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha256-mips.S @@ -3034,3 +3034,4 @@ K256: .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 .asciiz "SHA256 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S index 538bd9e459332d..6318b5a40f9bd0 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/crypto/sha/sha512-mips.S @@ -3202,3 +3202,4 @@ K512: .dword 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 .asciiz "SHA512 for MIPS, CRYPTOGAMS by " .align 5 + diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi index 300c2862915e33..fc1d3c145a33b8 100644 --- a/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm b/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm index f5b0bfea293e48..e9c443db4b2771 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux64-mips64" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", @@ -15949,3 +15972,4 @@ Verbose output. =back =cut + diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h index 3c6ad34101ddd7..d5e5dbc0f64a76 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-mips64" -#define DATE "built on: Fri Sep 13 16:02:04 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:14 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h b/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi b/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi index 8df2e5005f9e1a..28e13e5608601f 100644 --- a/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-mips64/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm b/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm index e31688fe1aceae..2e6f66474e82dc 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3175,6 +3176,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3225,6 +3227,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9132,6 +9135,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9197,6 +9205,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13935,6 +13948,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13948,6 +13962,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14055,6 +14070,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14107,6 +14126,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h index 6d2d3ce3fd2bc9..d9527a9bd229a3 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:44 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:02 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h b/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi index ede7c74f3f3045..1e147d24c7269a 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm b/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm index 8aa79776f95c01..8f15e57d4ea244 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -272,6 +272,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3175,6 +3176,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3225,6 +3227,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9132,6 +9135,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9197,6 +9205,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13935,6 +13948,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13948,6 +13962,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14055,6 +14070,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14107,6 +14126,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h index 34799d14b191e1..35610d05130a29 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:49 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:05 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h b/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi index cc32b3b5018b4f..a5656bc070313d 100644 --- a/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm b/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm index 0a65365f26b154..fc14fbb5d7bf5e 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "linux64-s390x" ], perlenv => { "AR" => undef, @@ -270,6 +270,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3155,6 +3156,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3205,6 +3207,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9097,6 +9100,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9162,6 +9170,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13870,6 +13883,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13883,6 +13897,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13990,6 +14005,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14042,6 +14061,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h index e0fd887860b698..e8844598bdcfeb 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: linux64-s390x" -#define DATE "built on: Fri Sep 13 16:01:53 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:07 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h b/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi b/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi index 9e454f069376a2..6cfbbb721fd86b 100644 --- a/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/linux64-s390x/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm index c14020e995aa16..e8983e7ba4bac6 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h index 15030d59077278..b270261855ab58 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:08 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:16 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi index 69bded9b4f426e..3a2d2232f77676 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm index 7663c71cf63596..623c419d997a97 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -268,6 +268,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3165,6 +3166,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3215,6 +3217,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9185,6 +9188,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9250,6 +9258,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14023,6 +14036,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14036,6 +14050,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14143,6 +14158,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14195,6 +14214,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h index 78995b8d82a7e3..1a8bcb87e2e2a6 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:14 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:20 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi index 2e4804695fb92e..d7544b646388d8 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm index b0d7b13f2220d9..ef2a18e9c01c2d 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "solaris-x86-gcc" ], perlenv => { "AR" => undef, @@ -264,6 +264,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3149,6 +3150,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3199,6 +3201,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9091,6 +9094,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9156,6 +9164,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13864,6 +13877,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13877,6 +13891,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13984,6 +13999,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14036,6 +14055,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h index e606a3cd7599e4..37e8f820a1abf7 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris-x86-gcc" -#define DATE "built on: Fri Sep 13 16:02:21 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:23 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi index 453f72681938fb..e3e22c6ee2959d 100644 --- a/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris-x86-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm index d651ee1e5ffe7c..61630cbc58a185 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3189,6 +3190,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3239,6 +3241,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9269,6 +9272,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9334,6 +9342,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14157,6 +14170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14170,6 +14184,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14277,6 +14292,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14329,6 +14348,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h index cc89c88b1cefa1..12e7dc433f88cd 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:25 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:25 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi index 0a3feb2ad53f17..902a1a818dbc6c 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm index f10bb2324203ea..de4bcbf80c5043 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/configdata.pm @@ -62,7 +62,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -269,6 +269,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3189,6 +3190,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3239,6 +3241,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9269,6 +9272,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9334,6 +9342,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -14157,6 +14170,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -14170,6 +14184,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -14277,6 +14292,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14329,6 +14348,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h index c7f32e0bb8b008..7d4ed349c8d5f0 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:40 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:34 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi index a634c666572987..edf02f83cb517c 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/asm_avx2/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm index 50438e79f77475..d3fc70db7e3e91 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/configdata.pm @@ -61,7 +61,7 @@ our %config = ( options => "enable-ssl-trace no-afalgeng no-asan no-asm no-buildtest-c++ no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-devcryptoeng no-dynamic-engine no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fuzz-afl no-fuzz-libfuzzer no-heartbeats no-md2 no-msan no-rc5 no-sctp no-shared no-ssl3 no-ssl3-method no-ubsan no-unit-test no-weak-ssl-ciphers no-zlib no-zlib-dynamic", perl_archname => "x86_64-linux-gnu-thread-multi", perl_cmd => "/usr/bin/perl", - perl_version => "5.28.1", + perl_version => "5.26.1", perlargv => [ "no-comp", "no-shared", "no-afalgeng", "enable-ssl-trace", "no-asm", "solaris64-x86_64-gcc" ], perlenv => { "AR" => undef, @@ -266,6 +266,7 @@ our @disablables = ( "poly1305", "posix-io", "psk", + "quic", "rc2", "rc4", "rc5", @@ -3151,6 +3152,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -3201,6 +3203,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", ], "products" => @@ -9093,6 +9096,11 @@ our %unified_info = ( ".", "include", ], + "ssl/ssl_quic.o" => + [ + ".", + "include", + ], "ssl/ssl_rsa.o" => [ ".", @@ -9158,6 +9166,11 @@ our %unified_info = ( ".", "include", ], + "ssl/statem/statem_quic.o" => + [ + ".", + "include", + ], "ssl/statem/statem_srvr.o" => [ ".", @@ -13866,6 +13879,7 @@ our %unified_info = ( "ssl/ssl_init.o", "ssl/ssl_lib.o", "ssl/ssl_mcnf.o", + "ssl/ssl_quic.o", "ssl/ssl_rsa.o", "ssl/ssl_sess.o", "ssl/ssl_stat.o", @@ -13879,6 +13893,7 @@ our %unified_info = ( "ssl/statem/statem_clnt.o", "ssl/statem/statem_dtls.o", "ssl/statem/statem_lib.o", + "ssl/statem/statem_quic.o", "ssl/statem/statem_srvr.o", "ssl/t1_enc.o", "ssl/t1_lib.o", @@ -13986,6 +14001,10 @@ our %unified_info = ( [ "ssl/ssl_mcnf.c", ], + "ssl/ssl_quic.o" => + [ + "ssl/ssl_quic.c", + ], "ssl/ssl_rsa.o" => [ "ssl/ssl_rsa.c", @@ -14038,6 +14057,10 @@ our %unified_info = ( [ "ssl/statem/statem_lib.c", ], + "ssl/statem/statem_quic.o" => + [ + "ssl/statem/statem_quic.c", + ], "ssl/statem/statem_srvr.o" => [ "ssl/statem/statem_srvr.c", diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h index ae3b2d62e6e569..5389ce09d4d71d 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/crypto/buildinf.h @@ -11,7 +11,7 @@ */ #define PLATFORM "platform: solaris64-x86_64-gcc" -#define DATE "built on: Fri Sep 13 16:02:54 2019 UTC" +#define DATE "built on: Thu Feb 20 00:04:41 2020 UTC" /* * Generate compiler_flags as an array of individual characters. This is a diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h index 9b3d270e20800e..308c65601bb960 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/include/progs.h @@ -2,7 +2,7 @@ * WARNING: do not edit! * Generated by apps/progs.pl * - * Copyright 1995-2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi index 1243b00e93eeec..84c85b8e9252c6 100644 --- a/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi +++ b/deps/openssl/config/archs/solaris64-x86_64-gcc/no-asm/openssl.gypi @@ -26,6 +26,7 @@ 'openssl/ssl/ssl_init.c', 'openssl/ssl/ssl_lib.c', 'openssl/ssl/ssl_mcnf.c', + 'openssl/ssl/ssl_quic.c', 'openssl/ssl/ssl_rsa.c', 'openssl/ssl/ssl_sess.c', 'openssl/ssl/ssl_stat.c', @@ -39,6 +40,7 @@ 'openssl/ssl/statem/statem_clnt.c', 'openssl/ssl/statem/statem_dtls.c', 'openssl/ssl/statem/statem_lib.c', + 'openssl/ssl/statem/statem_quic.c', 'openssl/ssl/statem/statem_srvr.c', 'openssl/ssl/t1_enc.c', 'openssl/ssl/t1_lib.c', From 295191f235aa094b057150570a869ee7f526ac5c Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Dec 2019 12:27:57 -0500 Subject: [PATCH 3/6] deps: add ngtcp2 and nghttp3 --- deps/nghttp3/COPYING | 22 + deps/nghttp3/lib/includes/config.h | 39 + deps/nghttp3/lib/includes/nghttp3/nghttp3.h | 1759 +++ deps/nghttp3/lib/includes/nghttp3/version.h | 46 + deps/nghttp3/lib/nghttp3_buf.c | 90 + deps/nghttp3/lib/nghttp3_buf.h | 74 + deps/nghttp3/lib/nghttp3_conn.c | 3232 ++++++ deps/nghttp3/lib/nghttp3_conn.h | 279 + deps/nghttp3/lib/nghttp3_conv.c | 130 + deps/nghttp3/lib/nghttp3_conv.h | 181 + deps/nghttp3/lib/nghttp3_debug.c | 61 + deps/nghttp3/lib/nghttp3_debug.h | 44 + deps/nghttp3/lib/nghttp3_err.c | 123 + deps/nghttp3/lib/nghttp3_err.h | 34 + deps/nghttp3/lib/nghttp3_frame.c | 200 + deps/nghttp3/lib/nghttp3_frame.h | 141 + deps/nghttp3/lib/nghttp3_gaptr.c | 131 + deps/nghttp3/lib/nghttp3_gaptr.h | 98 + deps/nghttp3/lib/nghttp3_http.c | 731 ++ deps/nghttp3/lib/nghttp3_http.h | 146 + deps/nghttp3/lib/nghttp3_idtr.c | 88 + deps/nghttp3/lib/nghttp3_idtr.h | 99 + deps/nghttp3/lib/nghttp3_ksl.c | 751 ++ deps/nghttp3/lib/nghttp3_ksl.h | 336 + deps/nghttp3/lib/nghttp3_macro.h | 47 + deps/nghttp3/lib/nghttp3_map.c | 213 + deps/nghttp3/lib/nghttp3_map.h | 147 + deps/nghttp3/lib/nghttp3_mem.c | 77 + deps/nghttp3/lib/nghttp3_mem.h | 45 + deps/nghttp3/lib/nghttp3_pq.c | 168 + deps/nghttp3/lib/nghttp3_pq.h | 129 + deps/nghttp3/lib/nghttp3_qpack.c | 4056 +++++++ deps/nghttp3/lib/nghttp3_qpack.h | 963 ++ deps/nghttp3/lib/nghttp3_qpack_huffman.c | 122 + deps/nghttp3/lib/nghttp3_qpack_huffman.h | 108 + deps/nghttp3/lib/nghttp3_qpack_huffman_data.c | 4981 +++++++++ deps/nghttp3/lib/nghttp3_range.c | 62 + deps/nghttp3/lib/nghttp3_range.h | 81 + deps/nghttp3/lib/nghttp3_rcbuf.c | 109 + deps/nghttp3/lib/nghttp3_rcbuf.h | 82 + deps/nghttp3/lib/nghttp3_ringbuf.c | 151 + deps/nghttp3/lib/nghttp3_ringbuf.h | 113 + deps/nghttp3/lib/nghttp3_str.c | 110 + deps/nghttp3/lib/nghttp3_str.h | 40 + deps/nghttp3/lib/nghttp3_stream.c | 1342 +++ deps/nghttp3/lib/nghttp3_stream.h | 425 + deps/nghttp3/lib/nghttp3_tnode.c | 334 + deps/nghttp3/lib/nghttp3_tnode.h | 155 + deps/nghttp3/lib/nghttp3_vec.c | 64 + deps/nghttp3/lib/nghttp3_vec.h | 35 + deps/nghttp3/lib/nghttp3_version.c | 39 + deps/nghttp3/nghttp3.gyp | 62 + deps/ngtcp2/COPYING | 22 + .../crypto/includes/ngtcp2/ngtcp2_crypto.h | 561 + .../includes/ngtcp2/ngtcp2_crypto_openssl.h | 34 + deps/ngtcp2/crypto/openssl/openssl.c | 395 + deps/ngtcp2/crypto/shared.c | 488 + deps/ngtcp2/crypto/shared.h | 72 + deps/ngtcp2/lib/includes/config.h | 39 + deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h | 2889 +++++ deps/ngtcp2/lib/includes/ngtcp2/version.h | 45 + deps/ngtcp2/lib/includes/ngtcp2/version.h.in | 45 + deps/ngtcp2/lib/ngtcp2_acktr.c | 331 + deps/ngtcp2/lib/ngtcp2_acktr.h | 215 + deps/ngtcp2/lib/ngtcp2_addr.c | 57 + deps/ngtcp2/lib/ngtcp2_addr.h | 60 + deps/ngtcp2/lib/ngtcp2_buf.c | 44 + deps/ngtcp2/lib/ngtcp2_buf.h | 79 + deps/ngtcp2/lib/ngtcp2_cc.c | 137 + deps/ngtcp2/lib/ngtcp2_cc.h | 92 + deps/ngtcp2/lib/ngtcp2_cid.c | 118 + deps/ngtcp2/lib/ngtcp2_cid.h | 137 + deps/ngtcp2/lib/ngtcp2_conn.c | 9502 +++++++++++++++++ deps/ngtcp2/lib/ngtcp2_conn.h | 708 ++ deps/ngtcp2/lib/ngtcp2_conv.c | 251 + deps/ngtcp2/lib/ngtcp2_conv.h | 261 + deps/ngtcp2/lib/ngtcp2_crypto.c | 649 ++ deps/ngtcp2/lib/ngtcp2_crypto.h | 102 + deps/ngtcp2/lib/ngtcp2_err.c | 136 + deps/ngtcp2/lib/ngtcp2_err.h | 34 + deps/ngtcp2/lib/ngtcp2_gaptr.c | 127 + deps/ngtcp2/lib/ngtcp2_gaptr.h | 96 + deps/ngtcp2/lib/ngtcp2_idtr.c | 86 + deps/ngtcp2/lib/ngtcp2_idtr.h | 95 + deps/ngtcp2/lib/ngtcp2_ksl.c | 749 ++ deps/ngtcp2/lib/ngtcp2_ksl.h | 334 + deps/ngtcp2/lib/ngtcp2_log.c | 730 ++ deps/ngtcp2/lib/ngtcp2_log.h | 99 + deps/ngtcp2/lib/ngtcp2_macro.h | 53 + deps/ngtcp2/lib/ngtcp2_map.c | 210 + deps/ngtcp2/lib/ngtcp2_map.h | 145 + deps/ngtcp2/lib/ngtcp2_mem.c | 75 + deps/ngtcp2/lib/ngtcp2_mem.h | 43 + deps/ngtcp2/lib/ngtcp2_net.h | 91 + deps/ngtcp2/lib/ngtcp2_path.c | 68 + deps/ngtcp2/lib/ngtcp2_path.h | 63 + deps/ngtcp2/lib/ngtcp2_pipeack.c | 155 + deps/ngtcp2/lib/ngtcp2_pipeack.h | 89 + deps/ngtcp2/lib/ngtcp2_pkt.c | 2252 ++++ deps/ngtcp2/lib/ngtcp2_pkt.h | 1118 ++ deps/ngtcp2/lib/ngtcp2_ppe.c | 230 + deps/ngtcp2/lib/ngtcp2_ppe.h | 153 + deps/ngtcp2/lib/ngtcp2_pq.c | 164 + deps/ngtcp2/lib/ngtcp2_pq.h | 126 + deps/ngtcp2/lib/ngtcp2_psl.c | 621 ++ deps/ngtcp2/lib/ngtcp2_psl.h | 231 + deps/ngtcp2/lib/ngtcp2_pv.c | 155 + deps/ngtcp2/lib/ngtcp2_pv.h | 163 + deps/ngtcp2/lib/ngtcp2_qlog.c | 1202 +++ deps/ngtcp2/lib/ngtcp2_qlog.h | 134 + deps/ngtcp2/lib/ngtcp2_range.c | 61 + deps/ngtcp2/lib/ngtcp2_range.h | 80 + deps/ngtcp2/lib/ngtcp2_rcvry.h | 44 + deps/ngtcp2/lib/ngtcp2_ringbuf.c | 106 + deps/ngtcp2/lib/ngtcp2_ringbuf.h | 110 + deps/ngtcp2/lib/ngtcp2_rob.c | 341 + deps/ngtcp2/lib/ngtcp2_rob.h | 203 + deps/ngtcp2/lib/ngtcp2_rst.c | 107 + deps/ngtcp2/lib/ngtcp2_rst.h | 69 + deps/ngtcp2/lib/ngtcp2_rtb.c | 670 ++ deps/ngtcp2/lib/ngtcp2_rtb.h | 329 + deps/ngtcp2/lib/ngtcp2_str.c | 98 + deps/ngtcp2/lib/ngtcp2_str.h | 85 + deps/ngtcp2/lib/ngtcp2_strm.c | 325 + deps/ngtcp2/lib/ngtcp2_strm.h | 219 + deps/ngtcp2/lib/ngtcp2_vec.c | 232 + deps/ngtcp2/lib/ngtcp2_vec.h | 114 + deps/ngtcp2/lib/ngtcp2_version.c | 39 + deps/ngtcp2/ngtcp2.gyp | 116 + 129 files changed, 53803 insertions(+) create mode 100644 deps/nghttp3/COPYING create mode 100644 deps/nghttp3/lib/includes/config.h create mode 100644 deps/nghttp3/lib/includes/nghttp3/nghttp3.h create mode 100644 deps/nghttp3/lib/includes/nghttp3/version.h create mode 100644 deps/nghttp3/lib/nghttp3_buf.c create mode 100644 deps/nghttp3/lib/nghttp3_buf.h create mode 100644 deps/nghttp3/lib/nghttp3_conn.c create mode 100644 deps/nghttp3/lib/nghttp3_conn.h create mode 100644 deps/nghttp3/lib/nghttp3_conv.c create mode 100644 deps/nghttp3/lib/nghttp3_conv.h create mode 100644 deps/nghttp3/lib/nghttp3_debug.c create mode 100644 deps/nghttp3/lib/nghttp3_debug.h create mode 100644 deps/nghttp3/lib/nghttp3_err.c create mode 100644 deps/nghttp3/lib/nghttp3_err.h create mode 100644 deps/nghttp3/lib/nghttp3_frame.c create mode 100644 deps/nghttp3/lib/nghttp3_frame.h create mode 100644 deps/nghttp3/lib/nghttp3_gaptr.c create mode 100644 deps/nghttp3/lib/nghttp3_gaptr.h create mode 100644 deps/nghttp3/lib/nghttp3_http.c create mode 100644 deps/nghttp3/lib/nghttp3_http.h create mode 100644 deps/nghttp3/lib/nghttp3_idtr.c create mode 100644 deps/nghttp3/lib/nghttp3_idtr.h create mode 100644 deps/nghttp3/lib/nghttp3_ksl.c create mode 100644 deps/nghttp3/lib/nghttp3_ksl.h create mode 100644 deps/nghttp3/lib/nghttp3_macro.h create mode 100644 deps/nghttp3/lib/nghttp3_map.c create mode 100644 deps/nghttp3/lib/nghttp3_map.h create mode 100644 deps/nghttp3/lib/nghttp3_mem.c create mode 100644 deps/nghttp3/lib/nghttp3_mem.h create mode 100644 deps/nghttp3/lib/nghttp3_pq.c create mode 100644 deps/nghttp3/lib/nghttp3_pq.h create mode 100644 deps/nghttp3/lib/nghttp3_qpack.c create mode 100644 deps/nghttp3/lib/nghttp3_qpack.h create mode 100644 deps/nghttp3/lib/nghttp3_qpack_huffman.c create mode 100644 deps/nghttp3/lib/nghttp3_qpack_huffman.h create mode 100644 deps/nghttp3/lib/nghttp3_qpack_huffman_data.c create mode 100644 deps/nghttp3/lib/nghttp3_range.c create mode 100644 deps/nghttp3/lib/nghttp3_range.h create mode 100644 deps/nghttp3/lib/nghttp3_rcbuf.c create mode 100644 deps/nghttp3/lib/nghttp3_rcbuf.h create mode 100644 deps/nghttp3/lib/nghttp3_ringbuf.c create mode 100644 deps/nghttp3/lib/nghttp3_ringbuf.h create mode 100644 deps/nghttp3/lib/nghttp3_str.c create mode 100644 deps/nghttp3/lib/nghttp3_str.h create mode 100644 deps/nghttp3/lib/nghttp3_stream.c create mode 100644 deps/nghttp3/lib/nghttp3_stream.h create mode 100644 deps/nghttp3/lib/nghttp3_tnode.c create mode 100644 deps/nghttp3/lib/nghttp3_tnode.h create mode 100644 deps/nghttp3/lib/nghttp3_vec.c create mode 100644 deps/nghttp3/lib/nghttp3_vec.h create mode 100644 deps/nghttp3/lib/nghttp3_version.c create mode 100644 deps/nghttp3/nghttp3.gyp create mode 100644 deps/ngtcp2/COPYING create mode 100644 deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h create mode 100644 deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h create mode 100644 deps/ngtcp2/crypto/openssl/openssl.c create mode 100644 deps/ngtcp2/crypto/shared.c create mode 100644 deps/ngtcp2/crypto/shared.h create mode 100644 deps/ngtcp2/lib/includes/config.h create mode 100644 deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h create mode 100644 deps/ngtcp2/lib/includes/ngtcp2/version.h create mode 100644 deps/ngtcp2/lib/includes/ngtcp2/version.h.in create mode 100644 deps/ngtcp2/lib/ngtcp2_acktr.c create mode 100644 deps/ngtcp2/lib/ngtcp2_acktr.h create mode 100644 deps/ngtcp2/lib/ngtcp2_addr.c create mode 100644 deps/ngtcp2/lib/ngtcp2_addr.h create mode 100644 deps/ngtcp2/lib/ngtcp2_buf.c create mode 100644 deps/ngtcp2/lib/ngtcp2_buf.h create mode 100644 deps/ngtcp2/lib/ngtcp2_cc.c create mode 100644 deps/ngtcp2/lib/ngtcp2_cc.h create mode 100644 deps/ngtcp2/lib/ngtcp2_cid.c create mode 100644 deps/ngtcp2/lib/ngtcp2_cid.h create mode 100644 deps/ngtcp2/lib/ngtcp2_conn.c create mode 100644 deps/ngtcp2/lib/ngtcp2_conn.h create mode 100644 deps/ngtcp2/lib/ngtcp2_conv.c create mode 100644 deps/ngtcp2/lib/ngtcp2_conv.h create mode 100644 deps/ngtcp2/lib/ngtcp2_crypto.c create mode 100644 deps/ngtcp2/lib/ngtcp2_crypto.h create mode 100644 deps/ngtcp2/lib/ngtcp2_err.c create mode 100644 deps/ngtcp2/lib/ngtcp2_err.h create mode 100644 deps/ngtcp2/lib/ngtcp2_gaptr.c create mode 100644 deps/ngtcp2/lib/ngtcp2_gaptr.h create mode 100644 deps/ngtcp2/lib/ngtcp2_idtr.c create mode 100644 deps/ngtcp2/lib/ngtcp2_idtr.h create mode 100644 deps/ngtcp2/lib/ngtcp2_ksl.c create mode 100644 deps/ngtcp2/lib/ngtcp2_ksl.h create mode 100644 deps/ngtcp2/lib/ngtcp2_log.c create mode 100644 deps/ngtcp2/lib/ngtcp2_log.h create mode 100644 deps/ngtcp2/lib/ngtcp2_macro.h create mode 100644 deps/ngtcp2/lib/ngtcp2_map.c create mode 100644 deps/ngtcp2/lib/ngtcp2_map.h create mode 100644 deps/ngtcp2/lib/ngtcp2_mem.c create mode 100644 deps/ngtcp2/lib/ngtcp2_mem.h create mode 100644 deps/ngtcp2/lib/ngtcp2_net.h create mode 100644 deps/ngtcp2/lib/ngtcp2_path.c create mode 100644 deps/ngtcp2/lib/ngtcp2_path.h create mode 100644 deps/ngtcp2/lib/ngtcp2_pipeack.c create mode 100644 deps/ngtcp2/lib/ngtcp2_pipeack.h create mode 100644 deps/ngtcp2/lib/ngtcp2_pkt.c create mode 100644 deps/ngtcp2/lib/ngtcp2_pkt.h create mode 100644 deps/ngtcp2/lib/ngtcp2_ppe.c create mode 100644 deps/ngtcp2/lib/ngtcp2_ppe.h create mode 100644 deps/ngtcp2/lib/ngtcp2_pq.c create mode 100644 deps/ngtcp2/lib/ngtcp2_pq.h create mode 100644 deps/ngtcp2/lib/ngtcp2_psl.c create mode 100644 deps/ngtcp2/lib/ngtcp2_psl.h create mode 100644 deps/ngtcp2/lib/ngtcp2_pv.c create mode 100644 deps/ngtcp2/lib/ngtcp2_pv.h create mode 100644 deps/ngtcp2/lib/ngtcp2_qlog.c create mode 100644 deps/ngtcp2/lib/ngtcp2_qlog.h create mode 100644 deps/ngtcp2/lib/ngtcp2_range.c create mode 100644 deps/ngtcp2/lib/ngtcp2_range.h create mode 100644 deps/ngtcp2/lib/ngtcp2_rcvry.h create mode 100644 deps/ngtcp2/lib/ngtcp2_ringbuf.c create mode 100644 deps/ngtcp2/lib/ngtcp2_ringbuf.h create mode 100644 deps/ngtcp2/lib/ngtcp2_rob.c create mode 100644 deps/ngtcp2/lib/ngtcp2_rob.h create mode 100644 deps/ngtcp2/lib/ngtcp2_rst.c create mode 100644 deps/ngtcp2/lib/ngtcp2_rst.h create mode 100644 deps/ngtcp2/lib/ngtcp2_rtb.c create mode 100644 deps/ngtcp2/lib/ngtcp2_rtb.h create mode 100644 deps/ngtcp2/lib/ngtcp2_str.c create mode 100644 deps/ngtcp2/lib/ngtcp2_str.h create mode 100644 deps/ngtcp2/lib/ngtcp2_strm.c create mode 100644 deps/ngtcp2/lib/ngtcp2_strm.h create mode 100644 deps/ngtcp2/lib/ngtcp2_vec.c create mode 100644 deps/ngtcp2/lib/ngtcp2_vec.h create mode 100644 deps/ngtcp2/lib/ngtcp2_version.c create mode 100644 deps/ngtcp2/ngtcp2.gyp diff --git a/deps/nghttp3/COPYING b/deps/nghttp3/COPYING new file mode 100644 index 00000000000000..37562ea58cd74c --- /dev/null +++ b/deps/nghttp3/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2019 nghttp3 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/nghttp3/lib/includes/config.h b/deps/nghttp3/lib/includes/config.h new file mode 100644 index 00000000000000..0aee7749bae78e --- /dev/null +++ b/deps/nghttp3/lib/includes/config.h @@ -0,0 +1,39 @@ + +/* Edited to match src/node.h. */ +#include + +#ifdef _WIN32 +#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) +typedef intptr_t ssize_t; +# define _SSIZE_T_ +# define _SSIZE_T_DEFINED +#endif +#else // !_WIN32 +# include // size_t, ssize_t +#endif // _WIN32 + +#ifdef _MSC_VER +# include +# define __builtin_popcount __popcnt +#endif + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ diff --git a/deps/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/nghttp3/lib/includes/nghttp3/nghttp3.h new file mode 100644 index 00000000000000..5c3a95c4c6f3b5 --- /dev/null +++ b/deps/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -0,0 +1,1759 @@ +/* + * nghttp3 + * + * Copyright (c) 2018 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_H +#define NGHTTP3_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include +#include + +#include + +#ifdef NGHTTP3_STATICLIB +# define NGHTTP3_EXTERN +#elif defined(WIN32) +# ifdef BUILDING_NGHTTP3 +# define NGHTTP3_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGHTTP3 */ +# define NGHTTP3_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGHTTP3 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGHTTP3 +# define NGHTTP3_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGHTTP3 */ +# define NGHTTP3_EXTERN +# endif /* !BUILDING_NGHTTP3 */ +#endif /* !defined(WIN32) */ + +typedef ptrdiff_t nghttp3_ssize; + +typedef enum { + NGHTTP3_ERR_INVALID_ARGUMENT = -101, + NGHTTP3_ERR_NOBUF = -102, + NGHTTP3_ERR_INVALID_STATE = -103, + NGHTTP3_ERR_WOULDBLOCK = -104, + NGHTTP3_ERR_STREAM_IN_USE = -105, + NGHTTP3_ERR_PUSH_ID_BLOCKED = -106, + NGHTTP3_ERR_MALFORMED_HTTP_HEADER = -107, + NGHTTP3_ERR_REMOVE_HTTP_HEADER = -108, + NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING = -109, + NGHTTP3_ERR_TOO_LATE = -110, + NGHTTP3_ERR_QPACK_FATAL = -111, + NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE = -112, + NGHTTP3_ERR_IGNORE_STREAM = -113, + NGHTTP3_ERR_STREAM_NOT_FOUND = -114, + NGHTTP3_ERR_IGNORE_PUSH_PROMISE = -115, + NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED = -402, + NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR = -403, + NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR = -404, + NGHTTP3_ERR_H3_FRAME_UNEXPECTED = -408, + NGHTTP3_ERR_H3_FRAME_ERROR = -409, + NGHTTP3_ERR_H3_MISSING_SETTINGS = -665, + NGHTTP3_ERR_H3_INTERNAL_ERROR = -667, + NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM = -668, + NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR = -669, + NGHTTP3_ERR_H3_ID_ERROR = -670, + NGHTTP3_ERR_H3_SETTINGS_ERROR = -671, + NGHTTP3_ERR_H3_STREAM_CREATION_ERROR = -672, + NGHTTP3_ERR_FATAL = -900, + NGHTTP3_ERR_NOMEM = -901, + NGHTTP3_ERR_CALLBACK_FAILURE = -902 +} nghttp3_lib_error; + +#define NGHTTP3_H3_NO_ERROR 0x0100 +#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101 +#define NGHTTP3_H3_INTERNAL_ERROR 0x0102 +#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103 +#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104 +#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105 +#define NGHTTP3_H3_FRAME_ERROR 0x0106 +#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107 +#define NGHTTP3_H3_ID_ERROR 0x0108 +#define NGHTTP3_H3_SETTINGS_ERROR 0x0109 +#define NGHTTP3_H3_MISSING_SETTINGS 0x010a +#define NGHTTP3_H3_REQUEST_REJECTED 0x010b +#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c +#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d +#define NGHTTP3_H3_CONNECT_ERROR 0x010f +#define NGHTTP3_H3_VERSION_FALLBACK 0x0110 +#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200 +#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201 +#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202 + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void (*nghttp3_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void conn_new() { + * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp3_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp3_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp3_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp3_realloc realloc; +} nghttp3_mem; + +/** + * @function + * + * `nghttp3_mem_default` returns the default memory allocator which + * uses malloc/calloc/realloc/free. + */ +NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void); + +/** + * @struct + * + * nghttp3_vec is struct iovec compatible structure to reference + * arbitrary array of bytes. + */ +typedef struct { + /** + * base points to the data. + */ + uint8_t *base; + /** + * len is the number of bytes which the buffer pointed by base + * contains. + */ + size_t len; +} nghttp3_vec; + +struct nghttp3_rcbuf; + +/** + * @struct + * + * :type:`nghttp3_rcbuf` is the object representing reference counted + * buffer. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp3_rcbuf nghttp3_rcbuf; + +/** + * @function + * + * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by + * 1. + */ +NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by + * 1. If the reference count becomes zero, the object pointed by + * |rcbuf| will be freed. In this case, application must not use + * |rcbuf| again. + */ +NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by + * |rcbuf|. + */ +NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer + * is statically allocated, and 0 otherwise. This can be useful for + * language bindings that wish to avoid creating duplicate strings for + * these buffers. + */ +NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf); + +/** + * @struct + * + * :type:`nghttp3_buf` is the variable size buffer. + */ +typedef struct { + /** + * begin points to the beginning of the buffer. + */ + uint8_t *begin; + /** + * end points to the one beyond of the last byte of the buffer + */ + uint8_t *end; + /** + * pos pointers to the start of data. Typically, this points to the + * point that next data should be read. Initially, it points to + * |begin|. + */ + uint8_t *pos; + /** + * last points to the one beyond of the last data of the buffer. + * Typically, new data is written at this point. Initially, it + * points to |begin|. + */ + uint8_t *last; +} nghttp3_buf; + +/** + * @function + * + * `nghttp3_buf_init` initializes empty |buf|. + */ +NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_free` frees resources allocated for |buf| using |mem| + * as memory allocator. buf->begin must be a heap buffer allocated by + * |mem|. + */ +NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_buf_left` returns the number of additional bytes which can + * be written to the underlying buffer. In other words, it returns + * buf->end - buf->last. + */ +NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_len` returns the number of bytes left to read. In + * other words, it returns buf->last - buf->pos. + */ +NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin. + */ +NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); + +/** + * @enum + * + * :type:`nghttp3_nv_flag` is the flags for header field name/value + * pair. + */ +typedef enum { + /** + * :enum:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. + */ + NGHTTP3_NV_FLAG_NONE = 0, + /** + * :enum:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this + * name/value pair must not be indexed. Other implementation calls + * this bit as "sensitive". + */ + NGHTTP3_NV_FLAG_NEVER_INDEX = 0x01, + /** + * :enum:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by + * application. If this flag is set, the library does not make a + * copy of header field name. This could improve performance. + */ + NGHTTP3_NV_FLAG_NO_COPY_NAME = 0x02, + /** + * :enum:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by + * application. If this flag is set, the library does not make a + * copy of header field value. This could improve performance. + */ + NGHTTP3_NV_FLAG_NO_COPY_VALUE = 0x04 +} nghttp3_nv_flag; + +/** + * @struct + * + * :type:`nghttp3_nv` is the name/value pair, which mainly used to + * represent header fields. + */ +typedef struct { + /** + * name is the header field name. + */ + uint8_t *name; + /** + * value is the header field value. + */ + uint8_t *value; + /** + * namelen is the length of the |name|, excluding terminating NULL. + */ + size_t namelen; + /** + * valuelen is the length of the |value|, excluding terminating + * NULL. + */ + size_t valuelen; + /** + * flags is bitwise OR of one or more of :type:`nghttp3_nv_flag`. + */ + uint8_t flags; +} nghttp3_nv; + +/* Generated by mkstatichdtbl.py */ +typedef enum { + NGHTTP3_QPACK_TOKEN__AUTHORITY = 0, + NGHTTP3_QPACK_TOKEN__PATH = 8, + NGHTTP3_QPACK_TOKEN_AGE = 43, + NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52, + NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55, + NGHTTP3_QPACK_TOKEN_COOKIE = 68, + NGHTTP3_QPACK_TOKEN_DATE = 69, + NGHTTP3_QPACK_TOKEN_ETAG = 71, + NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74, + NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75, + NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77, + NGHTTP3_QPACK_TOKEN_LINK = 78, + NGHTTP3_QPACK_TOKEN_LOCATION = 79, + NGHTTP3_QPACK_TOKEN_REFERER = 83, + NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85, + NGHTTP3_QPACK_TOKEN__METHOD = 1, + NGHTTP3_QPACK_TOKEN__SCHEME = 9, + NGHTTP3_QPACK_TOKEN__STATUS = 11, + NGHTTP3_QPACK_TOKEN_ACCEPT = 25, + NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27, + NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38, + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46, + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53, + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57, + NGHTTP3_QPACK_TOKEN_RANGE = 82, + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86, + NGHTTP3_QPACK_TOKEN_VARY = 92, + NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94, + NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98, + NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40, + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41, + NGHTTP3_QPACK_TOKEN_ALT_SVC = 44, + NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45, + NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56, + NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70, + NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72, + NGHTTP3_QPACK_TOKEN_FORWARDED = 73, + NGHTTP3_QPACK_TOKEN_IF_RANGE = 76, + NGHTTP3_QPACK_TOKEN_ORIGIN = 80, + NGHTTP3_QPACK_TOKEN_PURPOSE = 81, + NGHTTP3_QPACK_TOKEN_SERVER = 84, + NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89, + NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90, + NGHTTP3_QPACK_TOKEN_USER_AGENT = 91, + NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95, + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96, + /* Additional header fields for HTTP messaging validation */ + NGHTTP3_QPACK_TOKEN_HOST = 1000, + NGHTTP3_QPACK_TOKEN_CONNECTION, + NGHTTP3_QPACK_TOKEN_KEEP_ALIVE, + NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION, + NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING, + NGHTTP3_QPACK_TOKEN_UPGRADE, + NGHTTP3_QPACK_TOKEN_TE, + NGHTTP3_QPACK_TOKEN__PROTOCOL +} nghttp3_qpack_token; + +/** + * @struct + * + * nghttp3_qpack_nv represents header field name/value pair just like + * :type:`nghttp3_nv`. It is an extended version of + * :type:`nghttp3_nv` and has reference counted buffers and tokens + * which might be useful for applications. + */ +typedef struct { + /* The buffer containing header field name. NULL-termination is + guaranteed. */ + nghttp3_rcbuf *name; + /* The buffer containing header field value. NULL-termination is + guaranteed. */ + nghttp3_rcbuf *value; + /* nghttp3_qpack_token value for name. It could be -1 if we have no + token for that header field name. */ + int32_t token; + /* Bitwise OR of one or more of nghttp3_nv_flag. */ + uint8_t flags; +} nghttp3_qpack_nv; + +struct nghttp3_qpack_encoder; + +/** + * @struct + * + * :type:`nghttp3_qpack_encoder` is QPACK encoder. + */ +typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; + +/** + * @function + * + * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder| + * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic + * table size. |max_blocked| is the maximum number of streams which + * can be blocked. |mem| is a memory allocator. This function + * allocates memory for :type:`nghttp3_qpack_encoder` itself and + * assigns its pointer to |*pencoder| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t max_dtable_size, + size_t max_blocked, + const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|. + * This function frees memory pointed by |encoder| itself. + */ +NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder); + +/** + * @function + * + * `nghttp3_qpack_encoder_encode` encodes the list of header fields + * |nva|. |nvlen| is the length of |nva|. |stream_id| is the + * identifier of the stream which this header fields belong to. This + * function writes header block prefix, encoded header fields, and + * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The + * last field of nghttp3_buf will be adjusted when data is written. + * An application should write |pbuf| and |rbuf| to the request stream + * in this order. + * + * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty + * buffer. It is fine to pass a buffer initialized by + * nghttp3_buf_init(buf). This function allocates memory for these + * buffers as necessary. In particular, it frees and expands buffer + * if the current capacity of buffer is not enough. If begin field of + * any buffer is not NULL, it must be allocated by the same memory + * allocator passed to `nghttp3_qpack_encoder_new()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory + * :enum:`NGHTTP3_ERR_QPACK_FATAL` + * |encoder| is in unrecoverable error state and cannot be used + * anymore. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf, + nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen); + +/** + * @function + * + * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The + * buffer pointed by |src| of length |srclen| contains decoder stream. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory + * :enum:`NGHTTP3_ERR_QPACK_FATAL` + * |encoder| is in unrecoverable error state and cannot be used + * anymore. + * :enum:`NGHTTP3_ERR_QPACK_DECODER_STREAM` + * |encoder| is unable to process input because it is malformed. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder( + nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table + * size to |max_dtable_size|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :enum:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |max_dtable_size| exceeds the hard limit that decoder specifies. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum + * dynamic table size to |hard_max_dtable_size|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * TBD + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_size); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams + * which can be blocked to |max_blocked|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * TBD + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, + size_t max_blocked); + +/** + * @function + * + * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header + * block for a stream denoted by |stream_id| was acknowledged by + * decoder. This function is provided for debugging purpose only. In + * HTTP/3, |encoder| knows acknowledgement of header block by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_encoder_add_insert_count` increments known received + * count of |encoder| by |n|. This function is provided for debugging + * purpose only. In HTTP/3, |encoder| increments known received count + * by reading decoder stream with + * `nghttp3_qpack_encoder_read_decoder()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP3_QPACK_DECODER_STREAM` + * |n| is too large. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, + size_t n); + +/** + * @function + * + * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all + * encoded header blocks are acknowledged. This function is provided + * for debugging purpose only. In HTTP/3, |encoder| knows this by + * reading decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder); + +/** + * @function + * + * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream + * denoted by |stream_id| is cancelled. This function is provided for + * debugging purpose only. In HTTP/3, |encoder| knows this by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_encoder_get_num_blocked` returns the number of + * streams which is potentially blocked at decoder side. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder); + +struct nghttp3_qpack_stream_context; + +/** + * @struct + * + * :type:`nghttp3_qpack_stream_context` is a decoder context for an + * individual stream. + */ +typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context; + +/** + * @function + * + * `nghttp3_qpack_stream_context_new` initializes stream context. + * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem| + * is a memory allocator. This function allocates memory for + * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer + * to |*psctx| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, + int64_t stream_id, const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_stream_context_del` frees memory allocated for + * |sctx|. This function frees memory pointed by |sctx| itself. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx); + +/** + * @function + * + * `nghttp3_qpack_stream_context_get_ricnt` returns required insert + * count. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx); + +struct nghttp3_qpack_decoder; + +/** + * @struct + * + * `nghttp3_qpack_decoder` is QPACK decoder. + */ +typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; + +/** + * @function + * + * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder| + * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic + * table size. |max_blocked| is the maximum number of streams which + * can be blocked. |mem| is a memory allocator. This function + * allocates memory for :type:`nghttp3_qpack_decoder` itself and + * assigns its pointer to |*pdecoder| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, + size_t max_dtable_size, + size_t max_blocked, + const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|. + * This function frees memory pointed by |decoder| itself. + */ +NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder); + +/** + * @function + * + * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The + * buffer pointed by |src| of length |srclen| contains encoder stream. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP3_ERR_QPACK_FATAL` + * |decoder| is in unrecoverable error state and cannot be used + * anymore. + * :enum:`NGHTTP3_ERR_QPACK_ENCODER_STREAM` + * Could not interpret encoder stream instruction. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder( + nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen); + +/** + * @function + * + * `nghttp3_qpack_decoder_get_icnt` returns insert count. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); + +/** + * @enum + * + * :type:`nghttp3_qpack_decode_flag` is a set of flags for decoder. + */ +typedef enum { + /** + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag + * set. + */ + NGHTTP3_QPACK_DECODE_FLAG_NONE, + /** + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header + * field is successfully decoded. + */ + NGHTTP3_QPACK_DECODE_FLAG_EMIT = 0x01, + /** + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header + * fields have been decoded. + */ + NGHTTP3_QPACK_DECODE_FLAG_FINAL = 0x02, + /** + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding + * has been blocked. + */ + NGHTTP3_QPACK_DECODE_FLAG_BLOCKED = 0x04 +} nghttp3_qpack_decode_flag; + +/** + * @function + * + * `nghttp3_qpack_decoder_read_request` reads request stream. The + * request stream is given as the buffer pointed by |src| of length + * |srclen|. |sctx| is the stream context and it must be initialized + * by `nghttp3_qpack_stream_context_init()`. |*pflags| must be + * non-NULL pointer. |nv| must be non-NULL pointer. + * + * If this function succeeds, it assigns flags to |*pflags|. If + * |*pflags| has :enum:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a decoded + * header field is assigned to |nv|. If |*pflags| has + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields have + * been successfully decoded. If |*pflags| has + * :enum:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked + * due to required insert count. + * + * When a header field is decoded, an application receives it in |nv|. + * nv->name and nv->value are reference counted buffer, and the their + * reference counts are already incremented for application use. + * Therefore, when application finishes processing the header field, + * it must call nghttp3_rcbuf_decref(nv->name) and + * nghttp3_rcbuf_decref(nv->value) or memory leak might occur. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP3_ERR_QPACK_FATAL` + * |decoder| is in unrecoverable error state and cannot be used + * anymore. + * :enum:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` + * Could not interpret header block instruction. + * :enum:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` + * Header field is too large. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request( + nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen, + int fin); + +/** + * @function + * + * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into + * |dbuf|. + * + * The caller must ensure that nghttp3_buf_left(dbuf) >= + * nghttp3_qpack_decoder_get_decoder_streamlen(decoder). + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, + nghttp3_buf *dbuf); + +/** + * @function + * + * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of + * decoder stream. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder); + +/** + * @function + * + * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for + * stream denoted by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP3_ERR_QPACK_FATAL` + * Decoder stream overflow. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum + * dynamic table size. Normally, the maximum capacity is communicated + * in encoder stream. This function is provided for debugging and + * testing purpose. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, + size_t cap); + +/** + * @function + * + * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder| + * the maximum number of concurrent streams that a remote endpoint can + * open, including both bidirectional and unidirectional streams which + * potentially receive QPACK encoded HEADERS frame. This value is + * used as a hint to limit the length of decoder stream. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder, + size_t max_concurrent_streams); + +/** + * @function + * + * `nghttp3_strerror` returns textual representation of |liberr| which + * should be one of error codes defined in :type:`nghttp3_lib_error`. + */ +NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr); + +/** + * @function + * + * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application + * error code which corresponds to |liberr|. + */ +NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr); + +/** + * @functypedef + * + * :type:`nghttp3_debug_vprintf_callback` is a callback function + * invoked when the library outputs debug logging. The function is + * called with arguments suitable for ``vfprintf(3)`` + * + * The debug output is only enabled if the library is built with + * ``DEBUGBUILD`` macro defined. + */ +typedef void (*nghttp3_debug_vprintf_callback)(const char *format, + va_list args); + +/** + * @function + * + * `nghttp3_set_debug_vprintf_callback` sets a debug output callback + * called by the library when built with ``DEBUGBUILD`` macro defined. + * If this option is not used, debug log is written into standard + * error output. + * + * For builds without ``DEBUGBUILD`` macro defined, this function is + * noop. + * + * Note that building with ``DEBUGBUILD`` may cause significant + * performance penalty to libnghttp3 because of extra processing. It + * should be used for debugging purpose only. + * + * .. Warning:: + * + * Building with ``DEBUGBUILD`` may cause significant performance + * penalty to libnghttp3 because of extra processing. It should be + * used for debugging purpose only. We write this two times because + * this is important. + */ +NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback); + +struct nghttp3_conn; +typedef struct nghttp3_conn nghttp3_conn; + +/** + * @functypedef + * + * :type:`nghttp3_acked_stream_data` is a callback function which is + * invoked when data sent on stream denoted by |stream_id| supplied + * from application is acknowledged by remote endpoint. The number of + * bytes acknowledged is given in |datalen|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, + size_t datalen, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_conn_stream_close` is a callback function which is + * invoked when a stream identified by |stream_id| is closed. + * |app_error_code| indicates the reason of this closure. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_stream_close)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_data` is a callback function which is invoked + * when a part of request or response body on stream identified by + * |stream_id| is received. |data| points to the received data and + * its length is |datalen|. + * + * The application is responsible for increasing flow control credit + * by |datalen| bytes. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_data)(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *data, size_t datalen, + void *conn_user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_deferred_consume` is a callback function which is + * invoked when the library consumed |consumed| bytes for a stream + * identified by |stream_id|. This callback is used to notify the + * consumed bytes for stream blocked by QPACK decoder. The + * application is responsible for increasing flow control credit by + * |consumed| bytes. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, int32_t token, + nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *conn_user_data, + void *stream_user_data); + +typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_stream` is a callback function which is invoked + * when the receiving side of stream is closed. For server, this + * callback function is invoked when HTTP request is received + * completely. For client, this callback function is invoked when + * HTTP response is received completely. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_cancel_push` is a callback function which is invoked + * when the push identified by |push_id| is cancelled by remote + * endpoint. If a stream has been bound to the push ID, |stream_id| + * contains the stream ID and |stream_user_data| points to the stream + * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is + * NULL. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_send_stop_sending` is a callback function which is + * invoked when the library asks application to send STOP_SENDING to + * the stream identified by |stream_id|. |app_error_code| indicates + * the reason for this action. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_push_stream` is a callback function which is invoked + * when a push stream identified by |stream_id| is opened with + * |push_id|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :enum:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id, void *conn_user_data); + +typedef struct { + nghttp3_acked_stream_data acked_stream_data; + nghttp3_stream_close stream_close; + nghttp3_recv_data recv_data; + nghttp3_deferred_consume deferred_consume; + nghttp3_begin_headers begin_headers; + nghttp3_recv_header recv_header; + nghttp3_end_headers end_headers; + nghttp3_begin_headers begin_trailers; + nghttp3_recv_header recv_trailer; + nghttp3_end_headers end_trailers; + nghttp3_begin_push_promise begin_push_promise; + nghttp3_recv_push_promise recv_push_promise; + nghttp3_end_push_promise end_push_promise; + nghttp3_cancel_push cancel_push; + nghttp3_send_stop_sending send_stop_sending; + nghttp3_push_stream push_stream; + nghttp3_end_stream end_stream; +} nghttp3_conn_callbacks; + +typedef struct { + uint64_t max_header_list_size; + uint64_t max_pushes; + size_t qpack_max_table_capacity; + size_t qpack_blocked_streams; +} nghttp3_conn_settings; + +NGHTTP3_EXTERN void +nghttp3_conn_settings_default(nghttp3_conn_settings *settings); + +NGHTTP3_EXTERN int +nghttp3_conn_client_new(nghttp3_conn **pconn, + const nghttp3_conn_callbacks *callbacks, + const nghttp3_conn_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); + +NGHTTP3_EXTERN int +nghttp3_conn_server_new(nghttp3_conn **pconn, + const nghttp3_conn_callbacks *callbacks, + const nghttp3_conn_settings *settings, + const nghttp3_mem *mem, void *conn_user_data); + +NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_bind_control_stream` binds stream denoted by + * |stream_id| to outgoing unidirectional control stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_INVALID_STATE` + * Control stream has already corresponding stream ID. + * TBD + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_bind_qpack_streams` binds stream denoted by + * |qenc_stream_id| to outgoing QPACK encoder stream and stream + * denoted by |qdec_stream_id| to outgoing QPACK encoder stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP3_ERR_INVALID_STATE` + * QPACK encoder/decoder stream have already corresponding stream + * IDs. + * TBD + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, + int64_t qenc_stream_id, + int64_t qdec_stream_id); + +typedef enum { + NGHTTP3_FRAME_DATA = 0x00, + NGHTTP3_FRAME_HEADERS = 0x01, + NGHTTP3_FRAME_CANCEL_PUSH = 0x03, + NGHTTP3_FRAME_SETTINGS = 0x04, + NGHTTP3_FRAME_PUSH_PROMISE = 0x05, + NGHTTP3_FRAME_GOAWAY = 0x07, + NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d, +} nghttp3_frame_type; + +typedef struct { + int64_t type; + int64_t length; +} nghttp3_frame_hd; + +typedef struct { + nghttp3_frame_hd hd; +} nghttp3_frame_data; + +typedef struct { + nghttp3_frame_hd hd; + nghttp3_nv *nva; + size_t nvlen; +} nghttp3_frame_headers; + +typedef struct { + nghttp3_frame_hd hd; + int64_t push_id; +} nghttp3_frame_cancel_push; + +#define NGHTTP3_SETTINGS_ID_MAX_HEADER_LIST_SIZE 0x06 +#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 +#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 + +typedef struct { + uint64_t id; + uint64_t value; +} nghttp3_settings_entry; + +typedef struct { + nghttp3_frame_hd hd; + size_t niv; + nghttp3_settings_entry iv[1]; +} nghttp3_frame_settings; + +typedef struct { + nghttp3_frame_hd hd; + nghttp3_nv *nva; + size_t nvlen; + int64_t push_id; +} nghttp3_frame_push_promise; + +typedef struct { + nghttp3_frame_hd hd; + int64_t stream_id; +} nghttp3_frame_goaway; + +typedef struct { + nghttp3_frame_hd hd; + int64_t push_id; +} nghttp3_frame_max_push_id; + +typedef union { + nghttp3_frame_hd hd; + nghttp3_frame_data data; + nghttp3_frame_headers headers; + nghttp3_frame_cancel_push cancel_push; + nghttp3_frame_settings settings; + nghttp3_frame_push_promise push_promise; + nghttp3_frame_goaway goaway; + nghttp3_frame_max_push_id max_push_id; +} nghttp3_frame; + +/** + * @function + * + * nghttp3_conn_read_stream reads data |src| of length |srclen| on + * stream identified by |stream_id|. It returns the number of bytes + * consumed. The "consumed" means that application can increase flow + * control credit (both stream and connection) of underlying QUIC + * connection by that amount. It does not include the amount of data + * carried by DATA frame which contains application data (excluding + * any control or QPACK unidirectional streams) . See + * type:`nghttp3_recv_data` to handle those bytes. If |fin| is + * nonzero, this is the last data from remote endpoint in this stream. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, + int64_t stream_id, + const uint8_t *src, + size_t srclen, int fin); + +/** + * @function + * + * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of + * length |veccnt| and returns the number of nghttp3_vec object in + * which it stored data. It stores stream ID to |*pstream_id|. An + * application has to call `nghttp3_conn_add_write_offset` to inform + * |conn| of the actual number of bytes that underlying QUIC stack + * accepted. |*pfin| will be nonzero if this is the last data to + * send. If there is no stream to write data or send fin, this + * function returns 0, and -1 is assigned to |*pstream_id|. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, + int64_t *pstream_id, + int *pfin, + nghttp3_vec *vec, + size_t veccnt); + +/** + * @function + * + * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes + * |n| for stream denoted by |stream_id| QUIC stack accepted. + * + * If stream has no data to send but just sends fin (closing the write + * side of a stream), the number of bytes sent is 0. It is important + * to call this function even if |n| is 0 in this case. It is safe to + * call this function if |n| is 0. + * + * `nghttp3_conn_writev_stream` must be called before calling this + * function to get data to send, and those data must be fed into QUIC + * stack. + */ +NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn, + int64_t stream_id, size_t n); + +/** + * @function + * + * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n| + * for stream denoted by |stream_id| QUIC stack has acknowledged. + */ +NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, + int64_t stream_id, size_t n); + +/** + * @function + * + * `nghttp3_conn_block_stream` tells the library that stream + * identified by |stream_id| is blocked due to QUIC flow control. + */ +NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_unblock_stream` tells the library that stream + * identified by |stream_id| which was blocked by QUIC flow control is + * unblocked. + */ +NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_resume_stream` resumes stream identified by + * |stream_id| which was previously unable to provide data. + */ +NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_close_stream` closes stream identified by + * |stream_id|. |app_error_code| is the reason of the closure. + */ +NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `nghttp3_conn_reset_stream` must be called if stream identified by + * |stream_id| is reset by a remote endpoint. This is required in + * order to cancel QPACK stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, + int64_t stream_id); + +typedef enum { + NGHTTP3_DATA_FLAG_NONE = 0x00, + /** + * ``NGHTTP3_DATA_FLAG_EOF`` indicates that all request or response + * body has been provided to the library. It also indicates that + * sending side of stream is closed unless + * :enum:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same + * time. + */ + NGHTTP3_DATA_FLAG_EOF = 0x01, + /** + * ``NGHTTP3_DATA_FLAG_NO_END_STREAM`` indicates that sending side + * of stream is not closed even if NGHTTP3_DATA_FLAG_EOF is set. + * Usually this flag is used to send trailer fields with + * `nghttp3_conn_submit_trailers()`. If + * `nghttp3_conn_submit_trailers()` has been called, regardless of + * this flag, the submitted trailer fields are sent. + */ + NGHTTP3_DATA_FLAG_NO_END_STREAM = 0x02 +} nghttp3_data_flag; + +/** + * @function + * + * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the + * cumulative number of bidirectional streams that client can open. + */ +NGHTTP3_EXTERN void +nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, + uint64_t max_streams); + +/** + * @function + * + * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum + * number of concurrent streams that a remote endpoint can open, + * including both bidirectional and unidirectional streams which + * potentially receive QPACK encoded HEADERS frame. This value is + * used as a hint to limit the internal resource consumption. + */ +NGHTTP3_EXTERN void +nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, + size_t max_concurrent_streams); + +/** + * @functypedef + * + * :type:`nghttp3_read_data_callback` is a callback function invoked + * when the library asks an application to provide stream data for a + * stream denoted by |stream_id|. + * + * The library provides |vec| of length |veccnt| to the application. + * The application should fill data and its length to |vec|. It has + * to return the number of the filled objects. The application must + * retain data until they are safe to free. It is notified by + * :type:`nghttp3_acked_stream_data` callback. + * + * If this is the last data to send (or there is no data to send + * because all data have been sent already), set + * :enum:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|. + * + * If the application is unable to provide data temporarily, return + * :enum:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide + * data, call `nghttp3_conn_resume_stream()`. + * + * The callback should return the number of objects in |vec| that the + * application filled if it succeeds, or + * :enum:`NGHTTP3_ERR_CALLBACK_FAILURE`. + * + * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this + * stream. + */ +typedef nghttp3_ssize (*nghttp3_read_data_callback)( + nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *conn_user_data, void *stream_user_data); + +/** + * @struct + * + * :type:`nghttp3_data_reader` specifies the way how to generate + * request or response body. + */ +typedef struct { + /** + * read_data is a callback function to generate body. + */ + nghttp3_read_data_callback read_data; +} nghttp3_data_reader; + +/** + * @function + * + * `nghttp3_conn_submit_request` submits HTTP request header fields + * and body on the stream identified by |stream_id|. |stream_id| must + * be a client initiated bidirectional stream. Only client can submit + * HTTP request. |nva| of length |nvlen| specifies HTTP request + * header fields. |dr| specifies a request body. If there is no + * request body, specify NULL. If |dr| is NULL, it implies the end of + * stream. |stream_user_data| is an opaque pointer attached to the + * stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_request( + nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr, void *stream_user_data); + +/** + * @function + * + * `nghttp3_conn_submit_push_promise` submits push promise on the + * stream identified by |stream_id|. |stream_id| must be a client + * initiated bidirectional stream. Only server can submit push + * promise. On success, a push ID is assigned to |*ppush_id|. |nva| + * of length |nvlen| specifies HTTP request header fields. In order + * to submit HTTP response, first call + * `nghttp3_conn_bind_push_stream()` and then + * `nghttp3_conn_submit_response()`. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, + int64_t *ppush_id, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_submit_info` submits HTTP non-final response header + * fields on the stream identified by |stream_id|. |nva| of length + * |nvlen| specifies HTTP response header fields. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_submit_response` submits HTTP response header fields + * and body on the stream identified by |stream_id|. |nva| of length + * |nvlen| specifies HTTP response header fields. |dr| specifies a + * response body. If there is no response body, specify NULL. If + * |dr| is NULL, it implies the end of stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen, + const nghttp3_data_reader *dr); + +/** + * @function + * + * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the + * stream identified by |stream_id|. |nva| of length |nvlen| + * specifies HTTP trailer fields. Calling this function implies the + * end of stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_bind_push_stream` binds the stream identified by + * |stream_id| to the push identified by |push_id|. |stream_id| must + * be a server initiated unidirectional stream. |push_id| must be + * obtained from `nghttp3_conn_submit_push_promise()`. To send + * response to this push, call `nghttp3_conn_submit_response()`. + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, + int64_t push_id, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_cancel_push` cancels the push identified by + * |push_id|. It is not possible to cancel the push after the + * response stream opens. In that case, send RESET_STREAM (if |conn| + * is server) or STOP_SENDING (if |conn| is client) on the underlying + * QUIC stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn, + int64_t push_id); + +/** + * @function + * + * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the + * stream identified by |stream_id|. + */ +NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, + int64_t stream_id, + void *stream_user_data); + +/** + * @function + * + * `nghttp3_conn_get_frame_payload_left` returns the number of bytes + * left to read current frame payload for a stream denoted by + * |stream_id|. If no such stream is found, it returns + * :enum:`NGHTTP3_ERR_STREAM_NOT_FOUND`. + */ +NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a + * stream denoted by |stream_id| is QPACK encoder stream of a remote + * endpoint. + */ +NGHTTP3_EXTERN int +nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| + * elements. + */ +NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); + +/** + * @function + * + * `nghttp3_vec_empty` returns nonzero if |vec| of |cnt| elements has + * 0 length data. + */ +NGHTTP3_EXTERN int nghttp3_vec_empty(const nghttp3_vec *vec, size_t cnt); + +/** + * @function + * + * `nghttp3_vec_consume` removes first |len| bytes from |*pvec| of + * |*pcnt| elements. The adjusted vector and number of elements are + * stored in |*pvec| and |*pcnt| respectively. + */ +NGHTTP3_EXTERN void nghttp3_vec_consume(nghttp3_vec **pvec, size_t *pcnt, + size_t len); + +/** + * @function + * + * `nghttp3_check_header_name` returns nonzero if HTTP header field + * name |name| of length |len| is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP/3, the upper cased + * alphabet is treated as error. + */ +NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * `nghttp3_check_header_value` returns nonzero if HTTP header field + * value |value| of length |len| is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); + +/** + * @macro + * + * The age of :type:`nghttp3_info` + */ +#define NGHTTP3_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `nghttp3_version()` returns. It holds + * information about the particular nghttp3 version. + */ +typedef struct { + /** + * Age of this struct. This instance of nghttp3 sets it to + * :macro:`NGHTTP3_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGHTTP3_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGHTTP3_VERSION` string (since age ==1) + */ + const char *version_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp3_info; + +/** + * @function + * + * Returns a pointer to a nghttp3_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP3_H */ diff --git a/deps/nghttp3/lib/includes/nghttp3/version.h b/deps/nghttp3/lib/includes/nghttp3/version.h new file mode 100644 index 00000000000000..8f3108c95fdf6b --- /dev/null +++ b/deps/nghttp3/lib/includes/nghttp3/version.h @@ -0,0 +1,46 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_VERSION_H +#define NGHTTP3_VERSION_H + +/** + * @macro + * + * Version number of the nghttp3 library release. + */ +#define NGHTTP3_VERSION "0.1.90" + +/** + * @macro + * + * Numerical representation of the version number of the nghttp3 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGHTTP3_VERSION_NUM 0x00015a + +#endif /* NGHTTP3_VERSION_H */ diff --git a/deps/nghttp3/lib/nghttp3_buf.c b/deps/nghttp3/lib/nghttp3_buf.c new file mode 100644 index 00000000000000..aae075a73cc4be --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_buf.c @@ -0,0 +1,90 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_buf.h" + +void nghttp3_buf_init(nghttp3_buf *buf) { + buf->begin = buf->end = buf->pos = buf->last = NULL; +} + +void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) { + buf->begin = buf->pos = buf->last = src; + buf->end = buf->begin + len; +} + +void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) { + nghttp3_mem_free(mem, buf->begin); +} + +size_t nghttp3_buf_left(const nghttp3_buf *buf) { + return (size_t)(buf->end - buf->last); +} + +size_t nghttp3_buf_len(const nghttp3_buf *buf) { + return (size_t)(buf->last - buf->pos); +} + +size_t nghttp3_buf_cap(const nghttp3_buf *buf) { + return (size_t)(buf->end - buf->begin); +} + +void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; } + +int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) { + uint8_t *p; + nghttp3_ssize pos_offset, last_offset; + + if ((size_t)(buf->end - buf->begin) >= size) { + return 0; + } + + pos_offset = buf->pos - buf->begin; + last_offset = buf->last - buf->begin; + + p = nghttp3_mem_realloc(mem, buf->begin, size); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + buf->begin = p; + buf->end = p + size; + buf->pos = p + pos_offset; + buf->last = p + last_offset; + + return 0; +} + +void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) { + nghttp3_buf c = *a; + + *a = *b; + *b = c; +} + +void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, + nghttp3_buf_type type) { + tbuf->buf = *buf; + tbuf->type = type; +} diff --git a/deps/nghttp3/lib/nghttp3_buf.h b/deps/nghttp3/lib/nghttp3_buf.h new file mode 100644 index 00000000000000..493d81be315ba1 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_buf.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_BUF_H +#define NGHTTP3_BUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" + +void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); + +/* + * nghttp3_buf_cap returns the capacity of the buffer. In other + * words, it returns buf->end - buf->begin. + */ +size_t nghttp3_buf_cap(const nghttp3_buf *buf); + +int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); + +/* + * nghttp3_buf_swap swaps |a| and |b|. + */ +void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b); + +typedef enum { + /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for + this buffer only and should be freed after its use. */ + NGHTTP3_BUF_TYPE_PRIVATE, + /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared + memory. */ + NGHTTP3_BUF_TYPE_SHARED, + /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a + memory which comes from outside of the library. */ + NGHTTP3_BUF_TYPE_ALIEN, +} nghttp3_buf_type; + +typedef struct { + nghttp3_buf buf; + nghttp3_buf_type type; +} nghttp3_typed_buf; + +void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, + nghttp3_buf_type type); + +void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf); + +#endif /* NGHTTP3_BUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_conn.c b/deps/nghttp3/lib/nghttp3_conn.c new file mode 100644 index 00000000000000..457cd5cb6d1a35 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conn.c @@ -0,0 +1,3232 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_conn.h" + +#include +#include +#include + +#include "nghttp3_mem.h" +#include "nghttp3_macro.h" +#include "nghttp3_err.h" +#include "nghttp3_conv.h" +#include "nghttp3_http.h" + +/* + * conn_remote_stream_uni returns nonzero if |stream_id| is remote + * unidirectional stream ID. + */ +static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) { + if (conn->server) { + return (stream_id & 0x03) == 0x02; + } + return (stream_id & 0x03) == 0x03; +} + +static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.begin_headers) { + return 0; + } + + rv = conn->callbacks.begin_headers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_headers) { + return 0; + } + + rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_begin_trailers(nghttp3_conn *conn, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.begin_trailers) { + return 0; + } + + rv = conn->callbacks.begin_trailers(conn, stream->node.nid.id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_trailers) { + return 0; + } + + rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_begin_push_promise(nghttp3_conn *conn, + nghttp3_stream *stream, + int64_t push_id) { + int rv; + + if (!conn->callbacks.begin_push_promise) { + return 0; + } + + rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_push_promise(nghttp3_conn *conn, + nghttp3_stream *stream, int64_t push_id) { + int rv; + + if (!conn->callbacks.end_push_promise) { + return 0; + } + + rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_stream) { + return 0; + } + + rv = conn->callbacks.end_stream(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.cancel_push) { + return 0; + } + + rv = conn->callbacks.cancel_push( + conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data, + stream ? stream->user_data : NULL); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_send_stop_sending(nghttp3_conn *conn, + nghttp3_stream *stream, + uint64_t app_error_code) { + int rv; + + if (!conn->callbacks.send_stop_sending) { + return 0; + } + + rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id, + app_error_code, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.push_stream) { + return 0; + } + + rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id, + conn->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_deferred_consume(nghttp3_conn *conn, + nghttp3_stream *stream, + size_t nconsumed) { + int rv; + + if (nconsumed == 0 || !conn->callbacks.deferred_consume) { + return 0; + } + + rv = conn->callbacks.deferred_consume(conn, stream->node.nid.id, nconsumed, + conn->user_data, stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int ricnt_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + nghttp3_stream *lhs = + nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe); + nghttp3_stream *rhs = + nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe); + + return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt; +} + +static int conn_new(nghttp3_conn **pconn, int server, + const nghttp3_conn_callbacks *callbacks, + const nghttp3_conn_settings *settings, + const nghttp3_mem *mem, void *user_data) { + int rv; + nghttp3_conn *conn; + nghttp3_node_id nid; + + conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); + if (conn == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init(&conn->root, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_ROOT, 0), + 0, NGHTTP3_DEFAULT_WEIGHT, NULL, mem); + + nghttp3_tnode_init(&conn->orphan_root, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_ROOT, 1), + 0, NGHTTP3_DEFAULT_WEIGHT, NULL, mem); + + rv = nghttp3_map_init(&conn->streams, mem); + if (rv != 0) { + goto streams_init_fail; + } + + rv = nghttp3_map_init(&conn->placeholders, mem); + if (rv != 0) { + goto placeholders_init_fail; + } + + rv = nghttp3_map_init(&conn->pushes, mem); + if (rv != 0) { + goto pushes_init_fail; + } + + rv = nghttp3_qpack_decoder_init(&conn->qdec, + settings->qpack_max_table_capacity, + settings->qpack_blocked_streams, mem); + if (rv != 0) { + goto qdec_init_fail; + } + + rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem); + if (rv != 0) { + goto qenc_init_fail; + } + + nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); + + rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); + if (rv != 0) { + goto remote_bidi_idtr_init_fail; + } + + rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem); + if (rv != 0) { + goto push_idtr_init_fail; + } + + conn->callbacks = *callbacks; + conn->local.settings = *settings; + nghttp3_conn_settings_default(&conn->remote.settings); + conn->mem = mem; + conn->user_data = user_data; + conn->next_seq = 0; + conn->server = server; + + *pconn = conn; + + return 0; + +push_idtr_init_fail: + nghttp3_idtr_free(&conn->remote.bidi.idtr); +remote_bidi_idtr_init_fail: + nghttp3_qpack_encoder_free(&conn->qenc); +qenc_init_fail: + nghttp3_qpack_decoder_free(&conn->qdec); +qdec_init_fail: + nghttp3_map_free(&conn->pushes); +pushes_init_fail: + nghttp3_map_free(&conn->placeholders); +placeholders_init_fail: + nghttp3_map_free(&conn->streams); +streams_init_fail: + nghttp3_mem_free(mem, conn); + + return rv; +} + +int nghttp3_conn_client_new(nghttp3_conn **pconn, + const nghttp3_conn_callbacks *callbacks, + const nghttp3_conn_settings *settings, + const nghttp3_mem *mem, void *user_data) { + int rv; + + rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data); + if (rv != 0) { + return rv; + } + + (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes; + + return 0; +} + +int nghttp3_conn_server_new(nghttp3_conn **pconn, + const nghttp3_conn_callbacks *callbacks, + const nghttp3_conn_settings *settings, + const nghttp3_mem *mem, void *user_data) { + int rv; + + rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int free_push_promise(nghttp3_map_entry *ent, void *ptr) { + nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me); + const nghttp3_mem *mem = ptr; + + nghttp3_push_promise_del(pp, mem); + + return 0; +} + +static int free_placeholder(nghttp3_map_entry *ent, void *ptr) { + nghttp3_placeholder *ph = nghttp3_struct_of(ent, nghttp3_placeholder, me); + const nghttp3_mem *mem = ptr; + + nghttp3_placeholder_del(ph, mem); + + return 0; +} + +static int free_stream(nghttp3_map_entry *ent, void *ptr) { + nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me); + + (void)ptr; + + nghttp3_stream_del(stream); + + return 0; +} + +void nghttp3_conn_del(nghttp3_conn *conn) { + if (conn == NULL) { + return; + } + + nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); + nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); + + nghttp3_gaptr_free(&conn->remote.uni.push_idtr); + + nghttp3_idtr_free(&conn->remote.bidi.idtr); + + nghttp3_pq_free(&conn->qpack_blocked_streams); + + nghttp3_qpack_encoder_free(&conn->qenc); + nghttp3_qpack_decoder_free(&conn->qdec); + + nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem); + nghttp3_map_free(&conn->pushes); + + nghttp3_map_each_free(&conn->placeholders, free_placeholder, + (void *)conn->mem); + nghttp3_map_free(&conn->placeholders); + + nghttp3_map_each_free(&conn->streams, free_stream, NULL); + nghttp3_map_free(&conn->streams); + + nghttp3_tnode_free(&conn->orphan_root); + nghttp3_tnode_free(&conn->root); + + nghttp3_mem_free(conn->mem, conn); +} + +nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_stream *stream; + size_t bidi_nproc; + int rv; + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + /* TODO Assert idtr */ + /* QUIC transport ensures that this is new stream. */ + if (conn->server) { + if (nghttp3_client_stream_bidi(stream_id)) { + rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); + assert(rv == 0); + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + } else { + /* unidirectional stream */ + if (srclen == 0 && fin) { + return 0; + } + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, stream_id, 0, + NULL); + } + if (rv != 0) { + return rv; + } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + } else if (nghttp3_stream_uni(stream_id)) { + if (srclen == 0 && fin) { + return 0; + } + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, stream_id, 0, + NULL); + if (rv != 0) { + return rv; + } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + } else { + /* client doesn't expect to receive new bidirectional stream + from server. */ + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + } else if (conn->server) { + if (nghttp3_client_stream_bidi(stream_id)) { + if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + } + } + } else if (nghttp3_stream_uni(stream_id) && + stream->type == NGHTTP3_STREAM_TYPE_PUSH) { + if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + } + } + + if (srclen == 0 && !fin) { + return 0; + } + + if (nghttp3_stream_uni(stream_id)) { + return nghttp3_conn_read_uni(conn, stream, src, srclen, fin); + } + + if (fin) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; + } + return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin); +} + +static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + int64_t stream_type; + + assert(srclen); + + nread = nghttp3_read_varint(rvint, src, srclen, fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + if (rvint->left) { + return nread; + } + + stream_type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (stream_type) { + case NGHTTP3_STREAM_TYPE_CONTROL: + if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_CONTROL; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE; + break; + case NGHTTP3_STREAM_TYPE_PUSH: + if (conn->server) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + stream->type = NGHTTP3_STREAM_TYPE_PUSH; + rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID; + break; + case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: + if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; + break; + case NGHTTP3_STREAM_TYPE_QPACK_DECODER: + if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; + break; + default: + stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN; + break; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED; + + return nread; +} + +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_ssize nread = 0; + nghttp3_ssize nconsumed = 0; + size_t push_nproc; + int rv; + + assert(srclen || fin); + + if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { + if (srclen == 0 && fin) { + /* Ignore stream if it is closed before reading stream header. + If it is closed while reading it, return error, making it + consistent in our code base. */ + if (stream->rstate.rvint.left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + rv = conn_delete_stream(conn, stream); + assert(0 == rv); + + return 0; + } + nread = conn_read_type(conn, stream, src, srclen, fin); + if (nread < 0) { + return (int)nread; + } + if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { + assert((size_t)nread == srclen); + return (nghttp3_ssize)srclen; + } + + src += nread; + srclen -= (size_t)nread; + + if (srclen == 0) { + return nread; + } + } + + switch (stream->type) { + case NGHTTP3_STREAM_TYPE_CONTROL: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_PUSH: + if (fin) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; + } + nconsumed = + nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin); + break; + case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_QPACK_DECODER: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_UNKNOWN: + nconsumed = (nghttp3_ssize)srclen; + + rv = conn_call_send_stop_sending(conn, stream, + NGHTTP3_H3_STREAM_CREATION_ERROR); + if (rv != 0) { + return rv; + } + break; + default: + /* unreachable */ + assert(0); + } + + if (nconsumed < 0) { + return nconsumed; + } + + return nread + nconsumed; +} + +static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) { + return (int64_t)len >= rstate->left; +} + +nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, + nghttp3_stream *stream, + const uint8_t *src, size_t srclen) { + const uint8_t *p = src, *end = src + srclen; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + + assert(srclen); + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + break; + } + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) { + if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) { + return NGHTTP3_ERR_H3_MISSING_SETTINGS; + } + conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED; + } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_CANCEL_PUSH: + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH; + break; + case NGHTTP3_FRAME_SETTINGS: + /* SETTINGS frame might be empty. */ + if (rstate->left == 0) { + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; + break; + case NGHTTP3_FRAME_GOAWAY: + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY; + break; + case NGHTTP3_FRAME_MAX_PUSH_ID: + if (!conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID; + break; + case NGHTTP3_FRAME_DATA: + case NGHTTP3_FRAME_HEADERS: + case NGHTTP3_FRAME_PUSH_PROMISE: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.cancel_push.push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (conn->server) { + rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push); + } else { + rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push); + } + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS: + for (; p != end;) { + if (rstate->left == 0) { + nghttp3_stream_read_state_reset(rstate); + break; + } + /* Read Identifier */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID; + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + /* Read Value */ + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + len -= (size_t)nread; + if (len == 0) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + break; + } + + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = + nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); + if (rv != 0) { + return rv; + } + } + break; + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); + if (rv != 0) { + return rv; + } + + if (rstate->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; + break; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_GOAWAY: + /* TODO Not implemented yet */ + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: + /* server side only */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + conn->local.uni.max_pushes = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + return (nghttp3_ssize)nconsumed; + } + + nghttp3_stream_read_state_reset(rstate); + break; + default: + /* unreachable */ + assert(0); + } + } + + return (nghttp3_ssize)nconsumed; +} + +nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + int64_t push_id; + + if (stream->pp && + (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { + *pnproc = srclen; + return (nghttp3_ssize)srclen; + } + + if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED | + NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) { + *pnproc = 0; + + if (srclen == 0) { + return 0; + } + + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + return 0; + } + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id); + if (rv != 0) { + if (rv == NGHTTP3_ERR_IGNORE_STREAM) { + rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST; + break; + } + return (nghttp3_ssize)rv; + } + + rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE; + + if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) { + if (p != end) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (end == p) { + goto almost_done; + } + + /* Fall through */ + case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); + if (rv != 0) { + return rv; + } + /* DATA frame might be empty. */ + if (rstate->left == 0) { + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA; + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); + if (rv != 0) { + return rv; + } + if (rstate->left == 0) { + rv = nghttp3_stream_empty_headers_allowed(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_begin_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_begin_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS; + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + case NGHTTP3_FRAME_CANCEL_PUSH: + case NGHTTP3_FRAME_SETTINGS: + case NGHTTP3_FRAME_GOAWAY: + case NGHTTP3_FRAME_MAX_PUSH_ID: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_PUSH_STREAM_STATE_DATA: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + rv = nghttp3_conn_on_data(conn, stream, p, len); + if (rv != 0) { + return rv; + } + + p += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_HEADERS: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = nghttp3_http_on_response_headers(&stream->rx.http); + if (rv != 0) { + return rv; + } + + rv = conn_call_end_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_end_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: + nconsumed += (size_t)(end - p); + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + } + +almost_done: + if (fin) { + switch (rstate->state) { + case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: + if (rvint->left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_MSG_END); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: + break; + default: + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + } + + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; +} + +static void conn_delete_push_promise(nghttp3_conn *conn, + nghttp3_push_promise *pp) { + int rv; + + rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id); + assert(0 == rv); + + if (!conn->server && + !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) { + ++conn->remote.uni.unsent_max_pushes; + } + + nghttp3_push_promise_del(pp, conn->mem); +} + +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (nghttp3_stream_bidi_or_push(stream)) { + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + } + + rv = conn_call_deferred_consume(conn, stream, + nghttp3_stream_get_buffered_datalen(stream)); + if (rv != 0) { + return rv; + } + + if (conn->callbacks.stream_close) { + rv = conn->callbacks.stream_close(conn, stream->node.nid.id, + stream->error_code, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id); + + assert(0 == rv); + + if (stream->pp) { + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + + conn_delete_push_promise(conn, stream->pp); + } + + nghttp3_stream_del(stream); + + return 0; +} + +static int conn_process_blocked_stream_data(nghttp3_conn *conn, + nghttp3_stream *stream) { + nghttp3_buf *buf; + size_t nproc; + nghttp3_ssize nconsumed; + int rv; + size_t len; + + for (;;) { + len = nghttp3_ringbuf_len(&stream->inq); + if (len == 0) { + break; + } + buf = nghttp3_ringbuf_get(&stream->inq, 0); + if (nghttp3_stream_uni(stream->node.nid.id)) { + nconsumed = nghttp3_conn_read_push( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); + } else { + nconsumed = nghttp3_conn_read_bidi( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); + } + if (nconsumed < 0) { + return (int)nconsumed; + } + + buf->pos += nproc; + + rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed); + if (rv != 0) { + return 0; + } + + if (nghttp3_buf_len(buf) == 0) { + nghttp3_buf_free(buf, stream->mem); + nghttp3_ringbuf_pop_front(&stream->inq); + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + break; + } + } + + if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) && + (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) { + assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); + + rv = conn_delete_stream(conn, stream); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen) { + nghttp3_ssize nconsumed = + nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen); + nghttp3_stream *stream; + int rv; + + if (nconsumed < 0) { + return nconsumed; + } + + for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) { + stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams), + nghttp3_stream, qpack_blocked_pe); + if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) > + nghttp3_qpack_decoder_get_icnt(&conn->qdec)) { + break; + } + + nghttp3_conn_qpack_blocked_streams_pop(conn); + stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; + + rv = conn_process_blocked_stream_data(conn, stream); + if (rv != 0) { + return rv; + } + } + + return nconsumed; +} + +nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen) { + return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); +} + +nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + nghttp3_push_promise *pp; + nghttp3_push_promise fake_pp = { + {0}, {{0}, {0}, NULL, NULL, NULL, 0, {0}, 0, 0, 0, 0, 0}, {0}, NULL, -1, + 0}; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + *pnproc = 0; + + if (srclen == 0) { + return 0; + } + + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + return 0; + } + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); + if (rv != 0) { + return rv; + } + /* DATA frame might be empty. */ + if (rstate->left == 0) { + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_REQ_STREAM_STATE_DATA; + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); + if (rv != 0) { + return rv; + } + if (rstate->left == 0) { + rv = nghttp3_stream_empty_headers_allowed(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_begin_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_begin_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + if (conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + /* No stream->rx.hstate change with PUSH_PROMISE */ + + rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID; + break; + case NGHTTP3_FRAME_CANCEL_PUSH: + case NGHTTP3_FRAME_SETTINGS: + case NGHTTP3_FRAME_GOAWAY: + case NGHTTP3_FRAME_MAX_PUSH_ID: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_REQ_STREAM_STATE_DATA: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + rv = nghttp3_conn_on_data(conn, stream, p, len); + if (rv != 0) { + return rv; + } + p += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), + (int64_t)len == rstate->left); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.push_promise.push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + rv = nghttp3_conn_on_push_promise_push_id( + conn, rstate->fr.push_promise.push_id, stream); + if (rv != 0) { + if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) { + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE; + if (p == end) { + goto almost_done; + } + break; + } + + return rv; + } + + rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE; + + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE: + pp = + nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); + + assert(pp); + + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, pp, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_http_on_request_headers(&pp->http); + if (rv != 0) { + return rv; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; + + rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id); + if (rv != 0) { + return rv; + } + + if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) { + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) { + rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + } + + conn_delete_push_promise(conn, pp); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + if (pp->stream) { + ++conn->remote.uni.unsent_max_pushes; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { + assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); + + rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + + pp->stream->flags &= + (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + rv = conn_process_blocked_stream_data(conn, pp->stream); + if (rv != 0) { + return rv; + } + } + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_HEADERS: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + rv = nghttp3_http_on_request_headers(&stream->rx.http); + break; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = nghttp3_http_on_response_headers(&stream->rx.http); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = 0; + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_end_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_end_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + nghttp3_stream_read_state_reset(rstate); + break; + } + } + +almost_done: + if (fin) { + switch (rstate->state) { + case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: + if (rvint->left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_MSG_END); + if (rv != 0) { + return rv; + } + rv = conn_call_end_stream(conn, stream); + if (rv != 0) { + return rv; + } + break; + default: + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + } + + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; +} + +int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *data, size_t datalen) { + int rv; + + rv = nghttp3_http_on_data_chunk(stream, datalen); + if (rv != 0) { + return rv; + } + + if (!conn->callbacks.recv_data) { + return 0; + } + + rv = conn->callbacks.recv_data(conn, stream->node.nid.id, data, datalen, + conn->user_data, stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; + nghttp3_push_promise *pp; + + if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp) { + if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) || + (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) { + return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; + } + if (pp->stream) { + assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); + /* Push unidirectional stream has already been received and + blocked */ + } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) { + /* We will call begin_push_promise callback even if push is + cancelled */ + } else { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + assert(pp->stream_id == -1); + + pp->stream_id = stream->node.nid.id; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) && + pp->node.parent == &conn->orphan_root) { + nghttp3_tnode_remove(&pp->node); + nghttp3_tnode_insert(&pp->node, &stream->node); + } + } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) { + return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; + } else { + rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1); + if (rv != 0) { + return rv; + } + + rv = nghttp3_conn_create_push_promise( + conn, &pp, push_id, NGHTTP3_DEFAULT_WEIGHT, &stream->node); + if (rv != 0) { + return rv; + } + } + + rv = conn_call_begin_push_promise(conn, stream, push_id); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr) { + nghttp3_push_promise *pp; + nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; + int rv; + + if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, fr->push_id); + if (pp == NULL) { + if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) { + /* push is already cancelled or server is misbehaving */ + return 0; + } + + /* We have not received PUSH_PROMISE yet */ + rv = nghttp3_gaptr_push(push_idtr, (uint64_t)fr->push_id, 1); + if (rv != 0) { + return rv; + } + + rv = nghttp3_conn_create_push_promise( + conn, &pp, fr->push_id, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); + if (rv != 0) { + return rv; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; + + /* cancel_push callback will be called after PUSH_PROMISE frame is + completely received. */ + + return 0; + } + + if (pp->stream) { + return 0; + } + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { + rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + + return 0; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; + + return 0; +} + +int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr) { + nghttp3_push_promise *pp; + nghttp3_stream *stream; + int rv; + + if (conn->local.uni.next_push_id <= fr->push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, fr->push_id); + if (pp == NULL) { + return 0; + } + + stream = pp->stream; + + rv = conn_call_cancel_push(conn, fr->push_id, stream); + if (rv != 0) { + return rv; + } + + if (stream) { + rv = nghttp3_conn_close_stream(conn, stream->node.nid.id, + NGHTTP3_H3_REQUEST_CANCELLED); + if (rv != 0) { + assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv); + return rv; + } + return 0; + } + + rv = nghttp3_tnode_squash(&pp->node); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + + return 0; +} + +int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, + int64_t push_id) { + nghttp3_push_promise *pp; + int rv; + + if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id, + 1)) { + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp) { + if (pp->stream) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + pp->stream = stream; + stream->pp = pp; + + assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)); + assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL)); + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { + ++conn->remote.uni.unsent_max_pushes; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; + + return conn_call_push_stream(conn, push_id, stream); + } + + stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + return 0; + } + + /* Push ID has been received, but pp is gone. This means that + push is cancelled or server is misbehaving. We have no + information to distinguish the two, so just cancel QPACK stream + just in case, and ask application to send STOP_SENDING and + ignore all frames in this stream. */ + rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); + if (rv != 0) { + return rv; + } + rv = + conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED); + if (rv != 0) { + return rv; + } + return NGHTTP3_ERR_IGNORE_STREAM; + } + + if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + rv = nghttp3_gaptr_push(&conn->remote.uni.push_idtr, (uint64_t)push_id, 1); + if (rv != 0) { + return rv; + } + + /* Don't know the associated stream of PUSH_PROMISE. It doesn't + matter because client sends nothing to this stream. */ + rv = nghttp3_conn_create_push_promise( + conn, &pp, push_id, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); + if (rv != 0) { + return rv; + } + + pp->stream = stream; + stream->pp = pp; + stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + return 0; +} + +static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_ssize nread; + int rv; + nghttp3_qpack_decoder *qdec = &conn->qdec; + nghttp3_qpack_nv nv; + uint8_t flags; + nghttp3_buf buf; + nghttp3_recv_header recv_header = NULL; + nghttp3_http_state *http; + int request = 0; + int trailers = 0; + int ignore_pp = 0; + + assert(srclen); + + if (pp) { + request = 1; + ignore_pp = pp->stream_id != stream->node.nid.id; + if (ignore_pp) { + http = NULL; + } else { + http = &pp->http; + } + } else { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + recv_header = conn->callbacks.recv_header; + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + trailers = 1; + recv_header = conn->callbacks.recv_trailer; + break; + default: + /* Unreachable */ + assert(0); + } + http = &stream->rx.http; + } + + nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen); + buf.last = buf.end; + + for (;;) { + nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv, + &flags, buf.pos, + nghttp3_buf_len(&buf), fin); + + if (nread < 0) { + return (int)nread; + } + + buf.pos += nread; + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) { + if (conn->local.settings.qpack_blocked_streams <= + nghttp3_pq_size(&conn->qpack_blocked_streams)) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; + rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream); + if (rv != 0) { + return rv; + } + break; + } + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) { + nghttp3_qpack_stream_context_reset(&stream->qpack_sctx); + break; + } + + if (nread == 0) { + break; + } + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { + if (ignore_pp) { + nghttp3_rcbuf_decref(nv.name); + nghttp3_rcbuf_decref(nv.value); + + continue; + } + + assert(http); + + rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request, + trailers); + switch (rv) { + case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: + break; + case NGHTTP3_ERR_REMOVE_HTTP_HEADER: + rv = 0; + break; + case 0: + if (pp) { + if (conn->callbacks.recv_push_promise) { + rv = conn->callbacks.recv_push_promise( + conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name, + nv.value, nv.flags, conn->user_data, stream->user_data); + } + break; + } + if (recv_header) { + rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name, + nv.value, nv.flags, conn->user_data, + stream->user_data); + } + break; + default: + /* Unreachable */ + assert(0); + } + + nghttp3_rcbuf_decref(nv.name); + nghttp3_rcbuf_decref(nv.value); + + if (rv != 0) { + return rv; + } + } + } + + return buf.pos - src; +} + +nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *src, size_t srclen, + int fin) { + if (srclen == 0 && !fin) { + return 0; + } + + return conn_decode_headers(conn, stream, pp, src, srclen, fin); +} + +int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, + const nghttp3_frame_settings *fr) { + const nghttp3_settings_entry *ent = &fr->iv[0]; + nghttp3_conn_settings *dest = &conn->remote.settings; + int rv; + size_t max_table_capacity = SIZE_MAX; + size_t max_blocked_streams = SIZE_MAX; + + /* TODO Check for duplicates */ + switch (ent->id) { + case NGHTTP3_SETTINGS_ID_MAX_HEADER_LIST_SIZE: + dest->max_header_list_size = ent->value; + break; + case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: + dest->qpack_max_table_capacity = (size_t)ent->value; + max_table_capacity = + nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity); + rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc, + max_table_capacity); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc, + max_table_capacity); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: + dest->qpack_blocked_streams = (size_t)ent->value; + max_blocked_streams = + nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams); + rv = + nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams); + if (rv != 0) { + return rv; + } + break; + default: + /* Ignore unknown settings ID */ + break; + } + + return 0; +} + +static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, + size_t datalen, void *user_data) { + nghttp3_conn *conn = stream->conn; + int rv; + + if (!conn->callbacks.acked_stream_data) { + return 0; + } + + rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen, + conn->user_data, user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, + int64_t stream_id) { + return nghttp3_conn_create_stream_dependency( + conn, pstream, stream_id, NGHTTP3_DEFAULT_WEIGHT, &conn->orphan_root); +} + +int nghttp3_conn_create_stream_dependency(nghttp3_conn *conn, + nghttp3_stream **pstream, + int64_t stream_id, uint32_t weight, + nghttp3_tnode *parent) { + nghttp3_stream *stream; + int rv; + nghttp3_stream_callbacks callbacks = { + conn_stream_acked_data, + }; + + rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, weight, parent, + &callbacks, conn->mem); + if (rv != 0) { + return rv; + } + + stream->conn = conn; + + rv = nghttp3_map_insert(&conn->streams, &stream->me); + if (rv != 0) { + nghttp3_stream_del(stream); + return rv; + } + + ++conn->next_seq; + *pstream = stream; + + return 0; +} + +int nghttp3_conn_create_placeholder(nghttp3_conn *conn, + nghttp3_placeholder **pph, int64_t ph_id, + uint32_t weight, nghttp3_tnode *parent) { + nghttp3_placeholder *ph; + int rv; + + rv = nghttp3_placeholder_new(&ph, ph_id, conn->next_seq, weight, parent, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_insert(&conn->placeholders, &ph->me); + if (rv != 0) { + nghttp3_placeholder_del(ph, conn->mem); + return rv; + } + + ++conn->next_seq; + *pph = ph; + + return 0; +} + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, uint32_t weight, + nghttp3_tnode *parent) { + nghttp3_push_promise *pp; + int rv; + + rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, weight, parent, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_insert(&conn->pushes, &pp->me); + if (rv != 0) { + nghttp3_push_promise_del(pp, conn->mem); + return rv; + } + + ++conn->next_seq; + *ppp = pp; + + return 0; +} + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->streams, (key_type)stream_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_stream, me); +} + +nghttp3_placeholder *nghttp3_conn_find_placeholder(nghttp3_conn *conn, + int64_t ph_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->placeholders, (key_type)ph_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_placeholder, me); +} + +nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, + int64_t push_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->pushes, (key_type)push_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_push_promise, me); +} + +int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream; + nghttp3_frame_entry frent; + int rv; + + assert(!conn->server || nghttp3_server_stream_uni(stream_id)); + assert(conn->server || nghttp3_client_stream_uni(stream_id)); + + if (conn->tx.ctrl) { + return NGHTTP3_ERR_INVALID_STATE; + } + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, stream_id, 0, NULL); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_CONTROL; + + conn->tx.ctrl = stream; + + rv = nghttp3_stream_write_stream_type(stream); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; + frent.aux.settings.local_settings = &conn->local.settings; + + return nghttp3_stream_frq_add(stream, &frent); +} + +int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, + int64_t qdec_stream_id) { + nghttp3_stream *stream; + int rv; + + assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id)); + assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id)); + assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id)); + assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id)); + + if (conn->tx.qenc || conn->tx.qdec) { + return NGHTTP3_ERR_INVALID_STATE; + } + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, qenc_stream_id, 0, + NULL); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; + + conn->tx.qenc = stream; + + rv = nghttp3_stream_write_stream_type(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, qdec_stream_id, 0, + NULL); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; + + conn->tx.qdec = stream; + + return nghttp3_stream_write_stream_type(stream); +} + +static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id, + int *pfin, nghttp3_vec *vec, + size_t veccnt, nghttp3_stream *stream) { + int rv; + nghttp3_ssize n; + + assert(veccnt > 0); + + /* If stream is blocked by read callback, don't attempt to fill + more. */ + if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) { + rv = nghttp3_stream_fill_outq(stream); + if (rv != 0) { + return rv; + } + } + + if (!nghttp3_stream_uni(stream->node.nid.id) && conn->tx.qenc && + !nghttp3_stream_is_blocked(conn->tx.qenc)) { + n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt); + if (n < 0) { + return n; + } + if (n) { + *pstream_id = conn->tx.qenc->node.nid.id; + return n; + } + } + + n = nghttp3_stream_writev(stream, pfin, vec, veccnt); + if (n < 0) { + return n; + } + /* We might just want to write stream fin without sending any stream + data. */ + if (n == 0 && *pfin == 0) { + return 0; + } + + *pstream_id = stream->node.nid.id; + + return n; +} + +nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, + int64_t *pstream_id, int *pfin, + nghttp3_vec *vec, size_t veccnt) { + nghttp3_ssize ncnt; + nghttp3_stream *stream; + int rv; + + *pstream_id = -1; + *pfin = 0; + + if (veccnt == 0) { + return 0; + } + + if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) { + if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) && + conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) { + rv = nghttp3_conn_submit_max_push_id(conn); + if (rv != 0) { + return rv; + } + } + + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl); + if (ncnt) { + return ncnt; + } + } + + if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) { + rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec); + if (rv != 0) { + return rv; + } + + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec); + if (ncnt) { + return ncnt; + } + } + + if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) { + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc); + if (ncnt) { + return ncnt; + } + } + + stream = nghttp3_conn_get_next_tx_stream(conn); + if (stream == NULL) { + return 0; + } + + ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream); + if (ncnt < 0) { + return ncnt; + } + + if (nghttp3_stream_bidi_or_push(stream) && + !nghttp3_stream_require_schedule(stream)) { + nghttp3_stream_unschedule(stream); + } + + return ncnt; +} + +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { + nghttp3_tnode *node = nghttp3_tnode_get_next(&conn->root); + + if (node == NULL) { + node = nghttp3_tnode_get_next(&conn->orphan_root); + if (node == NULL) { + return NULL; + } + } + + if (node->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { + return nghttp3_struct_of(node, nghttp3_push_promise, node)->stream; + } + + return nghttp3_struct_of(node, nghttp3_stream, node); +} + +int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, + size_t n) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + int rv; + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + rv = nghttp3_stream_add_outq_offset(stream, n); + if (rv != 0) { + return rv; + } + + stream->unscheduled_nwrite += n; + if (nghttp3_stream_bidi_or_push(stream)) { + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_schedule(stream); + } + nghttp3_stream_unschedule(stream); + } + + return 0; +} + +int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, + size_t n) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return nghttp3_stream_add_ack_offset(stream, n); +} + +static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr) { + int rv; + nghttp3_nv *nnva; + nghttp3_frame_entry frent; + + rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_HEADERS; + frent.fr.headers.nva = nnva; + frent.fr.headers.nvlen = nvlen; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + nghttp3_nva_del(nnva, conn->mem); + return rv; + } + + if (dr) { + frent.fr.hd.type = NGHTTP3_FRAME_DATA; + frent.aux.data.dr = *dr; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + } + + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_schedule(stream); + } + + return 0; +} + +int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr, + void *stream_user_data) { + nghttp3_stream *stream; + int rv; + + assert(!conn->server); + assert(conn->tx.qenc); + + assert(nghttp3_client_stream_bidi(stream_id)); + + /* TODO Should we check that stream_id is client stream_id? */ + /* TODO Check GOAWAY last stream ID */ + if (nghttp3_stream_uni(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream != NULL) { + return NGHTTP3_ERR_STREAM_IN_USE; + } + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + stream->user_data = stream_user_data; + + nghttp3_http_record_request_method(stream, nva, nvlen); + + if (dr == NULL) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, dr); +} + +int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send info (non-final response) + now. */ + assert(conn->server); + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); +} + +int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send response now. */ + assert(conn->server); + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (dr == NULL) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, dr); +} + +int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send trailer now. */ + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) { + return NGHTTP3_ERR_INVALID_STATE; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + + return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); +} + +int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id, + int64_t stream_id, const nghttp3_nv *nva, + size_t nvlen) { + nghttp3_stream *stream; + int rv; + nghttp3_nv *nnva; + nghttp3_frame_entry frent; + int64_t push_id; + nghttp3_push_promise *pp; + + assert(conn->server); + assert(conn->tx.qenc); + assert(nghttp3_client_stream_bidi(stream_id)); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) { + return NGHTTP3_ERR_PUSH_ID_BLOCKED; + } + + push_id = conn->local.uni.next_push_id; + + rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, + NGHTTP3_DEFAULT_WEIGHT, &stream->node); + if (rv != 0) { + return rv; + } + + ++conn->local.uni.next_push_id; + + rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE; + frent.fr.push_promise.push_id = push_id; + frent.fr.push_promise.nva = nnva; + frent.fr.push_promise.nvlen = nvlen; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + nghttp3_nva_del(nnva, conn->mem); + return rv; + } + + *ppush_id = push_id; + + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_schedule(stream); + } + + return 0; +} + +int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id) { + nghttp3_push_promise *pp; + nghttp3_stream *stream; + int rv; + + assert(conn->server); + assert(nghttp3_server_stream_uni(stream_id)); + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + assert(NULL == nghttp3_conn_find_stream(conn, stream_id)); + + rv = nghttp3_conn_create_stream_dependency(conn, &stream, stream_id, 0, NULL); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_PUSH; + stream->pp = pp; + + pp->stream = stream; + + rv = nghttp3_stream_write_stream_type_push_id(stream); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) { + if (conn->server) { + return nghttp3_conn_server_cancel_push(conn, push_id); + } + return nghttp3_conn_client_cancel_push(conn, push_id); +} + +int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) { + nghttp3_push_promise *pp; + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (pp->stream) { + return NGHTTP3_ERR_TOO_LATE; + } + + frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; + frent.fr.cancel_push.push_id = push_id; + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + rv = nghttp3_tnode_squash(&pp->node); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + + return 0; +} + +int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) { + nghttp3_push_promise *pp; + nghttp3_frame_entry frent; + int rv; + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (pp->stream) { + return NGHTTP3_ERR_TOO_LATE; + } + + frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; + frent.fr.cancel_push.push_id = push_id; + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { + conn_delete_push_promise(conn, pp); + return 0; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; + + return 0; +} + +int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; + + if (nghttp3_stream_bidi_or_push(stream)) { + nghttp3_stream_unschedule(stream); + } + + return 0; +} + +int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; + + if (nghttp3_stream_bidi_or_push(stream) && + nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_ensure_scheduled(stream); + } + + return 0; +} + +int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; + + if (nghttp3_stream_bidi_or_push(stream) && + nghttp3_stream_require_schedule(stream)) { + return nghttp3_stream_ensure_scheduled(stream); + } + + return 0; +} + +int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + int rv; + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (nghttp3_stream_uni(stream_id) && + stream->type != NGHTTP3_STREAM_TYPE_PUSH && + stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + + stream->error_code = app_error_code; + + rv = nghttp3_stream_squash(stream); + if (rv != 0) { + return rv; + } + + if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX && + (conn->server || !stream->pp || + (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) { + return conn_delete_stream(conn, stream); + } + + stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED; + return 0; +} + +int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream; + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream) { + stream->flags |= NGHTTP3_STREAM_FLAG_RESET; + } + return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id); +} + +int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, + nghttp3_stream *stream) { + assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); + + return nghttp3_pq_push(&conn->qpack_blocked_streams, + &stream->qpack_blocked_pe); +} + +void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) { + assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams)); + nghttp3_pq_pop(&conn->qpack_blocked_streams); +} + +void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, + uint64_t max_streams) { + assert(conn->server); + assert(conn->remote.bidi.max_client_streams <= max_streams); + + conn->remote.bidi.max_client_streams = max_streams; +} + +void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, + size_t max_concurrent_streams) { + nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec, + max_concurrent_streams); +} + +int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) { + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED)); + + frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID; + /* The acutal push_id is set when frame is serialized */ + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; + + return 0; +} + +int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, + void *stream_user_data) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->user_data = stream_user_data; + + return 0; +} + +int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return stream->rstate.left; +} + +int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_stream *stream; + + if (!conn_remote_stream_uni(conn, stream_id)) { + return 0; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER; +} + +void nghttp3_conn_settings_default(nghttp3_conn_settings *settings) { + memset(settings, 0, sizeof(nghttp3_conn_settings)); + settings->max_header_list_size = NGHTTP3_VARINT_MAX; +} + +int nghttp3_placeholder_new(nghttp3_placeholder **pph, int64_t ph_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem) { + nghttp3_placeholder *ph; + nghttp3_node_id nid; + + ph = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_placeholder)); + if (ph == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &ph->node, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PLACEHOLDER, ph_id), seq, + weight, parent, mem); + + ph->me.key = (key_type)ph_id; + + *pph = ph; + + return 0; +} + +void nghttp3_placeholder_del(nghttp3_placeholder *ph, const nghttp3_mem *mem) { + if (ph == NULL) { + return; + } + + nghttp3_tnode_free(&ph->node); + + nghttp3_mem_free(mem, ph); +} + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem) { + nghttp3_push_promise *pp; + nghttp3_node_id nid; + + pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise)); + if (pp == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id), + seq, weight, parent, mem); + + pp->me.key = (key_type)push_id; + pp->node.nid.id = push_id; + pp->http.status_code = -1; + pp->http.content_length = -1; + + if (parent->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM) { + pp->stream_id = parent->nid.id; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; + } else { + pp->stream_id = -1; + } + + *ppp = pp; + + return 0; +} + +void nghttp3_push_promise_del(nghttp3_push_promise *pp, + const nghttp3_mem *mem) { + if (pp == NULL) { + return; + } + + nghttp3_tnode_free(&pp->node); + + nghttp3_mem_free(mem, pp); +} diff --git a/deps/nghttp3/lib/nghttp3_conn.h b/deps/nghttp3/lib/nghttp3_conn.h new file mode 100644 index 00000000000000..a29aeb89f994c5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conn.h @@ -0,0 +1,279 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_CONN_H +#define NGHTTP3_CONN_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_stream.h" +#include "nghttp3_map.h" +#include "nghttp3_qpack.h" +#include "nghttp3_tnode.h" +#include "nghttp3_idtr.h" +#include "nghttp3_gaptr.h" + +#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic + table size for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384 + +/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of + blocked streams for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 + +typedef struct { + nghttp3_map_entry me; + nghttp3_tnode node; +} nghttp3_placeholder; + +typedef enum { + NGHTTP3_PUSH_PROMISE_FLAG_NONE = 0x00, + /* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is + completely received. */ + NGHTTP3_PUSH_PROMISE_FLAG_RECVED = 0x01, + /* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is + cancelled by server before receiving PUSH_PROMISE completely. + This flag should have no effect if push stream has already + opened. */ + NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL = 0x02, + /* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is + canceled by client. */ + NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL = 0x04, + NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED = NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | + NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL, + /* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that + unsent_max_pushes has been updated for this push ID. */ + NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED = 0x08, + /* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise + object is bound to a stream where PUSH_PROMISE frame is received + first. nghttp3_push_promise object might be created before + receiving any PUSH_PROMISE when pushed stream is received before + it.*/ + NGHTTP3_PUSH_PROMISE_FLAG_BOUND = 0x10, +} nghttp3_push_promise_flag; + +struct nghttp3_push_promise; +typedef struct nghttp3_push_promise nghttp3_push_promise; + +struct nghttp3_push_promise { + nghttp3_map_entry me; + nghttp3_tnode node; + nghttp3_http_state http; + /* stream is server initiated unidirectional stream which fulfils + the push promise. */ + nghttp3_stream *stream; + /* stream_id is the stream ID where this PUSH_PROMISE is first + received. PUSH_PROMISE with same push ID is allowed to be sent + in the multiple streams. We ignore those duplicated PUSH_PROMISE + entirely because we don't see any value to add extra cost of + processing for it. Even ignoring those frame is not yet easy + because we have to decode the header blocks. Server push is + overly complex and there is no good use case after all. */ + int64_t stream_id; + /* flags is bitwise OR of zero or more of + nghttp3_push_promise_flag. */ + uint16_t flags; +}; + +typedef enum { + NGHTTP3_CONN_FLAG_NONE = 0x0000, + NGHTTP3_CONN_FLAG_SETTINGS_RECVED = 0x0001, + NGHTTP3_CONN_FLAG_CONTROL_OPENED = 0x0002, + NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED = 0x0004, + NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED = 0x0008, + /* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID + has been queued to control stream. */ + NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED = 0x0010, +} nghttp3_conn_flag; + +struct nghttp3_conn { + nghttp3_tnode root; + nghttp3_tnode orphan_root; + nghttp3_conn_callbacks callbacks; + nghttp3_map streams; + nghttp3_map placeholders; + nghttp3_map pushes; + nghttp3_qpack_decoder qdec; + nghttp3_qpack_encoder qenc; + nghttp3_pq qpack_blocked_streams; + const nghttp3_mem *mem; + void *user_data; + int server; + uint16_t flags; + uint64_t next_seq; + + struct { + nghttp3_conn_settings settings; + struct { + /* max_pushes is the number of push IDs that local endpoint can + issue. This field is used by server only. */ + uint64_t max_pushes; + /* next_push_id is the next push ID server uses. This field is + used by server only. */ + int64_t next_push_id; + } uni; + } local; + + struct { + struct { + nghttp3_idtr idtr; + /* max_client_streams is the cumulative number of client + initiated bidirectional stream ID the remote endpoint can + issue. This field is used on server side only. */ + uint64_t max_client_streams; + } bidi; + struct { + /* push_idtr tracks which push ID has been used by remote + server. This field is used by client only. */ + nghttp3_gaptr push_idtr; + /* unsent_max_pushes is the maximum number of push which the local + endpoint can accept. This limit is not yet notified to the + remote endpoint. This field is used by client only. */ + uint64_t unsent_max_pushes; + /* max_push is the maximum number of push which the local + endpoint can accept. This field is used by client only. */ + uint64_t max_pushes; + } uni; + nghttp3_conn_settings settings; + } remote; + + struct { + struct { + nghttp3_buf rbuf; + nghttp3_buf ebuf; + } qpack; + nghttp3_stream *ctrl; + nghttp3_stream *qenc; + nghttp3_stream *qdec; + } tx; +}; + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); + +nghttp3_placeholder *nghttp3_conn_find_placeholder(nghttp3_conn *conn, + int64_t ph_id); + +nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, + int64_t push_id); + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, + int64_t stream_id); + +int nghttp3_conn_create_stream_dependency(nghttp3_conn *conn, + nghttp3_stream **pstream, + int64_t stream_id, uint32_t weight, + nghttp3_tnode *parent); + +int nghttp3_conn_create_placeholder(nghttp3_conn *conn, + nghttp3_placeholder **pph, int64_t ph_id, + uint32_t weight, nghttp3_tnode *parent); + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, uint32_t weight, + nghttp3_tnode *parent); + +nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, + nghttp3_stream *stream, + const uint8_t *src, size_t srclen); + +nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen); + +nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen); + +int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream); + +int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr); + +int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr); + +int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, + int64_t push_id); + +int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *data, size_t datalen); + +nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *data, size_t datalen, + int fin); + +int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, + const nghttp3_frame_settings *fr); + +int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, + nghttp3_stream *stream); + +void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn); + +int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id); + +int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id); + +int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn); + +/* + * nghttp3_conn_get_next_tx_stream returns next stream to send. It + * returns NULL if there is no such stream. + */ +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); + +int nghttp3_placeholder_new(nghttp3_placeholder **pph, int64_t ph_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem); + +void nghttp3_placeholder_del(nghttp3_placeholder *ph, const nghttp3_mem *mem); + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, uint32_t weight, + nghttp3_tnode *parent, const nghttp3_mem *mem); + +void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem); + +#endif /* NGHTTP3_CONN_H */ diff --git a/deps/nghttp3/lib/nghttp3_conv.c b/deps/nghttp3/lib/nghttp3_conv.c new file mode 100644 index 00000000000000..b8a7f8a4d0fa70 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conv.c @@ -0,0 +1,130 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_conv.h" + +#include +#include + +#include "nghttp3_str.h" + +int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) { + union { + char b[8]; + uint16_t n16; + uint32_t n32; + uint64_t n64; + } n; + + *plen = 1u << (*p >> 6); + + switch (*plen) { + case 1: + return (int64_t)*p; + case 2: + memcpy(&n, p, 2); + n.b[0] &= 0x3f; + return (int64_t)ntohs(n.n16); + case 4: + memcpy(&n, p, 4); + n.b[0] &= 0x3f; + return (int64_t)ntohl(n.n32); + case 8: + memcpy(&n, p, 8); + n.b[0] &= 0x3f; + return (int64_t)nghttp3_ntohl64(n.n64); + } + + assert(0); +} + +int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } + +size_t nghttp3_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); } + +uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) { + n = nghttp3_htonl64(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n) { + n = nghttp3_htonl64(n); + return nghttp3_cpymem(p, ((const uint8_t *)&n) + 2, 6); +} + +uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) { + n = htonl(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n) { + n = htonl(n); + return nghttp3_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) { + n = htons(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) { + uint8_t *rv; + if (n < 64) { + *p++ = (uint8_t)n; + return p; + } + if (n < 16384) { + rv = nghttp3_put_uint16be(p, (uint16_t)n); + *p |= 0x40; + return rv; + } + if (n < 1073741824) { + rv = nghttp3_put_uint32be(p, (uint32_t)n); + *p |= 0x80; + return rv; + } + assert(n < 4611686018427387904LL); + rv = nghttp3_put_uint64be(p, (uint64_t)n); + *p |= 0xc0; + return rv; +} + +size_t nghttp3_put_varint_len(int64_t n) { + if (n < 64) { + return 1; + } + if (n < 16384) { + return 2; + } + if (n < 1073741824) { + return 4; + } + assert(n < 4611686018427387904LL); + return 8; +} + +uint64_t nghttp3_ord_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2) + 1; +} diff --git a/deps/nghttp3/lib/nghttp3_conv.h b/deps/nghttp3/lib/nghttp3_conv.h new file mode 100644 index 00000000000000..d14bdaa281b12a --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_conv.h @@ -0,0 +1,181 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_CONV_H +#define NGHTTP3_CONV_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH +# define nghttp3_ntohl64(N) be64toh(N) +# define nghttp3_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# define nghttp3_bswap64(N) \ + ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +# define nghttp3_ntohl64(N) nghttp3_bswap64(N) +# define nghttp3_htonl64(N) nghttp3_bswap64(N) +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family of functions. We + define inline functions for those functions so that we don't have + dependency on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +/* + * nghttp3_get_varint reads variable-length integer from |p|, and + * returns it in host byte order. The number of bytes read is stored + * in |*plen|. + */ +int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p); + +/* + * nghttp3_get_varint_fb reads first byte of encoded variable-length + * integer from |p|. + */ +int64_t nghttp3_get_varint_fb(const uint8_t *p); + +/* + * nghttp3_get_varint_len returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t nghttp3_get_varint_len(const uint8_t *p); + +/* + * nghttp3_put_uint64be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n); + +/* + * nghttp3_put_uint48be writes |n| in host byte order in |p| in + * network byte order. It writes only least significant 48 bits. It + * returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n); + +/* + * nghttp3_put_uint32be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n); + +/* + * nghttp3_put_uint24be writes |n| in host byte order in |p| in + * network byte order. It writes only least significant 24 bits. It + * returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n); + +/* + * nghttp3_put_uint16be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n); + +/* + * nghttp3_put_varint writes |n| in |p| using variable-length integer + * encoding. It returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n); + +/* + * nghttp3_put_varint_len returns the required number of bytes to + * encode |n|. + */ +size_t nghttp3_put_varint_len(int64_t n); + +/* + * nghttp3_ord_stream_id returns the ordinal number of |stream_id|. + */ +uint64_t nghttp3_ord_stream_id(int64_t stream_id); + +#endif /* NGHTTP3_CONV_H */ diff --git a/deps/nghttp3/lib/nghttp3_debug.c b/deps/nghttp3/lib/nghttp3_debug.c new file mode 100644 index 00000000000000..4021b0dc469b66 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_debug.c @@ -0,0 +1,61 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_debug.h" + +#include + +#ifdef DEBUGBUILD + +static void nghttp3_default_debug_vfprintf_callback(const char *fmt, + va_list args) { + vfprintf(stderr, fmt, args); +} + +static nghttp3_debug_vprintf_callback static_debug_vprintf_callback = + nghttp3_default_debug_vfprintf_callback; + +void nghttp3_debug_vprintf(const char *format, ...) { + if (static_debug_vprintf_callback) { + va_list args; + va_start(args, format); + static_debug_vprintf_callback(format, args); + va_end(args); + } +} + +void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback) { + static_debug_vprintf_callback = debug_vprintf_callback; +} + +#else /* !DEBUGBUILD */ + +void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback) { + (void)debug_vprintf_callback; +} + +#endif /* !DEBUGBUILD */ diff --git a/deps/nghttp3/lib/nghttp3_debug.h b/deps/nghttp3/lib/nghttp3_debug.h new file mode 100644 index 00000000000000..01ed918414cfe5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_debug.h @@ -0,0 +1,44 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_DEBUG_H +#define NGHTTP3_DEBUG_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#ifdef DEBUGBUILD +# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__) +void nghttp3_debug_vprintf(const char *format, ...); +#else +# define DEBUGF(...) \ + do { \ + } while (0) +#endif + +#endif /* NGHTTP3_DEBUG_H */ diff --git a/deps/nghttp3/lib/nghttp3_err.c b/deps/nghttp3/lib/nghttp3_err.c new file mode 100644 index 00000000000000..23033d4640d622 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_err.c @@ -0,0 +1,123 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_err.h" + +const char *nghttp3_strerror(int liberr) { + switch (liberr) { + case NGHTTP3_ERR_INVALID_ARGUMENT: + return "ERR_INVALID_ARGUMENT"; + case NGHTTP3_ERR_NOBUF: + return "ERR_NOBUF"; + case NGHTTP3_ERR_INVALID_STATE: + return "ERR_INVALID_STATE"; + case NGHTTP3_ERR_WOULDBLOCK: + return "ERR_WOULDBLOCK"; + case NGHTTP3_ERR_STREAM_IN_USE: + return "ERR_STREAM_IN_USE"; + case NGHTTP3_ERR_PUSH_ID_BLOCKED: + return "ERR_PUSH_ID_BLOCKED"; + case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: + return "ERR_MALFORMED_HTTP_HEADER"; + case NGHTTP3_ERR_REMOVE_HTTP_HEADER: + return "ERR_REMOVE_HTTP_HEADER"; + case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING: + return "ERR_MALFORMED_HTTP_MESSAGING"; + case NGHTTP3_ERR_TOO_LATE: + return "ERR_TOO_LATE"; + case NGHTTP3_ERR_QPACK_FATAL: + return "ERR_QPACK_FATAL"; + case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: + return "ERR_QPACK_HEADER_TOO_LARGE"; + case NGHTTP3_ERR_IGNORE_STREAM: + return "ERR_IGNORE_STREAM"; + case NGHTTP3_ERR_STREAM_NOT_FOUND: + return "ERR_STREAM_NOT_FOUND"; + case NGHTTP3_ERR_IGNORE_PUSH_PROMISE: + return "ERR_IGNORE_PUSH_PROMISE"; + case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: + return "ERR_QPACK_DECOMPRESSION_FAILED"; + case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: + return "ERR_QPACK_ENCODER_STREAM_ERROR"; + case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: + return "ERR_QPACK_DECODER_STREAM_ERROR"; + case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: + return "ERR_H3_FRAME_UNEXPECTED"; + case NGHTTP3_ERR_H3_FRAME_ERROR: + return "ERR_H3_FRAME_ERROR"; + case NGHTTP3_ERR_H3_MISSING_SETTINGS: + return "ERR_H3_MISSING_SETTINGS"; + case NGHTTP3_ERR_H3_INTERNAL_ERROR: + return "ERR_H3_INTERNAL_ERROR"; + case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: + return "ERR_CLOSED_CRITICAL_STREAM"; + case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: + return "ERR_H3_GENERAL_PROTOCOL_ERROR"; + case NGHTTP3_ERR_H3_ID_ERROR: + return "ERR_H3_ID_ERROR"; + case NGHTTP3_ERR_H3_SETTINGS_ERROR: + return "ERR_H3_SETTINGS_ERROR"; + case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: + return "ERR_H3_STREAM_CREATION_ERROR"; + case NGHTTP3_ERR_NOMEM: + return "ERR_NOMEM"; + case NGHTTP3_ERR_CALLBACK_FAILURE: + return "ERR_CALLBACK_FAILURE"; + default: + return "(unknown)"; + } +} + +uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { + switch (liberr) { + case 0: + return NGHTTP3_H3_NO_ERROR; + case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: + return NGHTTP3_QPACK_DECOMPRESSION_FAILED; + case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: + return NGHTTP3_QPACK_ENCODER_STREAM_ERROR; + case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: + return NGHTTP3_QPACK_DECODER_STREAM_ERROR; + case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: + return NGHTTP3_H3_FRAME_UNEXPECTED; + case NGHTTP3_ERR_H3_FRAME_ERROR: + return NGHTTP3_H3_FRAME_ERROR; + case NGHTTP3_ERR_H3_MISSING_SETTINGS: + return NGHTTP3_H3_MISSING_SETTINGS; + case NGHTTP3_ERR_H3_INTERNAL_ERROR: + return NGHTTP3_H3_INTERNAL_ERROR; + case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: + return NGHTTP3_H3_CLOSED_CRITICAL_STREAM; + case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: + return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; + case NGHTTP3_ERR_H3_ID_ERROR: + return NGHTTP3_H3_ID_ERROR; + case NGHTTP3_ERR_H3_SETTINGS_ERROR: + return NGHTTP3_H3_SETTINGS_ERROR; + case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: + return NGHTTP3_H3_STREAM_CREATION_ERROR; + default: + return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; + } +} diff --git a/deps/nghttp3/lib/nghttp3_err.h b/deps/nghttp3/lib/nghttp3_err.h new file mode 100644 index 00000000000000..2fa914f86b189e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_err.h @@ -0,0 +1,34 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_ERR_H +#define NGHTTP3_ERR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP3_ERR_H */ diff --git a/deps/nghttp3/lib/nghttp3_frame.c b/deps/nghttp3/lib/nghttp3_frame.c new file mode 100644 index 00000000000000..479b794f9aebb9 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_frame.c @@ -0,0 +1,200 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_frame.h" + +#include + +#include "nghttp3_conv.h" +#include "nghttp3_str.h" + +uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) { + p = nghttp3_put_varint(p, hd->type); + p = nghttp3_put_varint(p, hd->length); + return p; +} + +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { + return nghttp3_put_varint_len(hd->type) + nghttp3_put_varint_len(hd->length); +} + +uint8_t *nghttp3_frame_write_settings(uint8_t *p, + const nghttp3_frame_settings *fr) { + size_t i; + + p = nghttp3_frame_write_hd(p, &fr->hd); + + for (i = 0; i < fr->niv; ++i) { + p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id); + p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value); + } + + return p; +} + +size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, + const nghttp3_frame_settings *fr) { + size_t payloadlen = 0; + size_t i; + + for (i = 0; i < fr->niv; ++i) { + payloadlen += nghttp3_put_varint_len((int64_t)fr->iv[i].id) + + nghttp3_put_varint_len((int64_t)fr->iv[i].value); + } + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_SETTINGS) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p, + const nghttp3_frame_cancel_push *fr) { + p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_put_varint(p, fr->push_id); + + return p; +} + +size_t +nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, + const nghttp3_frame_cancel_push *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p, + const nghttp3_frame_max_push_id *fr) { + p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_put_varint(p, fr->push_id); + + return p; +} + +size_t +nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, + const nghttp3_frame_max_push_id *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_mem *mem) { + size_t i; + uint8_t *data = NULL; + size_t buflen = 0; + nghttp3_nv *p; + + if (nvlen == 0) { + *pnva = NULL; + + return 0; + } + + for (i = 0; i < nvlen; ++i) { + /* + 1 for null-termination */ + if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) { + buflen += nva[i].namelen + 1; + } + if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) { + buflen += nva[i].valuelen + 1; + } + } + + buflen += sizeof(nghttp3_nv) * nvlen; + + *pnva = nghttp3_mem_malloc(mem, buflen); + + if (*pnva == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + p = *pnva; + data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) { + p->name = nva[i].name; + p->namelen = nva[i].namelen; + } else { + if (nva[i].namelen) { + memcpy(data, nva[i].name, nva[i].namelen); + } + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp3_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + } + + if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) { + p->value = nva[i].value; + p->valuelen = nva[i].valuelen; + } else { + if (nva[i].valuelen) { + memcpy(data, nva[i].value, nva[i].valuelen); + } + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + } + + ++p; + } + return 0; +} + +void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) { + nghttp3_mem_free(mem, nva); +} + +void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, + const nghttp3_mem *mem) { + if (fr == NULL) { + return; + } + + nghttp3_nva_del(fr->nva, mem); +} + +void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, + const nghttp3_mem *mem) { + if (fr == NULL) { + return; + } + + nghttp3_nva_del(fr->nva, mem); +} diff --git a/deps/nghttp3/lib/nghttp3_frame.h b/deps/nghttp3/lib/nghttp3_frame.h new file mode 100644 index 00000000000000..d9245d9fb30c3b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_frame.h @@ -0,0 +1,141 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_FRAME_H +#define NGHTTP3_FRAME_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_buf.h" + +/* + * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This + * function assumes that |dest| has enough space to write |hd|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_hd_len returns the number of bytes required to + * write |hd|. hd->length must be set. + */ +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_settings(uint8_t *dest, + const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_settings_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, + const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to + * |dest|. This function assumes that |dest| has enough space to + * write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest, + const nghttp3_frame_cancel_push *fr); + +/* + * nghttp3_frame_write_cancel_push_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, + const nghttp3_frame_cancel_push *fr); + +/* + * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to + * |dest|. This function assumes that |dest| has enough space to + * write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest, + const nghttp3_frame_max_push_id *fr); + +/* + * nghttp3_frame_write_max_push_id_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, + const nghttp3_frame_max_push_id *fr); + +/* + * nghttp3_nva_copy copies name/value pairs from |nva|, which contains + * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so + * that all items can be stored. The resultant name and value in + * nghttp2_nv are guaranteed to be NULL-terminated even if the input + * is not null-terminated. + * + * The |*pnva| must be freed using nghttp3_nva_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_mem *mem); + +/* + * nghttp3_nva_del frees |nva|. + */ +void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem); + +/* + * nghttp3_frame_headers_free frees memory allocated for |fr|. It + * assumes that fr->nva is created by nghttp3_nva_copy() or NULL. + */ +void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, + const nghttp3_mem *mem); + +/* + * nghttp3_frame_push_promise_free frees memory allocated for |fr|. + * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL. + */ +void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, + const nghttp3_mem *mem); + +#endif /* NGHTTP3_FRAME_H */ diff --git a/deps/nghttp3/lib/nghttp3_gaptr.c b/deps/nghttp3/lib/nghttp3_gaptr.c new file mode 100644 index 00000000000000..7057cfde2cff2b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_gaptr.c @@ -0,0 +1,131 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_gaptr.h" + +#include +#include + +#include "nghttp3_macro.h" + +int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { + int rv; + nghttp3_range range = {0, UINT64_MAX}; + nghttp3_ksl_key key; + + rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, + sizeof(nghttp3_range), mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_ksl_insert(&gaptr->gap, NULL, nghttp3_ksl_key_ptr(&key, &range), + NULL); + if (rv != 0) { + nghttp3_ksl_free(&gaptr->gap); + return rv; + } + + gaptr->mem = mem; + + return 0; +} + +void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { + if (gaptr == NULL) { + return; + } + + nghttp3_ksl_free(&gaptr->gap); +} + +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { + int rv; + nghttp3_range k, m, l, r, q = {offset, offset + datalen}; + nghttp3_ksl_it it; + nghttp3_ksl_key key, old_key; + + it = + nghttp3_ksl_lower_bound_compar(&gaptr->gap, nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); + + for (; !nghttp3_ksl_it_end(&it);) { + k = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + m = nghttp3_range_intersect(&q, &k); + if (!nghttp3_range_len(&m)) { + break; + } + + if (nghttp3_range_eq(&k, &m)) { + nghttp3_ksl_remove(&gaptr->gap, &it, nghttp3_ksl_key_ptr(&key, &k)); + continue; + } + nghttp3_range_cut(&l, &r, &k, &m); + if (nghttp3_range_len(&l)) { + nghttp3_ksl_update_key(&gaptr->gap, nghttp3_ksl_key_ptr(&old_key, &k), + nghttp3_ksl_key_ptr(&key, &l)); + + if (nghttp3_range_len(&r)) { + rv = nghttp3_ksl_insert(&gaptr->gap, &it, nghttp3_ksl_key_ptr(&key, &r), + NULL); + if (rv != 0) { + return rv; + } + } + } else if (nghttp3_range_len(&r)) { + nghttp3_ksl_update_key(&gaptr->gap, nghttp3_ksl_key_ptr(&old_key, &k), + nghttp3_ksl_key_ptr(&key, &r)); + } + nghttp3_ksl_it_next(&it); + } + return 0; +} + +uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { + nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); + nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + return r.begin; +} + +nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset) { + nghttp3_range q = {offset, offset + 1}; + nghttp3_ksl_key key; + return nghttp3_ksl_lower_bound_compar(&gaptr->gap, + nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); +} + +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, + size_t datalen) { + nghttp3_ksl_key key; + nghttp3_range q = {offset, offset + datalen}; + nghttp3_ksl_it it = + nghttp3_ksl_lower_bound_compar(&gaptr->gap, nghttp3_ksl_key_ptr(&key, &q), + nghttp3_ksl_range_exclusive_compar); + nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it).ptr; + nghttp3_range m = nghttp3_range_intersect(&q, &k); + return nghttp3_range_len(&m) == 0; +} diff --git a/deps/nghttp3/lib/nghttp3_gaptr.h b/deps/nghttp3/lib/nghttp3_gaptr.h new file mode 100644 index 00000000000000..6972b404432377 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_gaptr.h @@ -0,0 +1,98 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_GAPTR_H +#define NGHTTP3_GAPTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" +#include "nghttp3_range.h" +#include "nghttp3_ksl.h" + +/* + * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct { + /* gap maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + nghttp3_ksl gap; + /* mem is custom memory allocator */ + const nghttp3_mem *mem; +} nghttp3_gaptr; + +/* + * nghttp3_gaptr_init initializes |gaptr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); + +/* + * nghttp3_gaptr_free frees resources allocated for |gaptr|. + */ +void nghttp3_gaptr_free(nghttp3_gaptr *gaptr); + +/* + * nghttp3_gaptr_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); + +/* + * nghttp3_gaptr_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr); + +/* + * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to + * the first gap which overlaps or comes after |offset|. + */ +nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset); + +/* + * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset + + * datalen) is completely pushed into this object. + */ +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, + size_t datalen); + +#endif /* NGHTTP3_GAPTR_H */ diff --git a/deps/nghttp3/lib/nghttp3_http.c b/deps/nghttp3/lib/nghttp3_http.c new file mode 100644 index 00000000000000..c66d9c9d3069ae --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_http.c @@ -0,0 +1,731 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_http.h" + +#include +#include + +#include "nghttp3_stream.h" +#include "nghttp3_macro.h" + +static uint8_t downcase(uint8_t c) { + return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp3_http_state *http, + const nghttp3_qpack_nv *nv, int flag) { + if (http->flags & flag) { + return 0; + } + if (lws(nv->value->base, nv->value->len)) { + return 0; + } + http->flags = (uint16_t)(http->flags | flag); + return 1; +} + +static int expect_response_body(nghttp3_http_state *http) { + return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 && + http->status_code / 100 != 1 && http->status_code != 304 && + http->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp3_http_state *http) { + return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) || + ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) && + (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK))); +} + +static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int trailers, + int connect_protocol) { + if (nv->name->base[0] == ':') { + if (trailers || + (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__AUTHORITY: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN__METHOD: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + switch (nv->value->len) { + case 4: + if (lstreq("HEAD", nv->value->base, nv->value->len)) { + http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value->base[6]) { + case 'T': + if (lstreq("CONNECT", nv->value->base, nv->value->len)) { + if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { + /* we won't allow CONNECT for push */ + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { + http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP3_QPACK_TOKEN__PATH: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (nv->value->base[0] == '/') { + http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR; + } else if (nv->value->len == 1 && nv->value->base[0] == '*') { + http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP3_QPACK_TOKEN__SCHEME: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || + (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; + } + break; + case NGHTTP3_QPACK_TOKEN__PROTOCOL: + if (!connect_protocol) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN_HOST: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { + if (http->content_length != -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = parse_uint(nv->value->base, nv->value->len); + if (http->content_length == -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP3_QPACK_TOKEN_CONNECTION: + case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: + case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: + case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: + case NGHTTP3_QPACK_TOKEN_UPGRADE: + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + case NGHTTP3_QPACK_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int http_response_on_header(nghttp3_http_state *http, + nghttp3_qpack_nv *nv, int trailers) { + if (nv->name->base[0] == ':') { + if (trailers || + (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__STATUS: { + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (nv->value->len != 3) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); + if (http->status_code < 100 || http->status_code == 101) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { + if (http->status_code == 204) { + /* content-length header field in 204 response is prohibited by + RFC 7230. But some widely used servers send content-length: + 0. Until they get fixed, we ignore it. */ + if (http->content_length != -1) { + /* Found multiple content-length field */ + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (!lstrieq("0", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = 0; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->status_code / 100 == 1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ + if (http->status_code / 100 == 2 && + (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->content_length != -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = parse_uint(nv->value->base, nv->value->len); + if (http->content_length == -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP3_QPACK_TOKEN_CONNECTION: + case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: + case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: + case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: + case NGHTTP3_QPACK_TOKEN_UPGRADE: + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + case NGHTTP3_QPACK_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +/* Generated by genauthroitychartbl.py */ +static char VALID_AUTHORITY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, + 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +static int check_authority(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_AUTHORITY_CHARS[*value]) { + return 0; + } + } + return 1; +} + +static int check_scheme(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + + if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { + return 0; + } + + last = value + len; + ++value; + + for (; value != last; ++value) { + if (!(('A' <= *value && *value <= 'Z') || + ('a' <= *value && *value <= 'z') || + ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || + *value == '.')) { + return 0; + } + } + return 1; +} + +int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int request, int trailers) { + int rv; + size_t i; + uint8_t c; + + if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) { + if (nv->name->len > 0 && nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->name->len; ++i) { + c = nv->name->base[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + /* When ignoring regular header fields, we set this flag so that + we still enforce header field ordering rule for pseudo header + fields. */ + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + + if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY || + nv->token == NGHTTP3_QPACK_TOKEN_HOST) { + rv = check_authority(nv->value->base, nv->value->len); + } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) { + rv = check_scheme(nv->value->base, nv->value->len); + } else { + rv = nghttp3_check_header_value(nv->value->base, nv->value->len); + } + + if (rv == 0) { + assert(nv->name->len > 0); + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + /* When ignoring regular header fields, we set this flag so that + we still enforce header field ordering rule for pseudo header + fields. */ + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + + if (request) { + return http_request_on_header(http, frame_type, nv, trailers, + /* connect_protocol = */ 0); + } + + return http_response_on_header(http, nv, trailers); +} + +int nghttp3_http_on_request_headers(nghttp3_http_state *http) { + if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && + (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { + if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) || + (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = -1; + } else { + if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) != + NGHTTP3_HTTP_FLAG_REQ_HEADERS || + (http->flags & + (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && + ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 || + (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (!check_path(http)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + return 0; +} + +int nghttp3_http_on_response_headers(nghttp3_http_state *http) { + if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + + if (http->status_code / 100 == 1) { + /* non-final response */ + http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | + NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->content_length = -1; + http->status_code = -1; + return 0; + } + + http->flags = + (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + + if (!expect_response_body(http)) { + http->content_length = 0; + } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + http->content_length = -1; + } + + return 0; +} + +int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) { + if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) { + return 0; + } + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->rx.http.content_length != -1 && + stream->rx.http.content_length != stream->rx.http.recv_content_length)) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + + return 0; +} + +int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) { + stream->rx.http.recv_content_length += (int64_t)n; + + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->rx.http.content_length != -1 && + stream->rx.http.recv_content_length > stream->rx.http.content_length)) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + + return 0; +} + +void nghttp3_http_record_request_method(nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen) { + size_t i; + const nghttp3_nv *nv; + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} + +/* Generated by gennmchartbl.py */ +static const int VALID_HD_NAME_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, + 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, + 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, + 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, + 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, + 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, + 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, + 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, + 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +int nghttp3_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ +static const int VALID_HD_VALUE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +int nghttp3_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} diff --git a/deps/nghttp3/lib/nghttp3_http.h b/deps/nghttp3/lib/nghttp3_http.h new file mode 100644 index 00000000000000..00b74f2a260c0e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_http.h @@ -0,0 +1,146 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_HTTP_H +#define NGHTTP3_HTTP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +struct nghttp3_stream; +typedef struct nghttp3_stream nghttp3_stream; + +struct nghttp3_http_state; +typedef struct nghttp3_http_state nghttp3_http_state; + +/* HTTP related flags to enforce HTTP semantics */ +typedef enum { + NGHTTP3_HTTP_FLAG_NONE = 0, + /* header field seen so far */ + NGHTTP3_HTTP_FLAG__AUTHORITY = 1, + NGHTTP3_HTTP_FLAG__PATH = 1 << 1, + NGHTTP3_HTTP_FLAG__METHOD = 1 << 2, + NGHTTP3_HTTP_FLAG__SCHEME = 1 << 3, + /* host is not pseudo header, but we require either host or + :authority */ + NGHTTP3_HTTP_FLAG_HOST = 1 << 4, + NGHTTP3_HTTP_FLAG__STATUS = 1 << 5, + /* required header fields for HTTP request except for CONNECT + method. */ + NGHTTP3_HTTP_FLAG_REQ_HEADERS = NGHTTP3_HTTP_FLAG__METHOD | + NGHTTP3_HTTP_FLAG__PATH | + NGHTTP3_HTTP_FLAG__SCHEME, + NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, + /* HTTP method flags */ + NGHTTP3_HTTP_FLAG_METH_CONNECT = 1 << 7, + NGHTTP3_HTTP_FLAG_METH_HEAD = 1 << 8, + NGHTTP3_HTTP_FLAG_METH_OPTIONS = 1 << 9, + NGHTTP3_HTTP_FLAG_METH_ALL = NGHTTP3_HTTP_FLAG_METH_CONNECT | + NGHTTP3_HTTP_FLAG_METH_HEAD | + NGHTTP3_HTTP_FLAG_METH_OPTIONS, + /* :path category */ + /* path starts with "/" */ + NGHTTP3_HTTP_FLAG_PATH_REGULAR = 1 << 11, + /* path "*" */ + NGHTTP3_HTTP_FLAG_PATH_ASTERISK = 1 << 12, + /* scheme */ + /* "http" or "https" scheme */ + NGHTTP3_HTTP_FLAG_SCHEME_HTTP = 1 << 13, + /* set if final response is expected */ + NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, + NGHTTP3_HTTP_FLAG__PROTOCOL = 1 << 15, +} nghttp3_http_flag; + +/* + * This function is called when HTTP header field |nv| in a frame of type + * |frame_type| is received for |http|. This function will validate |nv| + * against the current state of stream. Pass nonzero if this is request + * headers. Pass nonzero to |trailers| if |nv| is included in trailers. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP3_ERR_REMOVE_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int request, int trailers); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Required HTTP header field was not received; or an invalid + * header field was received. + */ +int nghttp3_http_on_request_headers(nghttp3_http_state *http); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Required HTTP header field was not received; or an invalid + * header field was received. + */ +int nghttp3_http_on_response_headers(nghttp3_http_state *http); + +/* + * This function is called when read side stream is closed. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING + * HTTP messaging is violated. + */ +int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING + * HTTP messaging is violated. + */ +int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n); + +/* + * This function inspects header fields in |nva| of length |nvlen| and + * records its method in stream->http_flags. + */ +void nghttp3_http_record_request_method(nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen); + +#endif /* NGHTTP3_HTTP_H */ diff --git a/deps/nghttp3/lib/nghttp3_idtr.c b/deps/nghttp3/lib/nghttp3_idtr.c new file mode 100644 index 00000000000000..cd8fd82e6b2bb5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_idtr.c @@ -0,0 +1,88 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_idtr.h" + +#include + +int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { + int rv; + + rv = nghttp3_gaptr_init(&idtr->gap, mem); + if (rv != 0) { + return rv; + } + + idtr->server = server; + idtr->mem = mem; + + return 0; +} + +void nghttp3_idtr_free(nghttp3_idtr *idtr) { + if (idtr == NULL) { + return; + } + + nghttp3_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to id space used by + * nghttp3_idtr. + */ +static uint64_t id_from_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2); +} + +int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) { + return NGHTTP3_ERR_STREAM_IN_USE; + } + + return nghttp3_gaptr_push(&idtr->gap, q, 1); +} + +int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1); +} + +uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) { + return nghttp3_gaptr_first_gap_offset(&idtr->gap); +} diff --git a/deps/nghttp3/lib/nghttp3_idtr.h b/deps/nghttp3/lib/nghttp3_idtr.h new file mode 100644 index 00000000000000..ba2808a11f5c68 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_idtr.h @@ -0,0 +1,99 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_IDTR_H +#define NGHTTP3_IDTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" +#include "nghttp3_gaptr.h" + +/* + * nghttp3_idtr tracks the usage of stream ID. + */ +typedef struct { + /* gap maintains the range of ID which is not used yet. Initially, + its range is [0, UINT64_MAX). */ + nghttp3_gaptr gap; + /* server is nonzero if this object records server initiated stream + ID. */ + int server; + /* mem is custom memory allocator */ + const nghttp3_mem *mem; +} nghttp3_idtr; + +/* + * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer + * per chunk. + * + * If this object records server initiated ID (even number), set + * |server| to nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); + +/* + * nghttp3_idtr_free frees resources allocated for |idtr|. + */ +void nghttp3_idtr_free(nghttp3_idtr *idtr); + +/* + * nghttp3_idtr_open claims that |stream_id| is in used. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_STREAM_IN_USE + * ID has already been used. + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id); + +/* + * nghttp3_idtr_open tells whether ID |stream_id| is in used or not. + * + * It returns nonzero if |stream_id| is used. + */ +int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id); + +/* + * nghttp3_idtr_first_gap returns the first id of first gap. If there + * is no gap, it returns UINT64_MAX. The returned id is an id space + * used in this object internally, and not stream ID. + */ +uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr); + +#endif /* NGHTTP3_IDTR_H */ diff --git a/deps/nghttp3/lib/nghttp3_ksl.c b/deps/nghttp3/lib/nghttp3_ksl.c new file mode 100644 index 00000000000000..35bae07d1e7d70 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ksl.c @@ -0,0 +1,751 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_ksl.h" + +#include +#include +#include +#include + +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" +#include "nghttp3_range.h" + +static size_t ksl_nodelen(size_t keylen) { + return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & + (size_t)~0xf; +} + +static size_t ksl_blklen(size_t nodelen) { + return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK - + sizeof(uint64_t); +} + +/* + * ksl_node_set_key sets |key| to |node|. + */ +static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, + const void *key) { + memcpy(&node->key, key, ksl->keylen); +} + +/* + * ksl_node_key assigns the pointer to key of |node| to key->ptr and + * returns |key|. + */ +static nghttp3_ksl_key *ksl_node_key(nghttp3_ksl_key *key, + nghttp3_ksl_node *node) { + key->ptr = &node->key; + return key; +} + +/* + * ksl_nth_node returns |n|th node under |blk|. + */ +static nghttp3_ksl_node *ksl_nth_node(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t n) { + return (nghttp3_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} + +nghttp3_ksl_node *nghttp3_ksl_nth_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t n) { + return ksl_nth_node(ksl, blk, n); +} + +int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, + const nghttp3_mem *mem) { + size_t nodelen = ksl_nodelen(keylen); + size_t blklen = ksl_blklen(nodelen); + nghttp3_ksl_blk *head; + + ksl->head = nghttp3_mem_malloc(mem, blklen); + if (!ksl->head) { + return NGHTTP3_ERR_NOMEM; + } + ksl->front = ksl->back = ksl->head; + ksl->compar = compar; + ksl->keylen = keylen; + ksl->nodelen = nodelen; + ksl->n = 0; + ksl->mem = mem; + + head = ksl->head; + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; + + return 0; +} + +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + ksl_free_blk(ksl, ksl_nth_node(ksl, blk, i)->blk); + } + } + + nghttp3_mem_free(ksl->mem, blk); +} + +void nghttp3_ksl_free(nghttp3_ksl *ksl) { + if (!ksl) { + return; + } + + ksl_free_blk(ksl, ksl->head); +} + +/* + * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new + * nghttp3_ksl_blk is always the "right" block. + * + * It returns the pointer to the nghttp3_ksl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + nghttp3_ksl_blk *rblk; + + rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + if (rblk->next) { + rblk->next->prev = rblk; + } else if (ksl->back == blk) { + ksl->back = rblk; + } + rblk->prev = blk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), + ksl->nodelen * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGHTTP3_KSL_MIN_NBLK); + assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK); + + return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *node; + nghttp3_ksl_blk *lblk = ksl_nth_node(ksl, blk, i)->blk, *rblk; + + rblk = ksl_split_blk(ksl, lblk); + if (rblk == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + memmove(blk->nodes + (i + 2) * ksl->nodelen, + blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + node = ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + + return 0; +} + +/* + * ksl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_head(nghttp3_ksl *ksl) { + nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; + nghttp3_ksl_node *node; + + rblk = ksl_split_blk(ksl, ksl->head); + if (rblk == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + lblk = ksl->head; + + nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (nhead == NULL) { + nghttp3_mem_free(ksl->mem, rblk); + return NGHTTP3_ERR_NOMEM; + } + nhead->next = nhead->prev = NULL; + nhead->n = 2; + nhead->leaf = 0; + + node = ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + node->blk = rblk; + + ksl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose key is |key| with the associated + * |data| at the index of |i|. This function assumes that the number + * of nodes contained by |blk| is strictly less than + * NGHTTP3_KSL_MAX_NBLK. + */ +static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, + const nghttp3_ksl_key *key, void *data) { + nghttp3_ksl_node *node; + + assert(blk->n < NGHTTP3_KSL_MAX_NBLK); + + memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, + ksl->nodelen * (blk->n - i)); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key->ptr); + node->data = data; + + ++blk->n; +} + +static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar) { + nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid; + nghttp3_ksl_node *node; + nghttp3_ksl_key node_key; + + while (right - left > 1) { + mid = (left + right) / 2; + node = ksl_nth_node(ksl, blk, (size_t)mid); + if (compar(ksl_node_key(&node_key, node), key)) { + left = mid; + } else { + right = mid; + } + } + + return (size_t)right; +} + +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key, void *data) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + nghttp3_ksl_key node_key; + size_t i; + int rv; + + if (blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_head(ksl); + if (rv != 0) { + return rv; + } + blk = ksl->head; + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + ksl_insert_node(ksl, blk, i, key, data); + ++ksl->n; + if (it) { + nghttp3_ksl_it_init(it, ksl, blk, i); + } + return 0; + } + + if (i == blk->n) { + /* This insertion extends the largest key in this subtree. */ + for (; !blk->leaf;) { + node = ksl_nth_node(ksl, blk, blk->n - 1); + if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, blk->n - 1); + if (rv != 0) { + return rv; + } + node = ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key->ptr); + blk = node->blk; + } + ksl_insert_node(ksl, blk, blk->n, key, data); + ++ksl->n; + if (it) { + nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1); + } + return 0; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, i); + if (rv != 0) { + return rv; + } + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + node = ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + ksl_node_set_key(ksl, node, key->ptr); + } + } + } + + blk = node->blk; + } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |ksl| by 1. + * + * This function returns the pointer to the merged block. + */ +static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t i) { + nghttp3_ksl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = ksl_nth_node(ksl, blk, i)->blk; + rblk = ksl_nth_node(ksl, blk, i + 1)->blk; + + assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK); + + memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, + ksl->nodelen * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + if (lblk->next) { + lblk->next->prev = lblk; + } else if (ksl->back == rblk) { + ksl->back = lblk; + } + + nghttp3_mem_free(ksl->mem, rblk); + + if (ksl->head == blk && blk->n == 2) { + nghttp3_mem_free(ksl->mem, ksl->head); + ksl->head = lblk; + } else { + ksl_remove_node(ksl, blk, i + 1); + ksl_node_set_key(ksl, ksl_nth_node(ksl, blk, i), + &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + } + + return lblk; +} + +/* + * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to + * blk->nodes[i - 1]->blk->nodes. + */ +static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *lnode, *rnode, *dest, *src; + + assert(i > 0); + + lnode = ksl_nth_node(ksl, blk, i - 1); + rnode = ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK); + assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK); + + dest = ksl_nth_node(ksl, lnode->blk, lnode->blk->n); + src = ksl_nth_node(ksl, rnode->blk, 0); + + memcpy(dest, src, ksl->nodelen); + ksl_node_set_key(ksl, lnode, &dest->key); + ++lnode->blk->n; + + --rnode->blk->n; + memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, + ksl->nodelen * rnode->blk->n); +} + +/* + * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to + * blk->nodes[i + 1]->blk->nodes. + */ +static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *lnode, *rnode, *dest, *src; + + assert(i < blk->n - 1); + + lnode = ksl_nth_node(ksl, blk, i); + rnode = ksl_nth_node(ksl, blk, i + 1); + + assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK); + assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK); + + memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, + ksl->nodelen * rnode->blk->n); + ++rnode->blk->n; + + dest = ksl_nth_node(ksl, rnode->blk, 0); + src = ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); + + memcpy(dest, src, ksl->nodelen); + + --lnode->blk->n; + ksl_node_set_key(ksl, lnode, + &ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +void nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + size_t i; + + if (!blk->leaf && blk->n == 2 && + ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && + ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { + blk = ksl_merge_node(ksl, ksl->head, 0); + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + assert(i < blk->n); + + if (blk->leaf) { + assert(i < blk->n); + ksl_remove_node(ksl, blk, i); + --ksl->n; + if (it) { + if (blk->n == i && blk->next) { + nghttp3_ksl_it_init(it, ksl, blk->next, 0); + } else { + nghttp3_ksl_it_init(it, ksl, blk, i); + } + } + return; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP3_KSL_MIN_NBLK) { + if (i > 0 && + ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + } else if (i + 1 < blk->n && + ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + } else if (i > 0) { + blk = ksl_merge_node(ksl, blk, i - 1); + } else { + assert(i + 1 < blk->n); + blk = ksl_merge_node(ksl, blk, i); + } + } else { + blk = node->blk; + } + } +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ksl_nth_node(ksl, blk, i)->blk; + } +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ksl_nth_node(ksl, blk, i)->blk; + } +} + +void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, + const nghttp3_ksl_key *new_key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + nghttp3_ksl_key node_key; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, ksl_node_key(&node_key, node), old_key)); + ksl_node_set_key(ksl, node, new_key->ptr); + return; + } + + ksl_node_key(&node_key, node); + if (key_equal(ksl->compar, &node_key, old_key) || + ksl->compar(&node_key, new_key)) { + ksl_node_set_key(ksl, node, new_key->ptr); + } + + blk = node->blk; + } +} + +static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { + size_t i; + nghttp3_ksl_node *node; + nghttp3_ksl_key node_key; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *ksl_node_key(&node_key, node)->i); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, ksl_nth_node(ksl, blk, i)->blk, level + 1); + } +} + +size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; } + +void nghttp3_ksl_clear(nghttp3_ksl *ksl) { + size_t i; + nghttp3_ksl_blk *head; + + if (!ksl->head->leaf) { + for (i = 0; i < ksl->head->n; ++i) { + ksl_free_blk(ksl, ksl_nth_node(ksl, ksl->head, i)->blk); + } + } + + ksl->front = ksl->back = ksl->head; + ksl->n = 0; + + head = ksl->head; + + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; +} + +void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } + +nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { + nghttp3_ksl_it it; + nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + return it; +} + +nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { + nghttp3_ksl_it it; + nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + return it; +} + +void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t i) { + it->ksl = ksl; + it->blk = blk; + it->i = i; +} + +void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { + assert(it->i < it->blk->n); + return ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void nghttp3_ksl_it_next(nghttp3_ksl_it *it) { + assert(!nghttp3_ksl_it_end(it)); + + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} + +void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) { + assert(!nghttp3_ksl_it_begin(it)); + + if (it->i == 0) { + it->blk = it->blk->prev; + it->i = it->blk->n - 1; + } else { + --it->i; + } +} + +int nghttp3_ksl_it_end(const nghttp3_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} + +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +nghttp3_ksl_key nghttp3_ksl_it_key(const nghttp3_ksl_it *it) { + nghttp3_ksl_key node_key; + + assert(it->i < it->blk->n); + + return *ksl_node_key(&node_key, ksl_nth_node(it->ksl, it->blk, it->i)); +} + +nghttp3_ksl_key *nghttp3_ksl_key_ptr(nghttp3_ksl_key *key, const void *ptr) { + key->ptr = ptr; + return key; +} + +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_range *a = lhs->ptr, *b = rhs->ptr; + return a->begin < b->begin; +} + +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_range *a = lhs->ptr, *b = rhs->ptr; + return a->begin < b->begin && + !(nghttp3_max(a->begin, b->begin) < nghttp3_min(a->end, b->end)); +} diff --git a/deps/nghttp3/lib/nghttp3_ksl.h b/deps/nghttp3/lib/nghttp3_ksl.h new file mode 100644 index 00000000000000..89ca732aeb6fee --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ksl.h @@ -0,0 +1,336 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_KSL_H +#define NGHTTP3_KSL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +/* + * Skip List using single key instead of range. + */ + +#define NGHTTP3_KSL_DEGR 8 +/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1) +/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1) + +/* + * nghttp3_ksl_key represents key in nghttp3_ksl. + */ +typedef union { + /* i is defined to retrieve int64_t key for convenience. */ + const int64_t *i; + /* ptr points to the key. */ + const void *ptr; +} nghttp3_ksl_key; + +struct nghttp3_ksl_node; +typedef struct nghttp3_ksl_node nghttp3_ksl_node; + +struct nghttp3_ksl_blk; +typedef struct nghttp3_ksl_blk nghttp3_ksl_blk; + +/* + * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or + * opaque data. If a node is an internal node, it contains + * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the + * location starting at key. + */ +struct nghttp3_ksl_node { + union { + nghttp3_ksl_blk *blk; + void *data; + }; + union { + uint64_t align; + /* key is a buffer to include key associated to this node. + Because the length of key is unknown until nghttp3_ksl_init is + called, the actual buffer will be allocated after this + field. */ + uint8_t key[1]; + }; +}; + +/* + * nghttp3_ksl_blk contains nghttp3_ksl_node objects. + */ +struct nghttp3_ksl_blk { + /* next points to the next block if leaf field is nonzero. */ + nghttp3_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + nghttp3_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + size_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + int leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK + nghttp3_ksl_node objects. Because nghttp3_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until nghttp3_ksl_init + is called. */ + uint8_t nodes[1]; + }; +}; + +/* + * nghttp3_ksl_compar is a function type which returns nonzero if key + * |lhs| should be placed before |rhs|. It returns 0 otherwise. + */ +typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +struct nghttp3_ksl; +typedef struct nghttp3_ksl nghttp3_ksl; + +struct nghttp3_ksl_it; +typedef struct nghttp3_ksl_it nghttp3_ksl_it; + +/* + * nghttp3_ksl_it is a forward iterator to iterate nodes. + */ +struct nghttp3_ksl_it { + const nghttp3_ksl *ksl; + nghttp3_ksl_blk *blk; + size_t i; +}; + +/* + * nghttp3_ksl is a deterministic paged skip list. + */ +struct nghttp3_ksl { + /* head points to the root block. */ + nghttp3_ksl_blk *head; + /* front points to the first leaf block. */ + nghttp3_ksl_blk *front; + /* back points to the last leaf block. */ + nghttp3_ksl_blk *back; + nghttp3_ksl_compar compar; + size_t n; + /* keylen is the size of key */ + size_t keylen; + /* nodelen is the actual size of nghttp3_ksl_node including key + storage. */ + size_t nodelen; + const nghttp3_mem *mem; +}; + +/* + * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare + * function. |keylen| is the length of key. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, + const nghttp3_mem *mem); + +/* + * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |ksl| itself. + */ +void nghttp3_ksl_free(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_insert inserts |key| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function assumes that |key| does not exist in |ksl|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key, void *data); + +/* + * nghttp3_ksl_remove removes the |key| from |ksl|. It assumes such + * the key is included in |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. + */ +void nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_lower_bound returns the iterator which points to the + * first node which has the key which is equal to |key| or the last + * node which satisfies !compar(&node->key, key). If there is no such + * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it) + * != 0. + */ +nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar); + +/* + * nghttp3_ksl_update_key replaces the key of nodes which has |old_key| + * with |new_key|. |new_key| must be strictly greater than the + * previous node and strictly smaller than the next node. + */ +void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, + const nghttp3_ksl_key *new_key); + +/* + * nghttp3_ksl_begin returns the iterator which points to the first + * node. If there is no node in |ksl|, it returns the iterator which + * satisfies nghttp3_ksl_it_end(it) != 0. + */ +nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_end returns the iterator which points to the node + * following the last node. The returned object satisfies + * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the + * iterator which satisfies nghttp3_ksl_it_begin(it) != 0. + */ +nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_len returns the number of elements stored in |ksl|. + */ +size_t nghttp3_ksl_len(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_clear removes all elements stored in |ksl|. + */ +void nghttp3_ksl_clear(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_nth_node returns the |n|th node under |blk|. This + * function is provided for unit testing. + */ +nghttp3_ksl_node *nghttp3_ksl_nth_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t n); + +/* + * nghttp3_ksl_print prints its internal state in stderr. It assumes + * that the key is of type int64_t. This function should be used for + * the debugging purpose only. + */ +void nghttp3_ksl_print(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_it_init initializes |it|. + */ +void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t i); + +/* + * nghttp3_ksl_it_get returns the data associated to the node which + * |it| points to. It is undefined to call this function when + * nghttp3_ksl_it_end(it) returns nonzero. + */ +void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_next advances the iterator by one. It is undefined + * if this function is called when nghttp3_ksl_it_end(it) returns + * nonzero. + */ +void nghttp3_ksl_it_next(nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_prev moves backward the iterator by one. It is + * undefined if this function is called when nghttp3_ksl_it_begin(it) + * returns nonzero. + */ +void nghttp3_ksl_it_prev(nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +int nghttp3_ksl_it_end(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_begin returns nonzero if |it| points to the first + * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and + * nghttp3_ksl_it_end(&it) if the skip list has no node. + */ +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when nghttp3_ksl_it_end(it) + * returns nonzero. + */ +nghttp3_ksl_key nghttp3_ksl_it_key(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_key_ptr is a convenient function which initializes + * |key| with |ptr| and returns |key|. + */ +nghttp3_ksl_key *nghttp3_ksl_key_ptr(nghttp3_ksl_key *key, const void *ptr); + +/* + * nghttp3_ksl_range_compar is an implementation of + * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin. + */ +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_range_exclusive_compar is an implementation of + * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +#endif /* NGHTTP3_KSL_H */ diff --git a/deps/nghttp3/lib/nghttp3_macro.h b/deps/nghttp3/lib/nghttp3_macro.h new file mode 100644 index 00000000000000..6ee704cc47dd98 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_macro.h @@ -0,0 +1,47 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_MACRO_H +#define NGHTTP3_MACRO_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B)) + +#define nghttp3_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A))) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +#endif /* NGHTTP3_MACRO_H */ diff --git a/deps/nghttp3/lib/nghttp3_map.c b/deps/nghttp3/lib/nghttp3_map.c new file mode 100644 index 00000000000000..e383d2b0b8a3fc --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_map.c @@ -0,0 +1,213 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_map.h" + +#include + +#include "nghttp3_conv.h" + +#define INITIAL_TABLE_LENGTH 256 + +int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_entry *)); + if (map->table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp3_map_free(nghttp3_map *map) { + nghttp3_mem_free(map->mem, map->table); +} + +void nghttp3_map_each_free(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp3_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int nghttp3_map_each(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry, *next; + for (entry = map->table[i]; entry;) { + next = entry->next; + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + entry = next; + } + } + return 0; +} + +void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) { + entry->key = key; + entry->next = NULL; +} + +/* FNV1a hash */ +static uint32_t hash(key_type key, uint32_t mod) { + uint8_t *p, *end; + uint32_t h = 0x811C9DC5u; + + key = nghttp3_htonl64(key); + p = (uint8_t *)&key; + end = p + sizeof(key_type); + + for (; p != end; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + return h & (mod - 1); +} + +static int insert(nghttp3_map_entry **table, uint32_t tablelen, + nghttp3_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + nghttp3_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(nghttp3_map *map, uint32_t new_tablelen) { + uint32_t i; + nghttp3_map_entry **new_table; + + new_table = + nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_entry *)); + if (new_table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + nghttp3_map_entry *entry; + for (entry = map->table[i]; entry;) { + nghttp3_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + nghttp3_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { + int rv; + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp3_map_entry *nghttp3_map_find(const nghttp3_map *map, key_type key) { + uint32_t h; + nghttp3_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int nghttp3_map_remove(nghttp3_map *map, key_type key) { + uint32_t h; + nghttp3_map_entry **dst; + + h = hash(key, map->tablelen); + + for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { + if ((*dst)->key != key) { + continue; + } + + *dst = (*dst)->next; + --map->size; + return 0; + } + return NGHTTP3_ERR_INVALID_ARGUMENT; +} + +void nghttp3_map_clear(nghttp3_map *map) { + uint32_t i; + + for (i = 0; i < map->tablelen; ++i) { + map->table[i] = NULL; + } + + map->size = 0; +} + +size_t nghttp3_map_size(const nghttp3_map *map) { return map->size; } diff --git a/deps/nghttp3/lib/nghttp3_map.h b/deps/nghttp3/lib/nghttp3_map.h new file mode 100644 index 00000000000000..0b7c0d28c37c7f --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_map.h @@ -0,0 +1,147 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_MAP_H +#define NGHTTP3_MAP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" + +/* Implementation of unordered map */ + +typedef uint64_t key_type; + +typedef struct nghttp3_map_entry { + struct nghttp3_map_entry *next; + key_type key; +} nghttp3_map_entry; + +typedef struct { + nghttp3_map_entry **table; + const nghttp3_mem *mem; + size_t size; + uint32_t tablelen; +} nghttp3_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp3_map_each_free() to free + * each entries. + */ +void nghttp3_map_free(nghttp3_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void nghttp3_map_each_free(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +nghttp3_map_entry *nghttp3_map_find(const nghttp3_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int nghttp3_map_remove(nghttp3_map *map, key_type key); + +/* + * Removes all entries from |map|. + */ +void nghttp3_map_clear(nghttp3_map *map); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp3_map_size(const nghttp3_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * nghttp3_map_each_free() instead. + */ +int nghttp3_map_each(const nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr); + +#endif /* NGHTTP3_MAP_H */ diff --git a/deps/nghttp3/lib/nghttp3_mem.c b/deps/nghttp3/lib/nghttp3_mem.c new file mode 100644 index 00000000000000..e5f93f10bdabf2 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_mem.c @@ -0,0 +1,77 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static nghttp3_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; } + +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, + void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/deps/nghttp3/lib/nghttp3_mem.h b/deps/nghttp3/lib/nghttp3_mem.h new file mode 100644 index 00000000000000..55ef86b4f921c9 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_mem.h @@ -0,0 +1,45 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_MEM_H +#define NGHTTP3_MEM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); +void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, + void *mem_user_data); +void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size); +void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP3_MEM_H */ diff --git a/deps/nghttp3/lib/nghttp3_pq.c b/deps/nghttp3/lib/nghttp3_pq.c new file mode 100644 index 00000000000000..5d09050ae63798 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_pq.c @@ -0,0 +1,168 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_pq.h" + +#include + +#include "nghttp3_macro.h" + +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, + const nghttp3_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; +} + +void nghttp3_pq_free(nghttp3_pq *pq) { + nghttp3_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp3_pq *pq, size_t i, size_t j) { + nghttp3_pq_entry *a = pq->q[i]; + nghttp3_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(nghttp3_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = nghttp3_max(4, (pq->capacity * 2)); + + nq = nghttp3_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(nghttp3_pq_entry *)); + if (nq == NULL) { + return NGHTTP3_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) { + assert(pq->length); + return pq->q[0]; +} + +static void bubble_down(nghttp3_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void nghttp3_pq_pop(nghttp3_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + nghttp3_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; } + +size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; } + +int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} + +void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; } diff --git a/deps/nghttp3/lib/nghttp3_pq.h b/deps/nghttp3/lib/nghttp3_pq.h new file mode 100644 index 00000000000000..e80b1bc43b9f3e --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_pq.h @@ -0,0 +1,129 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_PQ_H +#define NGHTTP3_PQ_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" + +/* Implementation of priority queue */ + +/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates + that an entry is not queued. Assigning this value to + nghttp3_pq_entry.index can check that the entry is queued or not. */ +#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX + +typedef struct { + size_t index; +} nghttp3_pq_entry; + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs, + const nghttp3_pq_entry *rhs); + +typedef struct { + /* The pointer to the pointer to the item stored */ + nghttp3_pq_entry **q; + /* Memory allocator */ + const nghttp3_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp3_less less; +} nghttp3_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + */ +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp3_pq_free(nghttp3_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. It is undefined if the + * queue is empty. + */ +nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp3_pq_pop(nghttp3_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp3_pq_empty(const nghttp3_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp3_pq_size(const nghttp3_pq *pq); + +typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item); + +void nghttp3_pq_clear(nghttp3_pq *pq); + +#endif /* NGHTTP3_PQ_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack.c b/deps/nghttp3/lib/nghttp3_qpack.c new file mode 100644 index 00000000000000..08303c05be06a0 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack.c @@ -0,0 +1,4056 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_qpack.h" + +#include +#include +#include + +#include "nghttp3_str.h" +#include "nghttp3_macro.h" +#include "nghttp3_debug.h" + +/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent + nghttp3_qpack_stream object to handle a client which never cancel + or acknowledge header block. After this limit, encoder stops using + dynamic table. */ +#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000 + +/* Make scalar initialization form of nghttp3_qpack_static_entry */ +#define MAKE_STATIC_ENT(I, T, H) \ + { I, T, H } + +/* Generated by mkstatichdtbl.py */ +static nghttp3_qpack_static_entry token_stable[] = { + MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u), + MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u), + MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), + MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), + MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), + MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), + MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u), + MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u), + MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u), + MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, + 901040780u), + MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, + 901040780u), + MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN, + 2710797292u), + MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS, + 2449824425u), + MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS, + 3599549072u), + MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, + 2417078055u), + MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, + 2417078055u), + MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u), + MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u), + MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u), + MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u), + MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), + MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), + MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u), + MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY, + 1569039836u), + MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u), + MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u), + MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u), + MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u), + MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u), + MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u), + MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u), + MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u), + MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u), + MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u), + MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u), + MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u), + MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u), + MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u), + MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u), + MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u), + MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u), + MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u), + MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u), + MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS, + 2479169413u), + MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u), + MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), + MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), + MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS, + 3644557769u), + MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u), + MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), + MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), + MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u), +}; + +/* Make scalar initialization form of nghttp3_qpack_static_entry */ +#define MAKE_STATIC_HD(N, V, T) \ + { \ + {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ + } + +static nghttp3_qpack_static_header stable[] = { + MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY), + MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH), + MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE), + MAKE_STATIC_HD("content-disposition", "", + NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION), + MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH), + MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE), + MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE), + MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG), + MAKE_STATIC_HD("if-modified-since", "", + NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE), + MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH), + MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED), + MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK), + MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION), + MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER), + MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE), + MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME), + MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME), + MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT), + MAKE_STATIC_HD("accept", "application/dns-message", + NGHTTP3_QPACK_TOKEN_ACCEPT), + MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br", + NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING), + MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES), + MAKE_STATIC_HD("access-control-allow-headers", "cache-control", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-headers", "content-type", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-origin", "*", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN), + MAKE_STATIC_HD("cache-control", "max-age=0", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "max-age=2592000", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "max-age=604800", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "no-cache", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "no-store", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "public, max-age=31536000", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("content-encoding", "br", + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), + MAKE_STATIC_HD("content-encoding", "gzip", + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), + MAKE_STATIC_HD("content-type", "application/dns-message", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/javascript", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/json", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/gif", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/jpeg", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/png", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/css", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/html; charset=utf-8", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/plain", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE), + MAKE_STATIC_HD("strict-transport-security", "max-age=31536000", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("strict-transport-security", + "max-age=31536000; includesubdomains", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("strict-transport-security", + "max-age=31536000; includesubdomains; preload", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY), + MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY), + MAKE_STATIC_HD("x-content-type-options", "nosniff", + NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS), + MAKE_STATIC_HD("x-xss-protection", "1; mode=block", + NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION), + MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE), + MAKE_STATIC_HD("access-control-allow-credentials", "FALSE", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), + MAKE_STATIC_HD("access-control-allow-credentials", "TRUE", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), + MAKE_STATIC_HD("access-control-allow-headers", "*", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-methods", "get", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-allow-methods", "get, post, options", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-allow-methods", "options", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-expose-headers", "content-length", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS), + MAKE_STATIC_HD("access-control-request-headers", "content-type", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS), + MAKE_STATIC_HD("access-control-request-method", "get", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), + MAKE_STATIC_HD("access-control-request-method", "post", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), + MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC), + MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION), + MAKE_STATIC_HD("content-security-policy", + "script-src 'none'; object-src 'none'; base-uri 'none'", + NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY), + MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA), + MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT), + MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED), + MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE), + MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN), + MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE), + MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER), + MAKE_STATIC_HD("timing-allow-origin", "*", + NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN), + MAKE_STATIC_HD("upgrade-insecure-requests", "1", + NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS), + MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT), + MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR), + MAKE_STATIC_HD("x-frame-options", "deny", + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), + MAKE_STATIC_HD("x-frame-options", "sameorigin", + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return n == 0 || memcmp(s1, s2, n) == 0; +} + +/* Generated by genlibtokenlookup.py */ +static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (memeq("t", name, 1)) { + return NGHTTP3_QPACK_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'e': + if (memeq("ag", name, 2)) { + return NGHTTP3_QPACK_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (memeq("dat", name, 3)) { + return NGHTTP3_QPACK_TOKEN_DATE; + } + break; + case 'g': + if (memeq("eta", name, 3)) { + return NGHTTP3_QPACK_TOKEN_ETAG; + } + break; + case 'k': + if (memeq("lin", name, 3)) { + return NGHTTP3_QPACK_TOKEN_LINK; + } + break; + case 't': + if (memeq("hos", name, 3)) { + return NGHTTP3_QPACK_TOKEN_HOST; + } + break; + case 'y': + if (memeq("var", name, 3)) { + return NGHTTP3_QPACK_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (memeq("rang", name, 4)) { + return NGHTTP3_QPACK_TOKEN_RANGE; + } + break; + case 'h': + if (memeq(":pat", name, 4)) { + return NGHTTP3_QPACK_TOKEN__PATH; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (memeq("cooki", name, 5)) { + return NGHTTP3_QPACK_TOKEN_COOKIE; + } + break; + case 'n': + if (memeq("origi", name, 5)) { + return NGHTTP3_QPACK_TOKEN_ORIGIN; + } + break; + case 'r': + if (memeq("serve", name, 5)) { + return NGHTTP3_QPACK_TOKEN_SERVER; + } + break; + case 't': + if (memeq("accep", name, 5)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'c': + if (memeq("alt-sv", name, 6)) { + return NGHTTP3_QPACK_TOKEN_ALT_SVC; + } + break; + case 'd': + if (memeq(":metho", name, 6)) { + return NGHTTP3_QPACK_TOKEN__METHOD; + } + break; + case 'e': + if (memeq(":schem", name, 6)) { + return NGHTTP3_QPACK_TOKEN__SCHEME; + } + if (memeq("purpos", name, 6)) { + return NGHTTP3_QPACK_TOKEN_PURPOSE; + } + if (memeq("upgrad", name, 6)) { + return NGHTTP3_QPACK_TOKEN_UPGRADE; + } + break; + case 'r': + if (memeq("refere", name, 6)) { + return NGHTTP3_QPACK_TOKEN_REFERER; + } + break; + case 's': + if (memeq(":statu", name, 6)) { + return NGHTTP3_QPACK_TOKEN__STATUS; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (memeq("if-rang", name, 7)) { + return NGHTTP3_QPACK_TOKEN_IF_RANGE; + } + break; + case 'n': + if (memeq("locatio", name, 7)) { + return NGHTTP3_QPACK_TOKEN_LOCATION; + } + break; + } + break; + case 9: + switch (name[8]) { + case 'd': + if (memeq("forwarde", name, 8)) { + return NGHTTP3_QPACK_TOKEN_FORWARDED; + } + break; + case 'l': + if (memeq(":protoco", name, 8)) { + return NGHTTP3_QPACK_TOKEN__PROTOCOL; + } + break; + case 't': + if (memeq("expect-c", name, 8)) { + return NGHTTP3_QPACK_TOKEN_EXPECT_CT; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'a': + if (memeq("early-dat", name, 9)) { + return NGHTTP3_QPACK_TOKEN_EARLY_DATA; + } + break; + case 'e': + if (memeq("keep-aliv", name, 9)) { + return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE; + } + if (memeq("set-cooki", name, 9)) { + return NGHTTP3_QPACK_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (memeq("connectio", name, 9)) { + return NGHTTP3_QPACK_TOKEN_CONNECTION; + } + break; + case 't': + if (memeq("user-agen", name, 9)) { + return NGHTTP3_QPACK_TOKEN_USER_AGENT; + } + break; + case 'y': + if (memeq(":authorit", name, 9)) { + return NGHTTP3_QPACK_TOKEN__AUTHORITY; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (memeq("content-typ", name, 11)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (memeq("last-modifie", name, 12)) { + return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED; + } + break; + case 'h': + if (memeq("if-none-matc", name, 12)) { + return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (memeq("cache-contro", name, 12)) { + return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (memeq("authorizatio", name, 12)) { + return NGHTTP3_QPACK_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (memeq("accept-range", name, 12)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (memeq("content-lengt", name, 13)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (memeq("accept-languag", name, 14)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (memeq("accept-encodin", name, 14)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING; + } + break; + case 'r': + if (memeq("x-forwarded-fo", name, 14)) { + return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR; + } + break; + case 's': + if (memeq("x-frame-option", name, 14)) { + return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'g': + if (memeq("content-encodin", name, 15)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (memeq("proxy-connectio", name, 15)) { + return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION; + } + if (memeq("x-xss-protectio", name, 15)) { + return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (memeq("if-modified-sinc", name, 16)) { + return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (memeq("transfer-encodin", name, 16)) { + return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'n': + if (memeq("content-dispositio", name, 18)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION; + } + if (memeq("timing-allow-origi", name, 18)) { + return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN; + } + break; + } + break; + case 22: + switch (name[21]) { + case 's': + if (memeq("x-content-type-option", name, 21)) { + return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS; + } + break; + } + break; + case 23: + switch (name[22]) { + case 'y': + if (memeq("content-security-polic", name, 22)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY; + } + break; + } + break; + case 25: + switch (name[24]) { + case 's': + if (memeq("upgrade-insecure-request", name, 24)) { + return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS; + } + break; + case 'y': + if (memeq("strict-transport-securit", name, 24)) { + return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (memeq("access-control-allow-origi", name, 26)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + case 28: + switch (name[27]) { + case 's': + if (memeq("access-control-allow-header", name, 27)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS; + } + if (memeq("access-control-allow-method", name, 27)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS; + } + break; + } + break; + case 29: + switch (name[28]) { + case 'd': + if (memeq("access-control-request-metho", name, 28)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD; + } + break; + case 's': + if (memeq("access-control-expose-header", name, 28)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS; + } + break; + } + break; + case 30: + switch (name[29]) { + case 's': + if (memeq("access-control-request-header", name, 29)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS; + } + break; + } + break; + case 32: + switch (name[31]) { + case 's': + if (memeq("access-control-allow-credential", name, 31)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS; + } + break; + } + break; + } + return -1; +} + +static size_t table_space(size_t namelen, size_t valuelen) { + return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen; +} + +static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { + return a->name->len == b->namelen && + memeq(a->name->base, b->name, b->namelen); +} + +static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { + return a->value->len == b->valuelen && + memeq(a->value->base, b->value, b->valuelen); +} + +static void qpack_map_init(nghttp3_qpack_map *map) { + memset(map, 0, sizeof(nghttp3_qpack_map)); +} + +static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { + nghttp3_qpack_entry **bucket; + + bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + + if (*bucket == NULL) { + *bucket = ent; + return; + } + + /* larger absidx is linked near the root */ + ent->map_next = *bucket; + *bucket = ent; +} + +static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { + nghttp3_qpack_entry **dst; + + dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + + for (; *dst; dst = &(*dst)->map_next) { + if (*dst != ent) { + continue; + } + + *dst = ent->map_next; + ent->map_next = NULL; + return; + } +} + +/* + * qpack_context_can_reference returns nonzero if dynamic table entry + * at |absidx| can be referenced. In other words, it is within + * ctx->max_dtable_size. + */ +static int qpack_context_can_reference(nghttp3_qpack_context *ctx, + size_t absidx) { + nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); + return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size; +} + +/* |*ppb_match| (post-base match), if it is not NULL, is always exact + match. */ +static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, + int *exact_match, + nghttp3_qpack_entry **pmatch, + nghttp3_qpack_entry **ppb_match, + const nghttp3_nv *nv, int32_t token, + uint32_t hash, size_t krcnt, + int allow_blocking, int name_only) { + nghttp3_qpack_entry *p; + + *exact_match = 0; + *pmatch = NULL; + *ppb_match = NULL; + + for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p; + p = p->map_next) { + if (token != p->nv.token || + (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) || + !qpack_context_can_reference(&encoder->ctx, p->absidx)) { + continue; + } + if (allow_blocking || p->absidx + 1 <= krcnt) { + if (!*pmatch) { + *pmatch = p; + if (name_only) { + return; + } + } + if (qpack_nv_value_eq(&p->nv, nv)) { + *pmatch = p; + *exact_match = 1; + return; + } + } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) { + *ppb_match = p; + } + } +} + +/* + * qpack_context_init initializes |ctx|. |max_dtable_size| is the + * maximum size of dynamic table. |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_context_init(nghttp3_qpack_context *ctx, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; + size_t len2; + + for (len2 = 1; len2 < len; len2 <<= 1) + ; + + rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *), + mem); + if (rv != 0) { + return rv; + } + + ctx->mem = mem; + ctx->dtable_size = 0; + ctx->dtable_sum = 0; + ctx->hard_max_dtable_size = max_dtable_size; + ctx->max_dtable_size = 0; + ctx->max_blocked = max_blocked; + ctx->next_absidx = 0; + ctx->bad = 0; + + return 0; +} + +static void qpack_context_free(nghttp3_qpack_context *ctx) { + nghttp3_qpack_entry *ent; + size_t i, len = nghttp3_ringbuf_len(&ctx->dtable); + + for (i = 0; i < len; ++i) { + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i); + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(ctx->mem, ent); + } + nghttp3_ringbuf_free(&ctx->dtable); +} + +static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + nghttp3_qpack_header_block_ref *lhs = + nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); + nghttp3_qpack_header_block_ref *rhs = + nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); + + return lhs->min_cnt < rhs->min_cnt; +} + +typedef struct { + size_t max_cnt; + uint64_t id; +} nghttp3_blocked_streams_key; + +static int max_cnt_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_blocked_streams_key *a = lhs->ptr; + const nghttp3_blocked_streams_key *b = rhs->ptr; + return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id); +} + +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + + rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_init(&encoder->streams, mem); + if (rv != 0) { + goto streams_init_fail; + } + + rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + sizeof(nghttp3_blocked_streams_key), mem); + if (rv != 0) { + goto blocked_streams_init_fail; + } + + qpack_map_init(&encoder->dtable_map); + nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem); + + encoder->krcnt = 0; + encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; + encoder->opcode = 0; + encoder->min_dtable_update = SIZE_MAX; + encoder->last_max_dtable_update = 0; + encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; + + nghttp3_qpack_read_state_reset(&encoder->rstate); + + return 0; + +blocked_streams_init_fail: + nghttp3_map_free(&encoder->streams); +streams_init_fail: + qpack_context_free(&encoder->ctx); + + return rv; +} + +static int map_stream_free(nghttp3_map_entry *entry, void *ptr) { + const nghttp3_mem *mem = ptr; + nghttp3_qpack_stream *stream = + nghttp3_struct_of(entry, nghttp3_qpack_stream, me); + nghttp3_qpack_stream_del(stream, mem); + return 0; +} + +void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) { + nghttp3_pq_free(&encoder->min_cnts); + nghttp3_ksl_free(&encoder->blocked_streams); + nghttp3_map_each_free(&encoder->streams, map_stream_free, + (void *)encoder->ctx.mem); + nghttp3_map_free(&encoder->streams); + qpack_context_free(&encoder->ctx); +} + +int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size) { + if (encoder->ctx.hard_max_dtable_size < max_dtable_size) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (encoder->ctx.max_dtable_size == max_dtable_size) { + return 0; + } + + encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; + + if (encoder->min_dtable_update > max_dtable_size) { + encoder->min_dtable_update = max_dtable_size; + encoder->ctx.max_dtable_size = max_dtable_size; + } + encoder->last_max_dtable_update = max_dtable_size; + + return 0; +} + +int nghttp3_qpack_encoder_set_hard_max_dtable_size( + nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) { + /* TODO This is not ideal. */ + if (encoder->ctx.hard_max_dtable_size) { + return NGHTTP3_ERR_INVALID_STATE; + } + + encoder->ctx.hard_max_dtable_size = hard_max_dtable_size; + + return 0; +} + +int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, + size_t max_blocked) { + /* TODO This is not ideal. */ + if (encoder->ctx.max_blocked) { + return NGHTTP3_ERR_INVALID_STATE; + } + + encoder->ctx.max_blocked = max_blocked; + + return 0; +} + +size_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { + assert(!nghttp3_pq_empty(&encoder->min_cnts)); + + return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts), + nghttp3_qpack_header_block_ref, min_cnts_pe) + ->min_cnt; +} + +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { + nghttp3_ringbuf *dtable = &encoder->ctx.dtable; + const nghttp3_mem *mem = encoder->ctx.mem; + size_t min_cnt = SIZE_MAX; + size_t len; + nghttp3_qpack_entry *ent; + + if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) { + return; + } + + if (!nghttp3_pq_empty(&encoder->min_cnts)) { + min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); + } + + for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) { + len = nghttp3_ringbuf_len(dtable); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); + if (ent->absidx + 1 == min_cnt) { + return; + } + + encoder->ctx.dtable_size -= + table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(dtable); + qpack_map_remove(&encoder->dtable_map, ent); + + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } +} + +/* + * qpack_encoder_add_stream_ref adds another dynamic table reference + * to a stream denoted by |stream_id|. |max_cnt| and |min_cnt| is the + * maximum and minimum insert count it references respectively. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, + int64_t stream_id, size_t max_cnt, + size_t min_cnt) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + nghttp3_qpack_header_block_ref *ref; + const nghttp3_mem *mem = encoder->ctx.mem; + size_t prev_max_cnt = 0; + int rv; + + if (stream == NULL) { + rv = nghttp3_qpack_stream_new(&stream, stream_id, mem); + if (rv != 0) { + assert(rv == NGHTTP3_ERR_NOMEM); + return rv; + } + rv = nghttp3_map_insert(&encoder->streams, &stream->me); + if (rv != 0) { + assert(rv == NGHTTP3_ERR_NOMEM); + nghttp3_qpack_stream_del(stream, mem); + return rv; + } + } else { + prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream); + if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) && + max_cnt > prev_max_cnt) { + nghttp3_qpack_encoder_unblock_stream(encoder, stream); + } + } + + rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_qpack_stream_add_ref(stream, ref); + if (rv != 0) { + nghttp3_qpack_header_block_ref_del(ref, mem); + return rv; + } + + if (max_cnt > prev_max_cnt && + nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { + rv = nghttp3_qpack_encoder_block_stream(encoder, stream); + if (rv != 0) { + return rv; + } + } + + return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe); +} + +static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + size_t i, len; + nghttp3_qpack_header_block_ref *ref; + + nghttp3_map_remove(&encoder->streams, stream->me.key); + + len = nghttp3_ringbuf_len(&stream->refs); + for (i = 0; i < len; ++i) { + ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, + i); + + assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); + } +} + +/* + * reserve_buf_internal ensures that |buf| contains at least + * |extra_size| of free space. In other words, if this function + * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is + * the minimum size of buffer. The allocated buffer has at least + * |min_size| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size, + size_t min_size, const nghttp3_mem *mem) { + size_t left = nghttp3_buf_left(buf); + size_t n = min_size, need; + + if (left >= extra_size) { + return 0; + } + + need = nghttp3_buf_cap(buf) + extra_size - left; + + for (; n < need; n *= 2) + ; + + return nghttp3_buf_reserve(buf, n, mem); +} + +static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size, + const nghttp3_mem *mem) { + return reserve_buf_internal(buf, extra_size, 32, mem); +} + +static int reserve_buf(nghttp3_buf *buf, size_t extra_size, + const nghttp3_mem *mem) { + return reserve_buf_internal(buf, extra_size, 32, mem); +} + +int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, + nghttp3_buf *pbuf, nghttp3_buf *rbuf, + nghttp3_buf *ebuf, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + size_t i, base, max_cnt = 0, min_cnt = SIZE_MAX; + int rv = 0; + int allow_blocking; + int blocked_stream; + nghttp3_qpack_stream *stream; + + if (encoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf); + if (rv != 0) { + goto fail; + } + + base = encoder->ctx.next_absidx; + + stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); + blocked_stream = + stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); + allow_blocking = + blocked_stream || + encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams); + + DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, + blocked_stream, allow_blocking); + + for (i = 0; i < nvlen; ++i) { + rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf, + ebuf, &nva[i], base, allow_blocking); + if (rv != 0) { + goto fail; + } + } + + nghttp3_qpack_encoder_write_header_block_prefix(encoder, pbuf, max_cnt, base); + + /* TODO If max_cnt == 0, no reference is made to dtable. */ + if (!max_cnt) { + return 0; + } + + rv = qpack_encoder_add_stream_ref(encoder, stream_id, max_cnt, min_cnt); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + encoder->ctx.bad = 1; + return rv; +} + +/* + * qpack_write_number writes variable integer to |rbuf|. |num| is an + * integer to write. |prefix| is a prefix of variable integer + * encoding. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num, + size_t prefix, const nghttp3_mem *mem) { + int rv; + size_t len = nghttp3_qpack_put_varint_len(num, prefix); + uint8_t *p; + + rv = reserve_buf(rbuf, len, mem); + if (rv != 0) { + return rv; + } + + p = rbuf->last; + + *p = fb; + p = nghttp3_qpack_put_varint(p, num, prefix); + + assert((size_t)(p - rbuf->last) == len); + + rbuf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf) { + int rv; + + nghttp3_qpack_encoder_shrink_dtable(encoder); + + if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size || + !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) { + return 0; + } + + if (encoder->min_dtable_update < encoder->last_max_dtable_update) { + rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf, + encoder->min_dtable_update); + if (rv != 0) { + return rv; + } + } + + rv = nghttp3_qpack_encoder_write_set_dtable_cap( + encoder, ebuf, encoder->last_max_dtable_update); + if (rv != 0) { + return rv; + } + + encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; + encoder->min_dtable_update = SIZE_MAX; + encoder->ctx.max_dtable_size = encoder->last_max_dtable_update; + + return 0; +} + +int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t cap) { + DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap); + return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem); +} + +nghttp3_qpack_stream * +nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_map_entry *me = + nghttp3_map_find(&encoder->streams, (uint64_t)stream_id); + return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me); +} + +int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream); +} + +static uint32_t qpack_hash_name(const nghttp3_nv *nv) { + /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ + uint32_t h = 2166136261u; + size_t i; + + for (i = 0; i < nv->namelen; ++i) { + h ^= nv->name[i]; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h; +} + +/* + * qpack_encoder_decide_indexing_mode determines and returns indexing + * mode for header field |nv|. |token| is a token of header field + * name. + */ +static nghttp3_qpack_indexing_mode +qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, int32_t token) { + if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) { + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + } + + switch (token) { + case NGHTTP3_QPACK_TOKEN_AUTHORIZATION: + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + case NGHTTP3_QPACK_TOKEN_COOKIE: + if (nv->valuelen < 20) { + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + } + break; + case NGHTTP3_QPACK_TOKEN__PATH: + case NGHTTP3_QPACK_TOKEN_AGE: + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: + case NGHTTP3_QPACK_TOKEN_ETAG: + case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE: + case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH: + case NGHTTP3_QPACK_TOKEN_LOCATION: + case NGHTTP3_QPACK_TOKEN_SET_COOKIE: + return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; + } + + if (table_space(nv->namelen, nv->valuelen) > + encoder->ctx.max_dtable_size * 3 / 4) { + return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; + } + + return NGHTTP3_QPACK_INDEXING_MODE_STORE; +} + +/* + * qpack_encoder_can_index returns nonzero if an entry which occupies + * |need| bytes can be inserted into dynamic table. |min_cnt| is the + * minimum insert count which blocked stream requires. + */ +static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, + size_t min_cnt) { + size_t avail = 0; + size_t len; + size_t gmin_cnt; + nghttp3_qpack_entry *min_ent, *last_ent; + nghttp3_ringbuf *dtable = &encoder->ctx.dtable; + + if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) { + avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size; + if (need <= avail) { + return 1; + } + } + + if (!nghttp3_pq_empty(&encoder->min_cnts)) { + gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); + min_cnt = nghttp3_min(min_cnt, gmin_cnt); + } + + if (min_cnt == SIZE_MAX) { + return encoder->ctx.max_dtable_size >= need; + } + + min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); + + len = nghttp3_ringbuf_len(&encoder->ctx.dtable); + assert(len); + last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); + + if (min_ent == last_ent) { + return 0; + } + + return avail + min_ent->sum - last_ent->sum >= need; +} + +/* + * qpack_encoder_can_index_nv returns nonzero if header field |nv| can + * be inserted into dynamic table. |min_cnt| is the minimum insert + * count which blocked stream requires. + */ +static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, size_t min_cnt) { + return qpack_encoder_can_index( + encoder, table_space(nv->namelen, nv->valuelen), min_cnt); +} + +/* + * qpack_encoder_can_index_duplicate returns nonzero if an entry at + * |absidx| in dynamic table can be inserted to dynamic table as + * duplicate. |min_cnt| is the minimum insert count which blocked + * stream requires. + */ +static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, + size_t absidx, size_t min_cnt) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + return qpack_encoder_can_index( + encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt); +} + +/* + * qpack_context_check_draining returns nonzero if an entry at + * |absidx| in dynamic table is one of draining entries. + */ +static int qpack_context_check_draining(nghttp3_qpack_context *ctx, + size_t absidx) { + const size_t safe = + ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8); + nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); + + return ctx->dtable_sum - ent->sum > safe; +} + +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, + size_t *pmax_cnt, size_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, size_t base, + int allow_blocking) { + uint32_t hash = 0; + int32_t token; + nghttp3_qpack_indexing_mode indexing_mode; + nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1}; + nghttp3_qpack_entry *new_ent = NULL; + int static_entry; + int just_index = 0; + int rv; + + token = qpack_lookup_token(nv->name, nv->namelen); + static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable); + if (static_entry) { + hash = token_stable[token].hash; + } else { + hash = qpack_hash_name(nv); + } + + indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token); + + if (static_entry) { + sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode); + if (sres.index != -1 && sres.name_value_match) { + return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf, + (size_t)sres.index); + } + } + + if (nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) { + dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash, + indexing_mode, encoder->krcnt, + allow_blocking); + just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE && + dres.pb_index == -1; + } + + if (dres.index != -1 && dres.name_value_match) { + if (allow_blocking && + qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) && + qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index, + *pmin_cnt)) { + rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf, + (size_t)dres.index); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder, + (size_t)dres.index); + if (rv != 0) { + return rv; + } + + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + dres.index = (nghttp3_ssize)new_ent->absidx; + } + *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, (size_t)dres.index, base); + } + + if (sres.index != -1) { + if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { + rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf, + (size_t)sres.index, nv); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index, + nv, hash); + if (rv != 0) { + return rv; + } + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, new_ent->absidx, base); + } + } + + return nghttp3_qpack_encoder_write_static_indexed_name( + encoder, rbuf, (size_t)sres.index, nv); + } + + if (dres.index != -1) { + if (just_index && + qpack_encoder_can_index_nv( + encoder, nv, + allow_blocking ? *pmin_cnt + : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) { + rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf, + (size_t)dres.index, nv); + if (rv != 0) { + return rv; + } + + if (!allow_blocking) { + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1); + } + + rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index, + nv, hash); + if (rv != 0) { + return rv; + } + + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, new_ent->absidx, base); + } + } + + *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); + + return nghttp3_qpack_encoder_write_dynamic_indexed_name( + encoder, rbuf, (size_t)dres.index, base, nv); + } + + if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { + rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv); + if (rv != 0) { + return rv; + } + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf, + new_ent->absidx, base); + } + } + + return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv); +} + +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, + nghttp3_qpack_indexing_mode indexing_mode) { + nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx, + 0, -1}; + nghttp3_qpack_static_entry *ent; + nghttp3_qpack_static_header *hdr; + size_t i; + + assert(token >= 0); + + if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) { + return res; + } + + for (i = (size_t)token; + i < nghttp3_arraylen(token_stable) && token_stable[i].token == token; + ++i) { + ent = &token_stable[i]; + hdr = &stable[ent->absidx]; + if (hdr->value.len == nv->valuelen && + memeq(hdr->value.base, nv->value, nv->valuelen)) { + res.index = (nghttp3_ssize)ent->absidx; + res.name_value_match = 1; + return res; + } + } + return res; +} + +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( + nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, + uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, size_t krcnt, + int allow_blocking) { + nghttp3_qpack_lookup_result res = {-1, 0, -1}; + int exact_match = 0; + nghttp3_qpack_entry *match, *pb_match; + + encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token, + hash, krcnt, allow_blocking, + indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER); + if (match) { + res.index = (nghttp3_ssize)match->absidx; + res.name_value_match = exact_match; + } + if (pb_match) { + res.pb_index = (nghttp3_ssize)pb_match->absidx; + } + + return res; +} + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, + size_t max_cnt, size_t min_cnt, + const nghttp3_mem *mem) { + nghttp3_qpack_header_block_ref *ref = + nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref)); + + if (ref == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; + ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; + ref->max_cnt = max_cnt; + ref->min_cnt = min_cnt; + + *pref = ref; + + return 0; +} + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, + const nghttp3_mem *mem) { + nghttp3_mem_free(mem, ref); +} + +static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + const nghttp3_qpack_header_block_ref *lhs = + nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); + const nghttp3_qpack_header_block_ref *rhs = + nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); + + return lhs->max_cnt > rhs->max_cnt; +} + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_stream *stream; + + stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream)); + if (stream == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_ringbuf_init(&stream->refs, 4, + sizeof(nghttp3_qpack_header_block_ref *), mem); + if (rv != 0) { + nghttp3_mem_free(mem, stream); + return rv; + } + + nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); + + stream->me.next = NULL; + stream->me.key = (uint64_t)stream_id; + + *pstream = stream; + + return 0; +} + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, + const nghttp3_mem *mem) { + nghttp3_qpack_header_block_ref *ref; + size_t i, len; + + if (stream == NULL) { + return; + } + + nghttp3_pq_free(&stream->max_cnts); + + len = nghttp3_ringbuf_len(&stream->refs); + for (i = 0; i < len; ++i) { + ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, + i); + nghttp3_qpack_header_block_ref_del(ref, mem); + } + + nghttp3_ringbuf_free(&stream->refs); + + nghttp3_mem_free(mem, stream); +} + +size_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) { + nghttp3_qpack_header_block_ref *ref; + + if (nghttp3_pq_empty(&stream->max_cnts)) { + return 0; + } + + ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe); + return ref->max_cnt; +} + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, + nghttp3_qpack_header_block_ref *ref) { + nghttp3_qpack_header_block_ref **dest; + int rv; + + if (nghttp3_ringbuf_full(&stream->refs)) { + rv = nghttp3_ringbuf_reserve(&stream->refs, + nghttp3_ringbuf_len(&stream->refs) * 2); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(&stream->refs); + *dest = ref; + + return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); +} + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { + nghttp3_qpack_header_block_ref *ref; + + assert(nghttp3_ringbuf_len(&stream->refs)); + + ref = + *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + + assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe); + + nghttp3_ringbuf_pop_front(&stream->refs); +} + +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + size_t absidx) { + DEBUGF("qpack::encode: Indexed Header Field (static) absidx=%zu\n", absidx); + return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + size_t absidx, size_t base) { + DEBUGF("qpack::encode: Indexed Header Field (dynamic) absidx=%zu base=%zu\n", + absidx, base); + + if (absidx < base) { + return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6, + encoder->ctx.mem); + } + + return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem); +} + +/* + * qpack_encoder_write_indexed_name writes generic indexed name. |fb| + * is the first byte. |nameidx| is an index of referenced name. + * |prefix| is a prefix of variable integer encoding. |nv| is a + * header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, + nghttp3_buf *buf, uint8_t fb, + size_t nameidx, size_t prefix, + const nghttp3_nv *nv) { + int rv; + size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix); + uint8_t *p; + size_t hlen; + int h = 0; + + hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); + if (hlen < nv->valuelen) { + h = 1; + len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; + } else { + len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; + } + + rv = reserve_buf(buf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = buf->last; + + *p = fb; + p = nghttp3_qpack_put_varint(p, nameidx, prefix); + + if (h) { + *p = 0x80; + p = nghttp3_qpack_put_varint(p, hlen, 7); + p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); + } else { + *p = 0; + p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); + if (nv->valuelen) { + p = nghttp3_cpymem(p, nv->value, nv->valuelen); + } + } + + assert((size_t)(p - buf->last) == len); + + buf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_static_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, size_t absidx, + const nghttp3_nv *nv) { + uint8_t fb = + (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); + + DEBUGF("qpack::encode: Literal Header Field With Name Reference (static) " + "absidx=%zu never=%d\n", + absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, size_t absidx, + size_t base, const nghttp3_nv *nv) { + uint8_t fb; + + DEBUGF("qpack::encode: Literal Header Field With Name Reference (dynamic) " + "absidx=%zu base=%zu never=%d\n", + absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); + + if (absidx < base) { + fb = (uint8_t)(0x40 | + ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, + base - absidx - 1, 4, nv); + } + + fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0; + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3, + nv); +} + +/* + * qpack_encoder_write_literal writes generic literal header field + * representation. |fb| is a first byte. |prefix| is a prefix of + * variable integer encoding for name length. |nv| is a header field + * to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *buf, uint8_t fb, + size_t prefix, const nghttp3_nv *nv) { + int rv; + size_t len; + uint8_t *p; + size_t nhlen, vhlen; + int nh = 0, vh = 0; + + nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); + if (nhlen < nv->namelen) { + nh = 1; + len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen; + } else { + len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen; + } + + vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); + if (vhlen < nv->valuelen) { + vh = 1; + len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; + } else { + len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; + } + + rv = reserve_buf(buf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = buf->last; + + *p = fb; + if (nh) { + *p |= (uint8_t)(1 << prefix); + p = nghttp3_qpack_put_varint(p, nhlen, prefix); + p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen); + } else { + p = nghttp3_qpack_put_varint(p, nv->namelen, prefix); + if (nv->namelen) { + p = nghttp3_cpymem(p, nv->name, nv->namelen); + } + } + + *p = 0; + + if (vh) { + *p |= 0x80; + p = nghttp3_qpack_put_varint(p, vhlen, 7); + p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); + } else { + p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); + if (nv->valuelen) { + p = nghttp3_cpymem(p, nv->value, nv->valuelen); + } + } + + assert((size_t)(p - buf->last) == len); + + buf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + const nghttp3_nv *nv) { + uint8_t fb = + (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0)); + + DEBUGF("qpack::encode: Literal Header Field Without Name Reference\n"); + return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv); +} + +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%zu\n", + absidx); + return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%zu\n", + absidx); + return qpack_encoder_write_indexed_name( + encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv); +} + +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + size_t absidx) { + size_t idx = encoder->ctx.next_absidx - absidx - 1; + size_t len = nghttp3_qpack_put_varint_len(idx, 5); + uint8_t *p; + int rv; + + DEBUGF("qpack::encode: Insert duplicate absidx=%zu\n", absidx); + + rv = reserve_buf(ebuf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = ebuf->last; + + *p = 0; + p = nghttp3_qpack_put_varint(p, idx, 5); + + assert((size_t)(p - ebuf->last) == len); + + ebuf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert Without Name Reference\n"); + return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv); +} + +int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, + nghttp3_qpack_nv *qnv, + nghttp3_qpack_map *dtable_map, + uint32_t hash) { + nghttp3_qpack_entry *new_ent, **p, *ent; + const nghttp3_mem *mem = ctx->mem; + size_t space; + size_t i; + int rv; + + space = table_space(qnv->name->len, qnv->value->len); + + assert(space <= ctx->max_dtable_size); + + while (ctx->dtable_size + space > ctx->max_dtable_size) { + i = nghttp3_ringbuf_len(&ctx->dtable); + assert(i); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); + + ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(&ctx->dtable); + if (dtable_map) { + qpack_map_remove(dtable_map, ent); + } + + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } + + new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry)); + if (new_ent == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++, + hash); + + if (nghttp3_ringbuf_full(&ctx->dtable)) { + rv = nghttp3_ringbuf_reserve(&ctx->dtable, + nghttp3_ringbuf_len(&ctx->dtable) * 2); + if (rv != 0) { + goto fail; + } + } + + p = nghttp3_ringbuf_push_front(&ctx->dtable); + *p = new_ent; + + if (dtable_map) { + qpack_map_insert(dtable_map, new_ent); + } + + ctx->dtable_size += space; + ctx->dtable_sum += space; + + return 0; + +fail: + nghttp3_qpack_entry_free(new_ent); + nghttp3_mem_free(mem, new_ent); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, + size_t absidx, const nghttp3_nv *nv, + uint32_t hash) { + const nghttp3_qpack_static_header *shd; + nghttp3_qpack_nv qnv; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + return rv; + } + + assert(nghttp3_arraylen(stable) > absidx); + + shd = &stable[absidx]; + + qnv.name = (nghttp3_rcbuf *)&shd->name; + qnv.token = shd->token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, + size_t absidx, + const nghttp3_nv *nv, + uint32_t hash) { + nghttp3_qpack_nv qnv; + nghttp3_qpack_entry *ent; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + return rv; + } + + ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + qnv.name = ent->nv.name; + qnv.token = ent->nv.token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(qnv.name); + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, + size_t absidx) { + nghttp3_qpack_nv qnv; + nghttp3_qpack_entry *ent; + int rv; + + ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + qnv = ent->nv; + nghttp3_rcbuf_incref(qnv.name); + nghttp3_rcbuf_incref(qnv.value); + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, ent->hash); + + nghttp3_rcbuf_decref(qnv.name); + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, + int32_t token, uint32_t hash) { + nghttp3_qpack_nv qnv; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + nghttp3_rcbuf_decref(qnv.name); + return rv; + } + + qnv.token = token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, size_t absidx) { + size_t relidx; + + assert(ctx->next_absidx > absidx); + + relidx = ctx->next_absidx - absidx - 1; + + return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx); +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) { + assert(nghttp3_ringbuf_len(&ctx->dtable)); + return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0); +} + +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, + size_t sum, size_t absidx, uint32_t hash) { + ent->nv = *qnv; + ent->map_next = NULL; + ent->sum = sum; + ent->absidx = absidx; + ent->hash = hash; + + nghttp3_rcbuf_incref(ent->nv.name); + nghttp3_rcbuf_incref(ent->nv.value); +} + +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { + nghttp3_rcbuf_decref(ent->nv.value); + nghttp3_rcbuf_decref(ent->nv.name); +} + +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + nghttp3_blocked_streams_key bsk = { + nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + stream->me.key}; + nghttp3_ksl_key key; + + return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, + nghttp3_ksl_key_ptr(&key, &bsk), stream); +} + +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + nghttp3_blocked_streams_key bsk = { + nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + stream->me.key}; + nghttp3_ksl_key key; + nghttp3_ksl_it it; + + /* This is purely debugging purpose only */ + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, + nghttp3_ksl_key_ptr(&key, &bsk)); + + assert(!nghttp3_ksl_it_end(&it)); + assert(nghttp3_ksl_it_get(&it) == stream); + + nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &key); +} + +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, + size_t max_cnt) { + nghttp3_blocked_streams_key bsk = {max_cnt, 0}; + nghttp3_ksl_key key; + nghttp3_ksl_it it; + + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, + nghttp3_ksl_key_ptr(&key, &bsk)); + + for (; !nghttp3_ksl_it_end(&it);) { + bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it).ptr; + nghttp3_ksl_remove(&encoder->blocked_streams, &it, + nghttp3_ksl_key_ptr(&key, &bsk)); + } +} + +void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + const nghttp3_mem *mem = encoder->ctx.mem; + nghttp3_qpack_header_block_ref *ref; + + if (stream == NULL) { + /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we + don't create stream which does not use dynamic table. */ + return; + } + + assert(nghttp3_ringbuf_len(&stream->refs)); + + ref = + *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + + DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%zu " + "krcnt=%zu\n", + stream_id, ref->max_cnt, encoder->krcnt); + + if (encoder->krcnt < ref->max_cnt) { + encoder->krcnt = ref->max_cnt; + + nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt); + } + + nghttp3_qpack_stream_pop_ref(stream); + + assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); + + nghttp3_qpack_header_block_ref_del(ref, mem); + + if (nghttp3_ringbuf_len(&stream->refs)) { + return; + } + + qpack_encoder_remove_stream(encoder, stream); + + nghttp3_qpack_stream_del(stream, mem); +} + +int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, + size_t n) { + if (encoder->ctx.next_absidx < encoder->krcnt + n) { + return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; + } + encoder->krcnt += n; + + nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt); + + return 0; +} + +void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) { + encoder->krcnt = encoder->ctx.next_absidx; + + nghttp3_ksl_clear(&encoder->blocked_streams); + nghttp3_pq_clear(&encoder->min_cnts); + nghttp3_map_each_free(&encoder->streams, map_stream_free, + (void *)encoder->ctx.mem); +} + +void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + const nghttp3_mem *mem = encoder->ctx.mem; + + if (stream == NULL) { + return; + } + + if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { + nghttp3_qpack_encoder_unblock_stream(encoder, stream); + } + + qpack_encoder_remove_stream(encoder, stream); + + nghttp3_qpack_stream_del(stream, mem); +} + +size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) { + return nghttp3_ksl_len(&encoder->blocked_streams); +} + +int nghttp3_qpack_encoder_write_header_block_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, size_t ricnt, + size_t base) { + size_t max_ents = + encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + size_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; + int sign = base < ricnt; + size_t delta_base = sign ? ricnt - base - 1 : base - ricnt; + size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) + + nghttp3_qpack_put_varint_len(delta_base, 7); + uint8_t *p; + int rv; + + DEBUGF("qpack::encode: ricnt=%zu base=%zu icnt=%zu\n", ricnt, base, + encoder->ctx.next_absidx); + + rv = reserve_buf(pbuf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = pbuf->last; + + p = nghttp3_qpack_put_varint(p, encricnt, 8); + if (sign) { + *p = 0x80; + } else { + *p = 0; + } + p = nghttp3_qpack_put_varint(p, delta_base, 7); + + assert((size_t)(p - pbuf->last) == len); + + pbuf->last = p; + + return 0; +} + +/* + * qpack_read_varint reads |rstate->prefix| prefixed integer stored + * from |begin|. The |end| represents the 1 beyond the last of the + * valid contiguous memory region from |begin|. The decoded integer + * must be less than or equal to NGHTTP3_QPACK_INT_MAX. + * + * If the |rstate->left| is nonzero, it is used as an initial value, + * and this function assumes the |begin| starts with intermediate + * data. |rstate->shift| is used as initial integer shift. + * + * If an entire integer is decoded successfully, the |*fin| is set to + * nonzero. + * + * This function stores the decoded integer in |rstate->left| if it + * succeeds, including partial decoding (in this case, number of shift + * to make in the next call will be stored in |rstate->shift|) and + * returns number of bytes processed, or returns negative error code + * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error. + */ +static nghttp3_ssize qpack_read_varint(int *fin, + nghttp3_qpack_read_state *rstate, + const uint8_t *begin, + const uint8_t *end) { + uint64_t k = (uint8_t)((1 << rstate->prefix) - 1); + uint64_t n = rstate->left; + uint64_t add; + const uint8_t *p = begin; + size_t shift = rstate->shift; + + rstate->shift = 0; + *fin = 0; + + if (n == 0) { + if (((*p) & k) != k) { + rstate->left = (*p) & k; + *fin = 1; + return 1; + } + + n = k; + + if (++p == end) { + rstate->left = n; + return (nghttp3_ssize)(p - begin); + } + } + + for (; p != end; ++p, shift += 7) { + add = (*p) & 0x7f; + + if (shift > 62) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + add <<= shift; + + if (NGHTTP3_QPACK_INT_MAX - add < n) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + n += add; + + if (((*p) & (1 << 7)) == 0) { + break; + } + } + + rstate->shift = shift; + + if (p == end) { + rstate->left = n; + return (nghttp3_ssize)(p - begin); + } + + rstate->left = n; + *fin = 1; + return (nghttp3_ssize)(p + 1 - begin); +} + +nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, + const uint8_t *src, + size_t srclen) { + const uint8_t *p = src, *end; + int rv; + nghttp3_ssize nread; + int rfin; + + if (encoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if (srclen == 0) { + return 0; + } + + end = src + srclen; + + for (; p != end;) { + switch (encoder->state) { + case NGHTTP3_QPACK_DS_STATE_OPCODE: + if ((*p) & 0x80) { + DEBUGF("qpack::encode: OPCODE_HEADER_ACK\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_HEADER_ACK; + encoder->rstate.prefix = 7; + } else if ((*p) & 0x40) { + DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL; + encoder->rstate.prefix = 6; + } else { + DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT; + encoder->rstate.prefix = 6; + } + encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER; + /* fall through */ + case NGHTTP3_QPACK_DS_STATE_READ_NUMBER: + nread = qpack_read_varint(&rfin, &encoder->rstate, p, end); + if (nread < 0) { + assert(nread == NGHTTP3_ERR_QPACK_FATAL); + rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + switch (encoder->opcode) { + case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT: + rv = nghttp3_qpack_encoder_add_insert_count(encoder, + encoder->rstate.left); + if (rv != 0) { + goto fail; + } + break; + case NGHTTP3_QPACK_DS_OPCODE_HEADER_ACK: + nghttp3_qpack_encoder_ack_header(encoder, + (int64_t)encoder->rstate.left); + break; + case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: + nghttp3_qpack_encoder_cancel_stream(encoder, + (int64_t)encoder->rstate.left); + break; + default: + /* unreachable */ + assert(0); + break; + } + + encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&encoder->rstate); + break; + default: + /* unreachable */ + assert(0); + break; + } + } + + return p - src; + +fail: + encoder->ctx.bad = 1; + return rv; +} + +size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + + *buf = (uint8_t)(*buf & ~k); + + if (n < k) { + *buf = (uint8_t)(*buf | n); + return buf + 1; + } + + *buf = (uint8_t)(*buf | k); + ++buf; + + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); + } + + *buf++ = (uint8_t)n; + + return buf; +} + +void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) { + nghttp3_rcbuf_decref(rstate->value); + nghttp3_rcbuf_decref(rstate->name); +} + +void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { + rstate->name = NULL; + rstate->value = NULL; + nghttp3_buf_init(&rstate->namebuf); + nghttp3_buf_init(&rstate->valuebuf); + rstate->left = 0; + rstate->prefix = 0; + rstate->shift = 0; + rstate->absidx = 0; + rstate->never = 0; + rstate->dynamic = 0; + rstate->huffman_encoded = 0; +} + +int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + + rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + decoder->opcode = 0; + decoder->written_icnt = 0; + decoder->max_concurrent_streams = 0; + + nghttp3_qpack_read_state_reset(&decoder->rstate); + nghttp3_buf_init(&decoder->dbuf); + + return 0; +} + +void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { + nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem); + nghttp3_qpack_read_state_free(&decoder->rstate); + qpack_context_free(&decoder->ctx); +} + +/* + * qpack_read_huffman_string decodes huffman string in buffer [begin, + * end) and writes the decoded string to |dest|. This function + * assumes the buffer pointed by |dest| has enough space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * Could not decode huffman string. + */ +static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate, + nghttp3_buf *dest, + const uint8_t *begin, + const uint8_t *end) { + nghttp3_ssize nwrite; + size_t len = (size_t)(end - begin); + int fin = 0; + + if (len >= rstate->left) { + len = rstate->left; + fin = 1; + } + + nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin, + len, fin); + if (nwrite < 0) { + return nwrite; + } + + if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + dest->last += nwrite; + rstate->left -= len; + return (nghttp3_ssize)len; +} + +static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, + nghttp3_buf *dest, const uint8_t *begin, + const uint8_t *end) { + size_t len = (size_t)(end - begin); + size_t n = nghttp3_min(len, rstate->left); + + dest->last = nghttp3_cpymem(dest->last, begin, n); + + rstate->left -= n; + return (nghttp3_ssize)n; +} + +/* + * qpack_decoder_validate_index checks rstate->absidx is acceptable. + * + * It returns 0 if it suceeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * rstate->absidx is invalid. + */ +static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate) { + if (rstate->dynamic) { + return rstate->absidx < decoder->ctx.next_absidx && + decoder->ctx.next_absidx - rstate->absidx - 1 < + nghttp3_ringbuf_len(&decoder->ctx.dtable) + ? 0 + : NGHTTP3_ERR_QPACK_FATAL; + } + return rstate->absidx < nghttp3_arraylen(stable) ? 0 + : NGHTTP3_ERR_QPACK_FATAL; +} + +static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate, + const uint8_t b) { + rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0; +} + +static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) { + *rstate->namebuf.last = '\0'; + rstate->name->len = nghttp3_buf_len(&rstate->namebuf); +} + +static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) { + *rstate->valuebuf.last = '\0'; + rstate->value->len = nghttp3_buf_len(&rstate->valuebuf); +} + +nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, + const uint8_t *src, + size_t srclen) { + const uint8_t *p = src, *end; + int rv; + int busy = 0; + const nghttp3_mem *mem = decoder->ctx.mem; + nghttp3_ssize nread; + int rfin; + + if (decoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if (srclen == 0) { + return 0; + } + + end = src + srclen; + + for (; p != end || busy;) { + busy = 0; + switch (decoder->state) { + case NGHTTP3_QPACK_ES_STATE_OPCODE: + if ((*p) & 0x80) { + DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED; + decoder->rstate.dynamic = !((*p) & 0x40); + decoder->rstate.prefix = 6; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else if ((*p) & 0x40) { + DEBUGF("qpack::decode: OPCODE_INSERT\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT; + decoder->rstate.dynamic = 0; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN; + } else if ((*p) & 0x20) { + DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else if (!((*p) & 0x20)) { + DEBUGF("qpack::decode: OPCODE_DUPLICATE\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE; + decoder->rstate.dynamic = 1; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else { + DEBUGF("qpack::decode: unknown opcode %02x\n", *p); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + break; + case NGHTTP3_QPACK_ES_STATE_READ_INDEX: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) { + if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) { + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + DEBUGF("qpack::decode: Set dtable capacity to %zu\n", + decoder->rstate.left); + nghttp3_qpack_decoder_set_dtable_cap(decoder, decoder->rstate.left); + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + + rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate); + if (rv < 0) { + goto fail; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_DUPLICATE) { + rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder); + if (rv != 0) { + goto fail; + } + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED) { + decoder->rstate.prefix = 7; + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + break; + } + + /* Unreachable */ + assert(0); + break; + case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN: + qpack_read_state_check_huffman(&decoder->rstate, *p); + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN; + decoder->rstate.left = 0; + decoder->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->rstate.huffman_encoded) { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&decoder->rstate.name, + decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&decoder->rstate.name, decoder->rstate.left + 1, + mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&decoder->rstate.namebuf, + decoder->rstate.name->base, + decoder->rstate.name->len); + break; + case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN: + nread = qpack_read_huffman_string(&decoder->rstate, + &decoder->rstate.namebuf, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_name(&decoder->rstate); + + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + decoder->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_ES_STATE_READ_NAME: + nread = + qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_name(&decoder->rstate); + + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + break; + case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN: + qpack_read_state_check_huffman(&decoder->rstate, *p); + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN; + decoder->rstate.left = 0; + decoder->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->rstate.huffman_encoded) { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&decoder->rstate.value, + decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&decoder->rstate.value, decoder->rstate.left + 1, + mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&decoder->rstate.valuebuf, + decoder->rstate.value->base, + decoder->rstate.value->len); + + /* value might be 0 length */ + busy = 1; + break; + case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN: + nread = qpack_read_huffman_string(&decoder->rstate, + &decoder->rstate.valuebuf, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_value(&decoder->rstate); + + switch (decoder->opcode) { + case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: + rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); + break; + case NGHTTP3_QPACK_ES_OPCODE_INSERT: + rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); + break; + default: + /* Unreachable */ + assert(0); + } + if (rv != 0) { + goto fail; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + case NGHTTP3_QPACK_ES_STATE_READ_VALUE: + nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p, + end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_value(&decoder->rstate); + + switch (decoder->opcode) { + case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: + rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); + break; + case NGHTTP3_QPACK_ES_OPCODE_INSERT: + rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); + break; + default: + /* Unreachable */ + assert(0); + } + if (rv != 0) { + goto fail; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + } + + return p - src; + +fail: + decoder->ctx.bad = 1; + return rv; +} + +void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, + size_t cap) { + nghttp3_qpack_entry *ent; + size_t i; + nghttp3_qpack_context *ctx = &decoder->ctx; + const nghttp3_mem *mem = ctx->mem; + + ctx->max_dtable_size = cap; + + while (ctx->dtable_size > cap) { + i = nghttp3_ringbuf_len(&ctx->dtable); + assert(i); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); + + ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(&ctx->dtable); + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } +} + +int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { + DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%zu: " + "value=%*s\n", + decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx, + (int)decoder->rstate.value->len, decoder->rstate.value->base); + + if (decoder->rstate.dynamic) { + return nghttp3_qpack_decoder_dtable_dynamic_add(decoder); + } + + return nghttp3_qpack_decoder_dtable_static_add(decoder); +} + +int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + const nghttp3_qpack_static_header *shd; + + shd = &stable[decoder->rstate.absidx]; + + if (table_space(shd->name.len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = (nghttp3_rcbuf *)&shd->name; + qnv.value = decoder->rstate.value; + qnv.token = shd->token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + nghttp3_qpack_entry *ent; + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); + + if (table_space(ent->nv.name->len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = ent->nv.name; + qnv.value = decoder->rstate.value; + qnv.token = ent->nv.token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(qnv.name); + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { + int rv; + nghttp3_qpack_entry *ent; + nghttp3_qpack_nv qnv; + + DEBUGF("qpack::decode: Insert duplicate absidx=%zu\n", + decoder->rstate.absidx); + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); + + if (table_space(ent->nv.name->len, ent->nv.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv = ent->nv; + nghttp3_rcbuf_incref(qnv.name); + nghttp3_rcbuf_incref(qnv.value); + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + + DEBUGF("qpack::decode: Insert Without Name Reference: name=%*s value=%*s\n", + (int)decoder->rstate.name->len, decoder->rstate.name->base, + (int)decoder->rstate.value->len, decoder->rstate.value->base); + + if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = decoder->rstate.name; + qnv.value = decoder->rstate.value; + qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len); + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +void nghttp3_qpack_decoder_set_max_concurrent_streams( + nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) { + decoder->max_concurrent_streams = + nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams); +} + +void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, + int64_t stream_id, + const nghttp3_mem *mem) { + nghttp3_qpack_read_state_reset(&sctx->rstate); + + sctx->mem = mem; + sctx->rstate.prefix = 8; + sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT; + sctx->opcode = 0; + sctx->stream_id = stream_id; + sctx->ricnt = 0; + sctx->dbase_sign = 0; + sctx->base = 0; +} + +void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state_free(&sctx->rstate); +} + +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem); +} + +size_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) { + return sctx->ricnt; +} + +nghttp3_ssize +nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv, uint8_t *pflags, + const uint8_t *src, size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + int busy = 0; + nghttp3_ssize nread; + int rfin; + const nghttp3_mem *mem = decoder->ctx.mem; + + if (decoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE; + + for (; p != end || busy;) { + busy = 0; + switch (sctx->state) { + case NGHTTP3_QPACK_RS_STATE_RICNT: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt, + sctx->rstate.left); + if (rv != 0) { + goto fail; + } + + sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN; + break; + case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN: + if ((*p) & 0x80) { + sctx->dbase_sign = 1; + } + sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE; + sctx->rstate.left = 0; + sctx->rstate.prefix = 7; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_DBASE: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (sctx->dbase_sign) { + if (sctx->ricnt < sctx->rstate.left) { + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + sctx->base = sctx->ricnt - sctx->rstate.left - 1; + } else { + sctx->base = sctx->ricnt + sctx->rstate.left; + } + + DEBUGF("qpack::decode: ricnt=%zu base=%zu icnt=%zu\n", sctx->ricnt, + sctx->base, decoder->ctx.next_absidx); + + if (sctx->ricnt > decoder->ctx.next_absidx) { + DEBUGF("qpack::decode: stream blocked\n"); + sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED; + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; + return p - src; + } + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + break; + case NGHTTP3_QPACK_RS_STATE_OPCODE: + assert(sctx->rstate.left == 0); + assert(sctx->rstate.shift == 0); + if ((*p) & 0x80) { + DEBUGF("qpack::decode: OPCODE_INDEXED\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED; + sctx->rstate.dynamic = !((*p) & 0x40); + sctx->rstate.prefix = 6; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else if ((*p) & 0x40) { + DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME; + sctx->rstate.never = (*p) & 0x20; + sctx->rstate.dynamic = !((*p) & 0x10); + sctx->rstate.prefix = 4; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else if ((*p) & 0x20) { + DEBUGF("qpack::decode: OPCODE_LITERAL\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL; + sctx->rstate.never = (*p) & 0x10; + sctx->rstate.dynamic = 0; + sctx->rstate.prefix = 3; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN; + } else if ((*p) & 0x10) { + DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB; + sctx->rstate.dynamic = 1; + sctx->rstate.prefix = 4; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else { + DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB; + sctx->rstate.never = (*p) & 0x08; + sctx->rstate.dynamic = 1; + sctx->rstate.prefix = 3; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } + break; + case NGHTTP3_QPACK_RS_STATE_READ_INDEX: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED: + rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB: + rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + sctx->rstate.prefix = 7; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + break; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + sctx->rstate.prefix = 7; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + break; + default: + /* Unreachable */ + assert(0); + } + break; + case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN: + qpack_read_state_check_huffman(&sctx->rstate, *p); + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (sctx->rstate.huffman_encoded) { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&sctx->rstate.name, sctx->rstate.left * 2 + 1, + mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&sctx->rstate.name, sctx->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base, + sctx->rstate.name->len); + break; + case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN: + nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p, + end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_name(&sctx->rstate); + + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + sctx->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_RS_STATE_READ_NAME: + nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_name(&sctx->rstate); + + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + sctx->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN: + qpack_read_state_check_huffman(&sctx->rstate, *p); + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (sctx->rstate.huffman_encoded) { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&sctx->rstate.value, sctx->rstate.left * 2 + 1, + mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&sctx->rstate.value, sctx->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base, + sctx->rstate.value->len); + + /* value might be 0 length */ + busy = 1; + break; + case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN: + nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf, + p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_value(&sctx->rstate); + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + break; + case NGHTTP3_QPACK_RS_OPCODE_LITERAL: + nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); + break; + default: + /* Unreachable */ + assert(0); + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_STATE_READ_VALUE: + nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_value(&sctx->rstate); + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + break; + case NGHTTP3_QPACK_RS_OPCODE_LITERAL: + nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); + break; + default: + /* Unreachable */ + assert(0); + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_STATE_BLOCKED: + if (sctx->ricnt > decoder->ctx.next_absidx) { + DEBUGF("qpack::decode: stream still blocked\n"); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; + return p - src; + } + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + break; + } + } + +almost_ok: + if (fin) { + if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) { + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL; + + if (sctx->ricnt) { + rv = nghttp3_qpack_decoder_write_header_ack(decoder, sctx); + if (rv != 0) { + goto fail; + } + } + } + + return p - src; + +fail: + decoder->ctx.bad = 1; + return rv; +} + +static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) { + size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100); + /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */ + return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10; +} + +int nghttp3_qpack_decoder_write_header_ack( + nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) { + nghttp3_buf *dbuf = &decoder->dbuf; + uint8_t *p; + int rv; + + if (qpack_decoder_dbuf_overflow(decoder)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = reserve_buf_small( + dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7), + decoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = dbuf->last; + *p = 0x80; + dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7); + + if (decoder->written_icnt < sctx->ricnt) { + decoder->written_icnt = sctx->ricnt; + } + + return 0; +} + +size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) { + size_t n; + size_t len = 0; + + if (decoder->written_icnt < decoder->ctx.next_absidx) { + n = decoder->ctx.next_absidx - decoder->written_icnt; + len = nghttp3_qpack_put_varint_len(n, 6); + } + + return nghttp3_buf_len(&decoder->dbuf) + len; +} + +void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, + nghttp3_buf *dbuf) { + uint8_t *p; + size_t n = 0; + size_t len = 0; + + if (decoder->written_icnt < decoder->ctx.next_absidx) { + n = decoder->ctx.next_absidx - decoder->written_icnt; + len = nghttp3_qpack_put_varint_len(n, 6); + } + + assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len); + + if (nghttp3_buf_len(&decoder->dbuf)) { + dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos, + nghttp3_buf_len(&decoder->dbuf)); + } + + if (n) { + p = dbuf->last; + *p = 0; + dbuf->last = nghttp3_qpack_put_varint(p, n, 6); + + decoder->written_icnt = decoder->ctx.next_absidx; + } + + nghttp3_buf_reset(&decoder->dbuf); +} + +int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, + int64_t stream_id) { + uint8_t *p; + int rv; + + if (qpack_decoder_dbuf_overflow(decoder)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = reserve_buf(&decoder->dbuf, + nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6), + decoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = decoder->dbuf.last; + *p = 0x40; + decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6); + + return 0; +} + +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, + size_t *dest, size_t encricnt) { + uint64_t max_ents, full, max, max_wrapped, ricnt; + + if (encricnt == 0) { + *dest = 0; + return 0; + } + + max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + full = 2 * max_ents; + + if (encricnt > full) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + max = decoder->ctx.next_absidx + max_ents; + max_wrapped = max / full * full; + ricnt = max_wrapped + encricnt - 1; + + if (ricnt > max) { + if (ricnt <= full) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + ricnt -= full; + } + + if (ricnt == 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + *dest = ricnt; + + return 0; +} + +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate) { + DEBUGF("qpack::decode: dynamic=%d relidx=%zu icnt=%zu\n", rstate->dynamic, + rstate->left, decoder->ctx.next_absidx); + + if (rstate->dynamic) { + if (decoder->ctx.next_absidx < rstate->left + 1) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1; + } else { + rstate->absidx = rstate->left; + } + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + return 0; +} + +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state *rstate = &sctx->rstate; + + DEBUGF("qpack::decode: dynamic=%d relidx=%zu base=%zu icnt=%zu\n", + rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx); + + if (rstate->dynamic) { + if (sctx->base < rstate->left + 1) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + rstate->absidx = sctx->base - rstate->left - 1; + + if (rstate->absidx >= sctx->ricnt) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + } else { + rstate->absidx = rstate->left; + } + + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + return 0; +} + +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state *rstate = &sctx->rstate; + + DEBUGF("qpack::decode: pbidx=%zu base=%zu icnt=%zu\n", rstate->left, + sctx->base, decoder->ctx.next_absidx); + + assert(rstate->dynamic); + + rstate->absidx = rstate->left + sctx->base; + + if (rstate->absidx >= sctx->ricnt) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + return 0; +} + +static void +qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; + (void)decoder; + + nv->name = (nghttp3_rcbuf *)&shd->name; + nv->value = (nghttp3_rcbuf *)&shd->value; + nv->token = shd->token; + nv->flags = NGHTTP3_NV_FLAG_NONE; +} + +static void +qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + + *nv = ent->nv; + + nghttp3_rcbuf_incref(nv->name); + nghttp3_rcbuf_incref(nv->value); +} + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + DEBUGF("qpack::decode: Indexed (%s) absidx=%zu\n", + sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx); + + if (sctx->rstate.dynamic) { + qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv); + } else { + qpack_decoder_emit_static_indexed(decoder, sctx, nv); + } +} + +static void +qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; + (void)decoder; + + nv->name = (nghttp3_rcbuf *)&shd->name; + nv->value = sctx->rstate.value; + nv->token = shd->token; + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + sctx->rstate.value = NULL; +} + +static void +qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + (void)decoder; + + nv->name = ent->nv.name; + nv->value = sctx->rstate.value; + nv->token = ent->nv.token; + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(nv->name); + + sctx->rstate.value = NULL; +} + +void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + (void)decoder; + + DEBUGF("qpack::decode: Indexed name (%s) absidx=%zu value=%*s\n", + sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx, + (int)sctx->rstate.value->len, sctx->rstate.value->base); + + if (sctx->rstate.dynamic) { + qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); + } else { + qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + } +} + +void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + (void)decoder; + + DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n", + (int)sctx->rstate.name->len, sctx->rstate.name->base, + (int)sctx->rstate.value->len, sctx->rstate.value->base); + + nv->name = sctx->rstate.name; + nv->value = sctx->rstate.value; + nv->token = qpack_lookup_token(nv->name->base, nv->name->len); + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + sctx->rstate.name = NULL; + sctx->rstate.value = NULL; +} + +int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_encoder *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + *pencoder = p; + + return 0; +} + +void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) { + const nghttp3_mem *mem; + + if (encoder == NULL) { + return; + } + + mem = encoder->ctx.mem; + + nghttp3_qpack_encoder_free(encoder); + nghttp3_mem_free(mem, encoder); +} + +int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, + int64_t stream_id, + const nghttp3_mem *mem) { + nghttp3_qpack_stream_context *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_qpack_stream_context_init(p, stream_id, mem); + + *psctx = p; + + return 0; +} + +void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) { + const nghttp3_mem *mem; + + if (sctx == NULL) { + return; + } + + mem = sctx->mem; + + nghttp3_qpack_stream_context_free(sctx); + nghttp3_mem_free(mem, sctx); +} + +int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_decoder *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + *pdecoder = p; + + return 0; +} + +void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) { + const nghttp3_mem *mem; + + if (decoder == NULL) { + return; + } + + mem = decoder->ctx.mem; + + nghttp3_qpack_decoder_free(decoder); + nghttp3_mem_free(mem, decoder); +} + +size_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) { + return decoder->ctx.next_absidx; +} diff --git a/deps/nghttp3/lib/nghttp3_qpack.h b/deps/nghttp3/lib/nghttp3_qpack.h new file mode 100644 index 00000000000000..b3ccd7a5ee7601 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack.h @@ -0,0 +1,963 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_QPACK_H +#define NGHTTP3_QPACK_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_rcbuf.h" +#include "nghttp3_map.h" +#include "nghttp3_pq.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_ksl.h" +#include "nghttp3_qpack_huffman.h" + +#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of + header name this library can decode. */ +#define NGHTTP3_QPACK_MAX_NAMELEN 256 +/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of + header value this library can decode. */ +#define NGHTTP3_QPACK_MAX_VALUELEN 65536 + +/* nghttp3_qpack_indexing_mode is a indexing strategy. */ +typedef enum { + /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field + should not be inserted into dynamic table. */ + NGHTTP3_QPACK_INDEXING_MODE_LITERAL, + /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be + inserted into dynamic table. */ + NGHTTP3_QPACK_INDEXING_MODE_STORE, + /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should + not be inserted into dynamic table and this must be true for all + forwarding paths. */ + NGHTTP3_QPACK_INDEXING_MODE_NEVER, +} nghttp3_qpack_indexing_mode; + +struct nghttp3_qpack_entry; +typedef struct nghttp3_qpack_entry nghttp3_qpack_entry; + +struct nghttp3_qpack_entry { + /* The header field name/value pair */ + nghttp3_qpack_nv nv; + /* map_next points to the entry which shares same bucket in hash + table. */ + nghttp3_qpack_entry *map_next; + /* sum is the sum of all entries inserted up to this entry. This + value does not contain the space required for this entry. */ + size_t sum; + /* absidx is the absolute index of this entry. */ + size_t absidx; + /* The hash value for header name (nv.name). */ + uint32_t hash; +}; + +/* The entry used for static table. */ +typedef struct { + size_t absidx; + int32_t token; + uint32_t hash; +} nghttp3_qpack_static_entry; + +typedef struct { + nghttp3_rcbuf name; + nghttp3_rcbuf value; + int32_t token; +} nghttp3_qpack_static_header; + +/* + * nghttp3_qpack_header_block_ref is created per encoded header block + * and includes the required insert count and the minimum insert count + * of dynamic table entry it refers to. + */ +typedef struct { + nghttp3_pq_entry max_cnts_pe; + nghttp3_pq_entry min_cnts_pe; + /* max_cnt is the required insert count. */ + size_t max_cnt; + /* min_cnt is the minimum insert count of dynamic table entry it + refers to. In other words, this is the minimum absolute index of + dynamic header table entry this encoded block refers to plus + 1. */ + size_t min_cnt; +} nghttp3_qpack_header_block_ref; + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, + size_t max_cnt, size_t min_cnt, + const nghttp3_mem *mem); + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, + const nghttp3_mem *mem); + +typedef struct { + nghttp3_map_entry me; + /* refs is an array of pointer to nghttp3_qpack_header_block_ref in + the order of the time they are encoded. HTTP/3 allows multiple + header blocks (e.g., non-final response headers, final response + headers, trailers, and push promises) per stream. */ + nghttp3_ringbuf refs; + /* max_cnts is a priority queue sorted by descending order of + max_cnt of nghttp3_qpack_header_block_ref. */ + nghttp3_pq max_cnts; +} nghttp3_qpack_stream; + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, + const nghttp3_mem *mem); + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, + const nghttp3_mem *mem); + +size_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream); + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, + nghttp3_qpack_header_block_ref *ref); + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream); + +#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32 + +typedef struct { + /* dtable is a dynamic table */ + nghttp3_ringbuf dtable; + /* mem is memory allocator */ + const nghttp3_mem *mem; + /* dtable_size is abstracted buffer size of dtable as described in + the spec. This is the sum of length of name/value in dtable + + NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t dtable_size; + size_t dtable_sum; + /* hard_max_dtable_size is the maximum size of dynamic table. In + HTTP/3, it is notified by decoder as + SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal + to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has + the authority to decide how many entries are inserted into + dynamic table. */ + size_t hard_max_dtable_size; + /* max_dtable_size is the effective maximum size of dynamic table. */ + size_t max_dtable_size; + /* max_blocked is the maximum number of stream which can be + blocked. */ + size_t max_blocked; + /* next_absidx is the next absolute index for nghttp3_qpack_entry. + It is equivalent to insert count. */ + size_t next_absidx; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP3_ERR_QPACK_FATAL. */ + uint8_t bad; +} nghttp3_qpack_context; + +typedef struct { + nghttp3_qpack_huffman_decode_context huffman_ctx; + nghttp3_buf namebuf; + nghttp3_buf valuebuf; + nghttp3_rcbuf *name; + nghttp3_rcbuf *value; + uint64_t left; + size_t prefix; + size_t shift; + size_t absidx; + int never; + int dynamic; + int huffman_encoded; +} nghttp3_qpack_read_state; + +void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate); + +void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate); + +#define NGHTTP3_QPACK_MAP_SIZE 128 + +typedef struct { + nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE]; +} nghttp3_qpack_map; + +/* nghttp3_qpack_decoder_stream_state is a set of states when decoding + decoder stream. */ +typedef enum { + NGHTTP3_QPACK_DS_STATE_OPCODE, + NGHTTP3_QPACK_DS_STATE_READ_NUMBER, +} nghttp3_qpack_decoder_stream_state; + +/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder + stream. */ +typedef enum { + NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT, + NGHTTP3_QPACK_DS_OPCODE_HEADER_ACK, + NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL, +} nghttp3_qpack_decoder_stream_opcode; + +/* nghttp3_qpack_encoder_flag is a set of flags used by + nghttp3_qpack_encoder. */ +typedef enum { + NGHTTP3_QPACK_ENCODER_FLAG_NONE = 0x00, + /* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that + Set Dynamic Table Capacity is required. */ + NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP = 0x01, +} nghttp3_qpack_encoder_flag; + +struct nghttp3_qpack_encoder { + nghttp3_qpack_context ctx; + /* dtable_map is a map of hash to nghttp3_qpack_entry to provide + fast access to an entry in dynamic table. */ + nghttp3_qpack_map dtable_map; + /* streams is a map of stream ID to nghttp3_qpack_stream to keep + track of unacknowledged streams. */ + nghttp3_map streams; + /* blocked_streams is an ordered list of nghttp3_qpack_stream, in + descending order of max_cnt, to search the unblocked streams by + received known count. */ + nghttp3_ksl blocked_streams; + /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref + sorted by ascending order of min_cnt to know that an entry can be + evicted from dynamic table. */ + nghttp3_pq min_cnts; + /* krcnt is Known Received Count. */ + size_t krcnt; + /* state is a current state of reading decoder stream. */ + nghttp3_qpack_decoder_stream_state state; + /* opcode is a decoder stream opcode being processed. */ + nghttp3_qpack_decoder_stream_opcode opcode; + /* rstate is a set of intermediate state which are used to process + decoder stream. */ + nghttp3_qpack_read_state rstate; + /* min_dtable_update is the minimum dynamic table size required. */ + size_t min_dtable_update; + /* last_max_dtable_update is the dynamic table size last + requested. */ + size_t last_max_dtable_update; + /* flags is bitwise OR of zero or more of + nghttp3_qpack_encoder_flag. */ + uint8_t flags; +}; + +/* + * nghttp3_qpack_encoder_init initializes |encoder|. + * |max_dtable_size| is the maximum size of dynamic table. + * |max_blocked| is the maximum number of stream which can be blocked. + * |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. + * This function does not free memory pointed by |encoder|. + */ +void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request + * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is + * a header field to encode. |base| is base. |allow_blocking| is + * nonzero if this stream can be blocked (or it has been blocked + * already). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, + size_t *pmax_cnt, size_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, size_t base, + int allow_blocking); + +/* nghttp3_qpack_lookup_result stores a result of table lookup. */ +typedef struct { + /* index is an index of matched entry. -1 if no match is made. */ + nghttp3_ssize index; + /* name_value_match is nonzero if both name and value are + matched. */ + int name_value_match; + /* pb_index is the absolute index of matched post-based dynamic + table entry. -1 if no such entry exists. */ + nghttp3_ssize pb_index; +} nghttp3_qpack_lookup_result; + +/* + * nghttp3_qpack_lookup_stable searches |nv| in static table. |token| + * is a token of nv->name and it is -1 if there is no corresponding + * token defined. |indexing_mode| provides indexing strategy. + */ +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, + nghttp3_qpack_indexing_mode indexing_mode); + +/* + * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table. + * |token| is a token of nv->name and it is -1 if there is no + * corresponding token defined. |hash| is a hash of nv->name. + * |indexing_mode| provides indexing strategy. |krcnt| is Known + * Received Count. |allow_blocking| is nonzero if this stream can be + * blocked (or it has been blocked already). + */ +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( + nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, + uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, size_t krcnt, + int allow_blocking); + +/* + * nghttp3_qpack_encoder_write_header_block_prefix writes Header Block + * Prefix into |pbuf|. |ricnt| is Required Insert Count. |base| is + * Base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_header_block_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, size_t ricnt, + size_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header + * Field to |rbuf|. |absidx| is an absolute index into static table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + size_t absidx); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header + * Field to |rbuf|. |absidx| is an absolute index into dynamic table. + * |base| is base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + size_t absidx, size_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Literal Header + * Field With Name Reference to |rbuf|. |absidx| is an absolute index + * into static table to reference a name. |nv| is a header field to + * encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, size_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header + * Field With Name Reference to |rbuf|. |absidx| is an absolute index + * into dynamic table to reference a name. |base| is a base. |nv| is + * a header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, size_t absidx, + size_t base, const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_literal writes Literal Header Field + * Without Name Reference to |rbuf|. |nv| is a header field to + * encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_static_insert writes Insert With Name + * Reference to |ebuf|. |absidx| is an absolute index into static + * table to reference a name. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name + * Reference to |ebuf|. |absidx| is an absolute index into dynamic + * table to reference a name. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to + * |ebuf|. |absidx| is an absolute index into dynamic table to + * reference an entry. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + size_t absidx); + +/* + * nghttp3_qpack_encoder_write_literal_insert writes Insert Without + * Name Reference to |ebuf|. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + const nghttp3_nv *nv); + +int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_block_stream blocks |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock_stream unblocks |stream|. + */ +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less + * than or equal to |max_cnt|. + */ +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, + size_t max_cnt); + +/* + * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is + * |stream_id|. This function returns NULL if there is no such + * stream. + */ +nghttp3_qpack_stream * +nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +size_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that + * the dynamic table size is less than or equal to maximum size. + */ +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_process_dtable_update processes pending + * dynamic table size update. It might write encoder stream into + * |ebuf|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf); + +/* + * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table + * Capacity. to |ebuf|. |cap| is the capacity of dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t cap); + +/* + * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If + * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a + * hash value of name. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, + nghttp3_qpack_nv *qnv, + nghttp3_qpack_map *dtable_map, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table + * by referencing static table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, + size_t absidx, const nghttp3_nv *nv, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table + * by referencing dynamic table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, + size_t absidx, + const nghttp3_nv *nv, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table + * entry at an absolute index |absidx|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, + size_t absidx); + +/* + * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic + * table. |token| is a token of name and is -1 if it has no token + * value defined. |hash| is a hash of name. + * + * NGHTTP3_ERR_NOMEM Out of memory. + */ +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, + int32_t token, uint32_t hash); + +/* + * nghttp3_qpack_context_dtable_get returns dynamic table entry whose + * absolute index is |absidx|. This function assumes that such entry + * exists. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, size_t absidx); + +/* + * nghttp3_qpack_context_dtable_top returns latest dynamic table + * entry. This function assumes dynamic table is not empty. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx); + +/* + * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header + * field. |sum| is the sum of table space occupied by all entries + * inserted so far. It does not include this entry. |absidx| is an + * absolute index of this entry. |hash| is a hash of header field + * name. This function increases reference count of qnv->nv.name and + * qnv->nv.value. + */ +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, + size_t sum, size_t absidx, uint32_t hash); + +/* + * nghttp3_qpack_entry_free frees memory allocated for |ent|. + */ +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent); + +/* + * nghttp3_qpack_put_varint_len returns the required number of bytes + * to encode |n| with |prefix| bits. + */ +size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix); + +/* + * nghttp3_qpack_put_varint encodes |n| using variable integer + * encoding with |prefix| bits into |buf|. This function assumes the + * buffer pointed by |buf| has enough space. This function returns + * the one byte beyond the last write (buf + + * nghttp3_qpack_put_varint_len(n, prefix)). + */ +uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix); + +/* nghttp3_qpack_encoder_stream_state is a set of states for encoder + stream decoding. */ +typedef enum { + NGHTTP3_QPACK_ES_STATE_OPCODE, + NGHTTP3_QPACK_ES_STATE_READ_INDEX, + NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_NAMELEN, + NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_NAME, + NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_VALUELEN, + NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_VALUE, +} nghttp3_qpack_encoder_stream_state; + +/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in + encoder stream. */ +typedef enum { + NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED, + NGHTTP3_QPACK_ES_OPCODE_INSERT, + NGHTTP3_QPACK_ES_OPCODE_DUPLICATE, + NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP, +} nghttp3_qpack_encoder_stream_opcode; + +/* nghttp3_qpack_request_stream_state is a set of states for request + stream decoding. */ +typedef enum { + NGHTTP3_QPACK_RS_STATE_RICNT, + NGHTTP3_QPACK_RS_STATE_DBASE_SIGN, + NGHTTP3_QPACK_RS_STATE_DBASE, + NGHTTP3_QPACK_RS_STATE_OPCODE, + NGHTTP3_QPACK_RS_STATE_READ_INDEX, + NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_NAMELEN, + NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_NAME, + NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_VALUELEN, + NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_VALUE, + NGHTTP3_QPACK_RS_STATE_BLOCKED, +} nghttp3_qpack_request_stream_state; + +/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in + request stream. */ +typedef enum { + NGHTTP3_QPACK_RS_OPCODE_INDEXED, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB, + NGHTTP3_QPACK_RS_OPCODE_LITERAL, +} nghttp3_qpack_request_stream_opcode; + +struct nghttp3_qpack_decoder { + nghttp3_qpack_context ctx; + /* state is a current state of reading encoder stream. */ + nghttp3_qpack_encoder_stream_state state; + /* opcode is an encoder stream opcode being processed. */ + nghttp3_qpack_encoder_stream_opcode opcode; + /* rstate is a set of intermediate state which are used to process + encoder stream. */ + nghttp3_qpack_read_state rstate; + /* dbuf is decoder stream. */ + nghttp3_buf dbuf; + /* written_icnt is Insert Count written to decoder stream so far. */ + size_t written_icnt; + /* max_concurrent_streams is the number of concurrent streams that a + remote endpoint can open, including both bidirectional and + unidirectional streams which potentially receives QPACK encoded + HEADER frame. */ + size_t max_concurrent_streams; +}; + +/* + * nghttp3_qpack_decoder_init initializes |decoder|. + * |max_dtable_size| is the maximum size of dynamic table. + * |max_blocked| is the maximum number of stream which can be blocked. + * |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_decoder_free frees memory allocated for |decoder|. + * This function does not free memory pointed by |decoder|. + */ +void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in + * Insert With Name Reference to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_static_add adds entry received in + * Insert With Name Reference (static) to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in + * Insert With Name Reference (dynamic) to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in + * Duplicate to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_literal_add adds entry received in + * Insert Without Name Reference to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder); + +struct nghttp3_qpack_stream_context { + /* state is a current state of reading request stream. */ + nghttp3_qpack_request_stream_state state; + /* rstate is a set of intermediate state which are used to process + request stream. */ + nghttp3_qpack_read_state rstate; + const nghttp3_mem *mem; + /* opcode is a request stream opcode being processed. */ + nghttp3_qpack_request_stream_opcode opcode; + int64_t stream_id; + /* ricnt is Required Insert Count to decode this header block. */ + size_t ricnt; + /* base is Base in Header Block Prefix. */ + size_t base; + /* dbase_sign is the delta base sign in Header Block Prefix. */ + int dbase_sign; +}; + +/* + * nghttp3_qpack_stream_context_init initializes |sctx|. + */ +void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, + int64_t stream_id, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_stream_context_free frees memory allocated for + * |sctx|. This function does not free memory pointed by |sctx|. + */ +void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx); + +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx); + +/* + * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required + * Insert Count from the encoded form |encricnt| and stores Required + * Insert Count in |*dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Unable to reconstruct Required Insert Count. + */ +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, + size_t *dest, size_t encricnt); + +/* + * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left + * received in encoder stream to absolute index and stores it in + * rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Relative index is invalid. + */ +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate); + +/* + * nghttp3_qpack_decoder_brel2abs converts Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Base relative index is invalid. + */ +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx); + +/* + * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Post-Base relative index is invalid. + */ +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx); + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +/* + * nghttp3_qpack_decoder_write_header_ack writes Header + * Acknowledgement to decoder stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_FATAL + * Decoder stream overflow. + */ +int nghttp3_qpack_decoder_write_header_ack( + nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx); + +#endif /* NGHTTP3_QPACK_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/nghttp3/lib/nghttp3_qpack_huffman.c new file mode 100644 index 00000000000000..c36a68ededd1af --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman.c @@ -0,0 +1,122 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_qpack_huffman.h" + +#include +#include +#include + +#include "nghttp3_conv.h" + +size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huffman_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, + size_t srclen) { + const nghttp3_qpack_huffman_sym *sym; + const uint8_t *end = src + srclen; + uint64_t code = 0; + size_t nbits = 0; + uint32_t x; + + for (; src != end;) { + sym = &huffman_sym_table[*src++]; + code |= (uint64_t)sym->code << (32 - nbits); + nbits += sym->nbits; + if (nbits < 32) { + continue; + } + x = htonl((uint32_t)(code >> 32)); + memcpy(dest, &x, 4); + dest += 4; + code <<= 32; + nbits -= 32; + } + + for (; nbits >= 8;) { + *dest++ = (uint8_t)(code >> 56); + code <<= 8; + nbits -= 8; + } + + if (nbits) { + *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)); + } + + return dest; +} + +void nghttp3_qpack_huffman_decode_context_init( + nghttp3_qpack_huffman_decode_context *ctx) { + ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED; +} + +nghttp3_ssize +nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, + uint8_t *dest, const uint8_t *src, size_t srclen, + int fin) { + uint8_t *p = dest; + const uint8_t *end = src + srclen; + nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; + const nghttp3_qpack_huffman_decode_node *t = &node; + uint8_t c; + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ + for (; src != end;) { + c = *src++; + t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4]; + if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { + *p++ = t->sym; + } + + t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf]; + if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { + *p++ = t->sym; + } + } + + ctx->fstate = t->fstate; + + if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + return p - dest; +} + +int nghttp3_qpack_huffman_decode_failure_state( + nghttp3_qpack_huffman_decode_context *ctx) { + return ctx->fstate == 0x100; +} diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman.h b/deps/nghttp3/lib/nghttp3_qpack_huffman.h new file mode 100644 index 00000000000000..0cab6ed93e6ccc --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman.h @@ -0,0 +1,108 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_QPACK_HUFFMAN_H +#define NGHTTP3_QPACK_HUFFMAN_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp3_qpack_huffman_sym; + +extern const nghttp3_qpack_huffman_sym huffman_sym_table[]; + +size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len); + +uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, + size_t srclen); + +typedef enum { + /* FSA accepts this state as the end of huffman encoding + sequence. */ + NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14, + /* This state emits symbol */ + NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15, +} nghttp3_qpack_huffman_decode_flag; + +typedef struct { + /* fstate is the current huffman decoding state, which is actually + the node ID of internal huffman tree with + nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes, + but they are identical to root node other than emitting a symbol, + so we have 256 internal nodes [1..256], inclusive. The node ID + 256 is a special node and it is a terminal state that means + decoding failed. */ + uint16_t fstate; + /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */ + uint8_t sym; +} nghttp3_qpack_huffman_decode_node; + +typedef struct { + /* fstate is the current huffman decoding state. */ + uint16_t fstate; +} nghttp3_qpack_huffman_decode_context; + +extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16]; + +void nghttp3_qpack_huffman_decode_context_init( + nghttp3_qpack_huffman_decode_context *ctx); + +/* + * nghttp3_qpack_huffman_decode decodes huffman encoded byte string + * stored in |src| of length |srclen|. |ctx| is a decoding context. + * |ctx| remembers the decoding state, and caller can call this + * function multiple times to feed each chunk of huffman encoded + * substring. |fin| must be nonzero if |src| contains the last chunk + * of huffman string. The decoded string is written to the buffer + * pointed by |dest|. This function assumes that the buffer pointed + * by |dest| contains enough memory to store decoded byte string. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * Could not decode huffman string. + */ +nghttp3_ssize +nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, + uint8_t *dest, const uint8_t *src, size_t srclen, + int fin); + +/* + * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx| + * indicates that huffman decoding context is in failure state. + */ +int nghttp3_qpack_huffman_decode_failure_state( + nghttp3_qpack_huffman_decode_context *ctx); + +#endif /* NGHTTP3_QPACK_HUFFMAN_H */ diff --git a/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c b/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c new file mode 100644 index 00000000000000..0c104dbc0a0bd8 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_qpack_huffman_data.c @@ -0,0 +1,4981 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_qpack_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp3_qpack_huffman_sym huffman_sym_table[] = { + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; + +const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = { + /* 0 */ + { + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, + }, + /* 1 */ + { + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, + }, + /* 2 */ + { + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, + }, + /* 3 */ + { + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, + }, + /* 4 */ + { + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, + }, + /* 5 */ + { + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, + }, + /* 6 */ + { + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, + }, + /* 7 */ + { + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, + }, + /* 8 */ + { + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, + }, + /* 9 */ + { + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, + }, + /* 10 */ + { + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, + }, + /* 11 */ + { + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, + }, + /* 12 */ + { + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, + }, + /* 13 */ + { + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, + }, + /* 14 */ + { + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, + }, + /* 15 */ + { + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, + }, + /* 16 */ + { + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, + }, + /* 17 */ + { + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, + }, + /* 18 */ + { + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, + }, + /* 19 */ + { + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, + }, + /* 20 */ + { + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, + }, + /* 21 */ + { + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, + }, + /* 22 */ + { + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, + }, + /* 23 */ + { + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, + }, + /* 24 */ + { + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, + }, + /* 25 */ + { + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, + }, + /* 26 */ + { + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, + }, + /* 27 */ + { + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, + }, + /* 28 */ + { + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, + }, + /* 29 */ + { + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, + }, + /* 30 */ + { + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, + }, + /* 31 */ + { + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, + }, + /* 32 */ + { + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, + }, + /* 33 */ + { + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, + }, + /* 34 */ + { + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, + }, + /* 35 */ + { + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, + }, + /* 36 */ + { + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, + }, + /* 37 */ + { + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, + }, + /* 38 */ + { + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, + }, + /* 39 */ + { + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, + }, + /* 40 */ + { + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, + }, + /* 41 */ + { + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, + }, + /* 42 */ + { + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, + }, + /* 43 */ + { + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, + }, + /* 44 */ + { + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, + }, + /* 45 */ + { + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, + }, + /* 46 */ + { + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, + }, + /* 47 */ + { + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, + }, + /* 48 */ + { + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, + }, + /* 49 */ + { + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, + }, + /* 50 */ + { + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, + }, + /* 51 */ + { + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, + }, + /* 52 */ + { + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, + }, + /* 53 */ + { + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, + }, + /* 54 */ + { + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, + }, + /* 55 */ + { + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, + }, + /* 56 */ + { + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, + }, + /* 57 */ + { + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, + }, + /* 58 */ + { + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, + }, + /* 59 */ + { + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, + }, + /* 60 */ + { + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, + }, + /* 61 */ + { + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, + }, + /* 62 */ + { + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, + }, + /* 63 */ + { + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, + }, + /* 64 */ + { + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, + }, + /* 65 */ + { + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, + }, + /* 66 */ + { + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, + }, + /* 67 */ + { + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, + }, + /* 68 */ + { + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, + }, + /* 69 */ + { + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, + }, + /* 70 */ + { + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, + }, + /* 71 */ + { + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, + }, + /* 72 */ + { + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, + }, + /* 73 */ + { + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, + }, + /* 74 */ + { + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, + }, + /* 75 */ + { + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, + }, + /* 76 */ + { + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, + }, + /* 77 */ + { + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, + }, + /* 78 */ + { + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, + }, + /* 79 */ + { + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, + }, + /* 80 */ + { + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, + }, + /* 81 */ + { + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, + }, + /* 82 */ + { + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, + }, + /* 83 */ + { + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, + }, + /* 84 */ + { + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, + }, + /* 85 */ + { + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, + }, + /* 86 */ + { + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, + }, + /* 87 */ + { + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, + }, + /* 88 */ + { + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, + }, + /* 89 */ + { + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, + }, + /* 90 */ + { + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, + }, + /* 91 */ + { + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, + }, + /* 92 */ + { + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, + }, + /* 93 */ + { + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, + }, + /* 94 */ + { + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, + }, + /* 95 */ + { + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, + }, + /* 96 */ + { + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, + }, + /* 97 */ + { + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, + }, + /* 98 */ + { + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, + }, + /* 99 */ + { + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, + }, + /* 100 */ + { + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, + }, + /* 101 */ + { + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, + }, + /* 102 */ + { + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, + }, + /* 103 */ + { + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, + }, + /* 104 */ + { + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, + }, + /* 105 */ + { + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, + }, + /* 106 */ + { + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, + }, + /* 107 */ + { + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, + }, + /* 108 */ + { + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, + }, + /* 109 */ + { + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, + }, + /* 110 */ + { + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, + }, + /* 111 */ + { + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, + }, + /* 112 */ + { + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, + }, + /* 113 */ + { + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, + }, + /* 114 */ + { + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, + }, + /* 115 */ + { + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, + }, + /* 116 */ + { + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, + }, + /* 117 */ + { + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, + }, + /* 118 */ + { + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, + }, + /* 119 */ + { + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, + }, + /* 120 */ + { + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, + }, + /* 121 */ + { + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, + }, + /* 122 */ + { + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, + }, + /* 123 */ + { + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, + }, + /* 124 */ + { + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, + }, + /* 125 */ + { + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, + }, + /* 126 */ + { + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, + }, + /* 127 */ + { + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, + }, + /* 128 */ + { + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, + }, + /* 129 */ + { + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, + }, + /* 130 */ + { + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, + }, + /* 131 */ + { + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, + }, + /* 132 */ + { + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, + }, + /* 133 */ + { + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, + }, + /* 134 */ + { + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, + }, + /* 135 */ + { + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, + }, + /* 136 */ + { + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, + }, + /* 137 */ + { + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, + }, + /* 138 */ + { + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, + }, + /* 139 */ + { + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, + }, + /* 140 */ + { + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, + }, + /* 141 */ + { + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, + }, + /* 142 */ + { + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, + }, + /* 143 */ + { + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, + }, + /* 144 */ + { + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, + }, + /* 145 */ + { + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, + }, + /* 146 */ + { + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, + }, + /* 147 */ + { + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, + }, + /* 148 */ + { + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, + }, + /* 149 */ + { + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, + }, + /* 150 */ + { + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, + }, + /* 151 */ + { + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, + }, + /* 152 */ + { + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, + }, + /* 153 */ + { + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, + }, + /* 154 */ + { + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, + }, + /* 155 */ + { + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, + }, + /* 156 */ + { + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, + }, + /* 157 */ + { + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, + }, + /* 158 */ + { + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, + }, + /* 159 */ + { + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, + }, + /* 160 */ + { + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, + }, + /* 161 */ + { + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, + }, + /* 162 */ + { + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, + }, + /* 163 */ + { + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, + }, + /* 164 */ + { + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, + }, + /* 165 */ + { + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, + }, + /* 166 */ + { + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, + }, + /* 167 */ + { + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, + }, + /* 168 */ + { + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, + }, + /* 169 */ + { + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, + }, + /* 170 */ + { + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, + }, + /* 171 */ + { + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, + }, + /* 172 */ + { + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, + }, + /* 173 */ + { + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, + }, + /* 174 */ + { + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, + }, + /* 175 */ + { + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, + }, + /* 176 */ + { + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, + }, + /* 177 */ + { + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, + }, + /* 178 */ + { + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, + }, + /* 179 */ + { + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, + }, + /* 180 */ + { + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, + }, + /* 181 */ + { + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, + }, + /* 182 */ + { + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, + }, + /* 183 */ + { + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, + }, + /* 184 */ + { + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, + }, + /* 185 */ + { + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, + }, + /* 186 */ + { + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, + }, + /* 187 */ + { + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, + }, + /* 188 */ + { + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, + }, + /* 189 */ + { + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, + }, + /* 190 */ + { + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, + }, + /* 191 */ + { + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, + }, + /* 192 */ + { + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, + }, + /* 193 */ + { + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, + }, + /* 194 */ + { + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, + }, + /* 195 */ + { + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, + }, + /* 196 */ + { + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, + }, + /* 197 */ + { + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, + }, + /* 198 */ + { + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, + }, + /* 199 */ + { + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, + }, + /* 200 */ + { + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, + }, + /* 201 */ + { + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, + }, + /* 202 */ + { + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, + }, + /* 203 */ + { + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, + }, + /* 204 */ + { + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, + }, + /* 205 */ + { + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, + }, + /* 206 */ + { + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, + }, + /* 207 */ + { + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, + }, + /* 208 */ + { + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, + }, + /* 209 */ + { + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, + }, + /* 210 */ + { + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, + }, + /* 211 */ + { + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, + }, + /* 212 */ + { + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, + }, + /* 213 */ + { + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, + }, + /* 214 */ + { + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, + }, + /* 215 */ + { + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, + }, + /* 216 */ + { + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, + }, + /* 217 */ + { + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, + }, + /* 218 */ + { + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, + }, + /* 219 */ + { + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, + }, + /* 220 */ + { + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, + }, + /* 221 */ + { + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, + }, + /* 222 */ + { + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, + }, + /* 223 */ + { + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, + }, + /* 224 */ + { + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, + }, + /* 225 */ + { + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, + }, + /* 226 */ + { + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, + }, + /* 227 */ + { + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, + }, + /* 228 */ + { + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, + }, + /* 229 */ + { + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, + }, + /* 230 */ + { + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, + }, + /* 231 */ + { + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, + }, + /* 232 */ + { + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, + }, + /* 233 */ + { + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, + }, + /* 234 */ + { + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, + }, + /* 235 */ + { + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, + }, + /* 236 */ + { + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, + }, + /* 237 */ + { + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, + }, + /* 238 */ + { + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, + }, + /* 239 */ + { + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, + }, + /* 240 */ + { + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, + }, + /* 241 */ + { + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, + }, + /* 242 */ + { + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, + }, + /* 243 */ + { + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, + }, + /* 244 */ + { + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, + }, + /* 245 */ + { + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, + }, + /* 246 */ + { + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, + }, + /* 247 */ + { + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, + }, + /* 248 */ + { + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, + }, + /* 249 */ + { + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, + }, + /* 250 */ + { + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, + }, + /* 251 */ + { + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, + }, + /* 252 */ + { + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, + }, + /* 253 */ + { + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 254 */ + { + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, + }, + /* 255 */ + { + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, +}; diff --git a/deps/nghttp3/lib/nghttp3_range.c b/deps/nghttp3/lib/nghttp3_range.c new file mode 100644 index 00000000000000..0ce71480d72fec --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_range.c @@ -0,0 +1,62 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_range.h" +#include "nghttp3_macro.h" + +void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) { + r->begin = begin; + r->end = end; +} + +nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, + const nghttp3_range *b) { + nghttp3_range r = {0, 0}; + uint64_t begin = nghttp3_max(a->begin, b->begin); + uint64_t end = nghttp3_min(a->end, b->end); + if (begin < end) { + nghttp3_range_init(&r, begin, end); + } + return r; +} + +uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; } + +int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) { + return a->begin == b->begin && a->end == b->end; +} + +void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, + const nghttp3_range *a, const nghttp3_range *b) { + /* Assume that b is included in a */ + left->begin = a->begin; + left->end = b->begin; + right->begin = b->end; + right->end = a->end; +} + +int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) { + return a->end <= b->end; +} diff --git a/deps/nghttp3/lib/nghttp3_range.h b/deps/nghttp3/lib/nghttp3_range.h new file mode 100644 index 00000000000000..e65dd148354e48 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_range.h @@ -0,0 +1,81 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_RANGE_H +#define NGHTTP3_RANGE_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * nghttp3_range represents half-closed range [begin, end). + */ +typedef struct { + uint64_t begin; + uint64_t end; +} nghttp3_range; + +/* + * nghttp3_range_init initializes |r| with the range [|begin|, |end|). + */ +void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end); + +/* + * nghttp3_range_intersect returns the intersection of |a| and |b|. + * If they do not overlap, it returns empty range. + */ +nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, + const nghttp3_range *b); + +/* + * nghttp3_range_len returns the length of |r|. + */ +uint64_t nghttp3_range_len(const nghttp3_range *r); + +/* + * nghttp3_range_eq returns nonzero if |a| equals |b|, such that + * a->begin == b->begin, and a->end == b->end hold. + */ +int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b); + +/* + * nghttp3_range_cut returns the left and right range after removing + * |b| from |a|. This function assumes that |a| completely includes + * |b|. In other words, a->begin <= b->begin and b->end <= a->end + * hold. + */ +void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, + const nghttp3_range *a, const nghttp3_range *b); + +/* + * nghttp3_range_not_after returns nonzero if the right edge of |a| + * does not go beyond of the right edge of |b|. + */ +int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b); + +#endif /* NGHTTP3_RANGE_H */ diff --git a/deps/nghttp3/lib/nghttp3_rcbuf.c b/deps/nghttp3/lib/nghttp3_rcbuf.c new file mode 100644 index 00000000000000..1c31ecebf608d5 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_rcbuf.c @@ -0,0 +1,109 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_rcbuf.h" + +#include + +#include "nghttp3_mem.h" +#include "nghttp3_str.h" + +int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, + const nghttp3_mem *mem) { + uint8_t *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + *rcbuf_ptr = (void *)p; + + (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; + (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf); + (*rcbuf_ptr)->len = size; + (*rcbuf_ptr)->ref = 1; + + return 0; +} + +int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, const nghttp3_mem *mem) { + int rv; + uint8_t *p; + + rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem); + if (rv != 0) { + return rv; + } + + (*rcbuf_ptr)->len = srclen; + p = (*rcbuf_ptr)->base; + + if (srclen) { + p = nghttp3_cpymem(p, src, srclen); + } + + *p = '\0'; + + return 0; +} + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) { + nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); +} + +void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) { + if (rcbuf->ref == -1) { + return; + } + + ++rcbuf->ref; +} + +void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) { + if (rcbuf == NULL || rcbuf->ref == -1) { + return; + } + + assert(rcbuf->ref > 0); + + if (--rcbuf->ref == 0) { + nghttp3_rcbuf_del(rcbuf); + } +} + +nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) { + nghttp3_vec res = {rcbuf->base, rcbuf->len}; + return res; +} + +int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) { + return rcbuf->ref == -1; +} diff --git a/deps/nghttp3/lib/nghttp3_rcbuf.h b/deps/nghttp3/lib/nghttp3_rcbuf.h new file mode 100644 index 00000000000000..feea8040005422 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_rcbuf.h @@ -0,0 +1,82 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_RCBUF_H +#define NGHTTP3_RCBUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +struct nghttp3_rcbuf { + /* custom memory allocator belongs to the mem parameter when + creating this object. */ + void *mem_user_data; + nghttp3_free free; + /* The pointer to the underlying buffer */ + uint8_t *base; + /* Size of buffer pointed by |base|. */ + size_t len; + /* Reference count */ + int32_t ref; +}; + +/* + * Allocates nghttp3_rcbuf object with |size| as initial buffer size. + * When the function succeeds, the reference count becomes 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM: + * Out of memory. + */ +int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, + const nghttp3_mem *mem); + +/* + * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of + * length |srclen|. This function allocates additional byte at the + * end and puts '\0' into it, so that the resulting buffer could be + * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to + * |srclen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM: + * Out of memory. + */ +int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, const nghttp3_mem *mem); + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf); + +#endif /* NGHTTP3_RCBUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_ringbuf.c b/deps/nghttp3/lib/nghttp3_ringbuf.c new file mode 100644 index 00000000000000..6eea9830da17e0 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ringbuf.c @@ -0,0 +1,151 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_ringbuf.h" + +#include +#include +#ifdef WIN32 +# include +#endif + +#include "nghttp3_macro.h" + +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, + const nghttp3_mem *mem) { + if (nmemb) { +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + rb->buf = nghttp3_mem_malloc(mem, nmemb * size); + if (rb->buf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + } else { + rb->buf = NULL; + } + + rb->mem = mem; + rb->nmemb = nmemb; + rb->size = size; + rb->first = 0; + rb->len = 0; + + return 0; +} + +void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) { + if (rb == NULL) { + return; + } + + nghttp3_mem_free(rb->mem, rb->buf); +} + +void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) { + rb->first = (rb->first - 1) & (rb->nmemb - 1); + rb->len = nghttp3_min(rb->nmemb, rb->len + 1); + + return (void *)&rb->buf[rb->first * rb->size]; +} + +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) { + size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); + + if (rb->len == rb->nmemb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + } else { + ++rb->len; + } + + return (void *)&rb->buf[offset * rb->size]; +} + +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + --rb->len; +} + +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) { + assert(rb->len); + --rb->len; +} + +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) { + assert(len <= rb->nmemb); + rb->len = len; +} + +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) { + assert(offset < rb->len); + offset = (rb->first + offset) & (rb->nmemb - 1); + return &rb->buf[offset * rb->size]; +} + +size_t nghttp3_ringbuf_len(nghttp3_ringbuf *rb) { return rb->len; } + +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; } + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) { + uint8_t *buf; + + if (rb->nmemb >= nmemb) { + return 0; + } + +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size); + if (buf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + if (rb->buf != NULL) { + if (rb->first + rb->len <= rb->nmemb) { + memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size); + rb->first = 0; + } else { + memcpy(buf, rb->buf + rb->first * rb->size, + (rb->nmemb - rb->first) * rb->size); + memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf, + (rb->len - (rb->nmemb - rb->first)) * rb->size); + rb->first = 0; + } + + nghttp3_mem_free(rb->mem, rb->buf); + } + + rb->buf = buf; + rb->nmemb = nmemb; + + return 0; +} diff --git a/deps/nghttp3/lib/nghttp3_ringbuf.h b/deps/nghttp3/lib/nghttp3_ringbuf.h new file mode 100644 index 00000000000000..d11bb2b0318aae --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_ringbuf.h @@ -0,0 +1,113 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_RINGBUF_H +#define NGHTTP3_RINGBUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_mem.h" + +typedef struct { + /* buf points to the underlying buffer. */ + uint8_t *buf; + const nghttp3_mem *mem; + /* nmemb is the number of elements that can be stored in this ring + buffer. */ + size_t nmemb; + /* size is the size of each element. */ + size_t size; + /* first is the offset to the first element. */ + size_t first; + /* len is the number of elements actually stored. */ + size_t len; +} nghttp3_ringbuf; + +/* + * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of + * elements that can be stored in this buffer. |size| is the size of + * each element. |size| must be power of 2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, + const nghttp3_mem *mem); + +/* + * nghttp3_ringbuf_free frees resources allocated for |rb|. This + * function does not free the memory pointed by |rb|. + */ +void nghttp3_ringbuf_free(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_front moves the offset to the first element in + the buffer backward, and returns the pointer to the element. + Caller can store data to the buffer pointed by the returned + pointer. If this action exceeds the capacity of the ring buffer, + the last element is silently overwritten, and rb->len remains + unchanged. */ +void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_back moves the offset to the last element in + the buffer forward, and returns the pointer to the element. Caller + can store data to the buffer pointed by the returned pointer. If + this action exceeds the capacity of the ring buffer, the first + element is silently overwritten, and rb->len remains unchanged. */ +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_front removes first element in |rb|. + */ +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_back removes the last element in |rb|. + */ +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_resize changes the number of elements stored. This + does not change the capacity of the underlying buffer. */ +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); + +/* nghttp3_ringbuf_get returns the pointer to the element at + |offset|. */ +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset); + +/* nghttp3_ringbuf_len returns the number of elements stored. */ +size_t nghttp3_ringbuf_len(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb); + +#endif /* NGHTTP3_RINGBUF_H */ diff --git a/deps/nghttp3/lib/nghttp3_str.c b/deps/nghttp3/lib/nghttp3_str.c new file mode 100644 index 00000000000000..3782aa72cd6e81 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_str.c @@ -0,0 +1,110 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_str.h" + +#include +#include + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) { + memcpy(dest, src, n); + return dest + n; +} + +/* Generated by gendowncasetbl.py */ +static const uint8_t DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp3_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} diff --git a/deps/nghttp3/lib/nghttp3_str.h b/deps/nghttp3/lib/nghttp3_str.h new file mode 100644 index 00000000000000..19c1d2c71b559b --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_str.h @@ -0,0 +1,40 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_STR_H +#define NGHTTP3_STR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n); + +void nghttp3_downcase(uint8_t *s, size_t len); + +#endif /* NGHTTP3_STR_H */ diff --git a/deps/nghttp3/lib/nghttp3_stream.c b/deps/nghttp3/lib/nghttp3_stream.c new file mode 100644 index 00000000000000..b75d3f7ba0ecc1 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_stream.c @@ -0,0 +1,1342 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_stream.h" + +#include +#include +#include + +#include "nghttp3_conv.h" +#include "nghttp3_macro.h" +#include "nghttp3_frame.h" +#include "nghttp3_conn.h" +#include "nghttp3_str.h" +#include "nghttp3_http.h" + +/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which + makes a copy to outq. */ +#define NGHTTP3_STREAM_MAX_COPY_THRES 128 + +/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */ +#define NGHTTP3_MIN_RBLEN 4 + +int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_stream_callbacks *callbacks, + const nghttp3_mem *mem) { + int rv; + nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream)); + nghttp3_node_id nid; + + if (stream == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &stream->node, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq, + weight, parent, mem); + + rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); + if (rv != 0) { + goto frq_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); + if (rv != 0) { + goto chunks_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); + if (rv != 0) { + goto outq_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); + if (rv != 0) { + goto inq_init_fail; + } + + nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); + + stream->me.key = (key_type)stream_id; + stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; + stream->mem = mem; + stream->rx.http.status_code = -1; + stream->rx.http.content_length = -1; + stream->error_code = NGHTTP3_H3_NO_ERROR; + + if (callbacks) { + stream->callbacks = *callbacks; + } + + *pstream = stream; + + return 0; + +inq_init_fail: + nghttp3_ringbuf_free(&stream->outq); +outq_init_fail: + nghttp3_ringbuf_free(&stream->chunks); +chunks_init_fail: + nghttp3_ringbuf_free(&stream->frq); +frq_init_fail: + nghttp3_mem_free(mem, stream); + + return rv; +} + +static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) { + nghttp3_typed_buf *tbuf; + size_t i, len = nghttp3_ringbuf_len(outq); + + for (i = 0; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) { + nghttp3_buf_free(&tbuf->buf, mem); + } + } + + nghttp3_ringbuf_free(outq); +} + +static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) { + nghttp3_buf *buf; + size_t i, len = nghttp3_ringbuf_len(chunks); + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(chunks, i); + nghttp3_buf_free(buf, mem); + } + + nghttp3_ringbuf_free(chunks); +} + +static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { + nghttp3_frame_entry *frent; + size_t i, len = nghttp3_ringbuf_len(frq); + + for (i = 0; i < len; ++i) { + frent = nghttp3_ringbuf_get(frq, i); + switch (frent->fr.hd.type) { + case NGHTTP3_FRAME_HEADERS: + nghttp3_frame_headers_free(&frent->fr.headers, mem); + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem); + break; + default: + break; + } + } + + nghttp3_ringbuf_free(frq); +} + +void nghttp3_stream_del(nghttp3_stream *stream) { + if (stream == NULL) { + return; + } + + nghttp3_qpack_stream_context_free(&stream->qpack_sctx); + delete_chunks(&stream->inq, stream->mem); + delete_outq(&stream->outq, stream->mem); + delete_chunks(&stream->chunks, stream->mem); + delete_frq(&stream->frq, stream->mem); + nghttp3_tnode_free(&stream->node); + + nghttp3_mem_free(stream->mem, stream); +} + +void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) { + memset(rvint, 0, sizeof(*rvint)); +} + +void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) { + memset(rstate, 0, sizeof(*rstate)); +} + +nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, + const uint8_t *src, size_t srclen, int fin) { + size_t nread = 0; + size_t n; + size_t i; + + assert(srclen > 0); + + if (rvint->left == 0) { + assert(rvint->acc == 0); + + rvint->left = nghttp3_get_varint_len(src); + if (rvint->left <= srclen) { + rvint->acc = nghttp3_get_varint(&nread, src); + rvint->left = 0; + return (nghttp3_ssize)nread; + } + + if (fin) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + rvint->acc = nghttp3_get_varint_fb(src); + nread = 1; + ++src; + --srclen; + --rvint->left; + } + + n = nghttp3_min(rvint->left, srclen); + + for (i = 0; i < n; ++i) { + rvint->acc = (rvint->acc << 8) + src[i]; + } + + rvint->left -= n; + nread += n; + + if (fin && rvint->left) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + return (nghttp3_ssize)nread; +} + +int nghttp3_stream_frq_add(nghttp3_stream *stream, + const nghttp3_frame_entry *frent) { + nghttp3_ringbuf *frq = &stream->frq; + nghttp3_frame_entry *dest; + int rv; + + if (nghttp3_ringbuf_full(frq)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2); + rv = nghttp3_ringbuf_reserve(frq, nlen); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(frq); + *dest = *frent; + + return 0; +} + +int nghttp3_stream_fill_outq(nghttp3_stream *stream) { + nghttp3_ringbuf *frq = &stream->frq; + nghttp3_frame_entry *frent; + int data_eof; + int rv; + + for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) && + stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) { + frent = nghttp3_ringbuf_get(frq, 0); + + switch (frent->fr.hd.type) { + case NGHTTP3_FRAME_SETTINGS: + rv = nghttp3_stream_write_settings(stream, frent); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_write_headers(stream, frent); + if (rv != 0) { + return rv; + } + nghttp3_frame_headers_free(&frent->fr.headers, stream->mem); + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + rv = nghttp3_stream_write_push_promise(stream, frent); + if (rv != 0) { + return rv; + } + nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem); + break; + case NGHTTP3_FRAME_CANCEL_PUSH: + rv = nghttp3_stream_write_cancel_push(stream, frent); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_write_data(stream, &data_eof, frent); + if (rv != 0) { + return rv; + } + if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) { + return 0; + } + if (!data_eof) { + return 0; + } + break; + case NGHTTP3_FRAME_MAX_PUSH_ID: + rv = nghttp3_stream_write_max_push_id(stream, frent); + if (rv != 0) { + return rv; + } + break; + default: + /* TODO Not implemented */ + break; + } + + nghttp3_ringbuf_pop_front(frq); + } + + return 0; +} + +static void typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk) { + nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED); + tbuf->buf.pos = tbuf->buf.last; +} + +int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { + size_t len = nghttp3_put_varint_len((int64_t)stream->type); + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + int rv; + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) { + size_t len; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + int rv; + nghttp3_push_promise *pp = stream->pp; + + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + assert(pp); + + len = nghttp3_put_varint_len((int64_t)stream->type) + + nghttp3_put_varint_len(pp->node.nid.id); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); + chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id); + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_settings(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + struct { + nghttp3_frame_settings settings; + nghttp3_settings_entry iv[15]; + } fr; + nghttp3_settings_entry *iv; + nghttp3_conn_settings *local_settings = frent->aux.settings.local_settings; + + fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS; + fr.settings.niv = 3; + iv = &fr.settings.iv[0]; + + iv[0].id = NGHTTP3_SETTINGS_ID_MAX_HEADER_LIST_SIZE; + iv[0].value = local_settings->max_header_list_size; + iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; + iv[1].value = local_settings->qpack_max_table_capacity; + iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; + iv[2].value = local_settings->qpack_blocked_streams; + + len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push; + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + + len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id; + nghttp3_conn *conn = stream->conn; + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + + assert(conn); + assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED); + + fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1; + conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes; + conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; + + len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_headers(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_headers *fr = &frent->fr.headers; + nghttp3_conn *conn = stream->conn; + + assert(conn); + + return nghttp3_stream_write_header_block( + stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen); +} + +int nghttp3_stream_write_push_promise(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_push_promise *fr = &frent->fr.push_promise; + nghttp3_conn *conn = stream->conn; + + assert(conn); + + return nghttp3_stream_write_header_block( + stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva, + fr->nvlen); +} + +int nghttp3_stream_write_header_block(nghttp3_stream *stream, + nghttp3_qpack_encoder *qenc, + nghttp3_stream *qenc_stream, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + int64_t frame_type, int64_t push_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_buf pbuf; + int rv; + size_t len; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + nghttp3_frame_hd hd; + size_t push_idlen = 0; + uint8_t raw_pbuf[16]; + size_t pbuflen, rbuflen, ebuflen; + + nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf)); + + rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, + stream->node.nid.id, nva, nvlen); + if (rv != 0) { + goto fail; + } + + pbuflen = nghttp3_buf_len(&pbuf); + rbuflen = nghttp3_buf_len(rbuf); + ebuflen = nghttp3_buf_len(ebuf); + + if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { + push_idlen = nghttp3_put_varint_len(push_id); + } + + hd.type = frame_type; + hd.length = (int64_t)(pbuflen + rbuflen + push_idlen); + + len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen; + + if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { + len += rbuflen; + } + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + goto fail; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + + if (push_idlen) { + chunk->last = nghttp3_put_varint(chunk->last, push_id); + } + + chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); + nghttp3_buf_init(&pbuf); + + if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + + nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE); + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_init(rbuf); + } else if (rbuflen) { + chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen); + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_reset(rbuf); + } + + if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { + assert(qenc_stream); + + nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE); + rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); + if (rv != 0) { + return rv; + } + nghttp3_buf_init(ebuf); + } else if (ebuflen) { + assert(qenc_stream); + + rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen); + if (rv != 0) { + goto fail; + } + + chunk = nghttp3_stream_get_chunk(qenc_stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen); + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_reset(ebuf); + } + + assert(0 == nghttp3_buf_len(&pbuf)); + assert(0 == nghttp3_buf_len(rbuf)); + assert(0 == nghttp3_buf_len(ebuf)); + + return 0; + +fail: + + return rv; +} + +int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, + nghttp3_frame_entry *frent) { + int rv; + size_t len; + nghttp3_typed_buf tbuf; + nghttp3_buf buf; + nghttp3_buf *chunk; + nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data; + nghttp3_conn *conn = stream->conn; + size_t datalen; + uint32_t flags = 0; + nghttp3_frame_hd hd; + nghttp3_vec vec[8]; + nghttp3_vec *v; + nghttp3_ssize sveccnt; + size_t i; + + assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); + assert(read_data); + assert(conn); + + *peof = 0; + + sveccnt = read_data(conn, stream->node.nid.id, vec, nghttp3_arraylen(vec), + &flags, conn->user_data, stream->user_data); + if (sveccnt < 0) { + if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; + return 0; + } + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + datalen = nghttp3_vec_len(vec, (size_t)sveccnt); + + assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF); + + if (flags & NGHTTP3_DATA_FLAG_EOF) { + *peof = 1; + if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + if (datalen == 0) { + if (nghttp3_stream_outq_write_done(stream)) { + /* If this is the last data and its is 0 length, we don't + need send DATA frame. We rely on the non-emptiness of + outq to schedule stream, so add empty tbuf to outq to + just send fin. */ + nghttp3_buf_init(&buf); + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE); + return nghttp3_stream_outq_add(stream, &tbuf); + } + return 0; + } + } + + if (datalen == 0) { + /* We are going to send more frames, but no DATA frame this + time. */ + return 0; + } + } + + hd.type = NGHTTP3_FRAME_DATA; + hd.length = (int64_t)datalen; + + len = nghttp3_frame_write_hd_len(&hd); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + + if (datalen) { + for (i = 0; i < (size_t)sveccnt; ++i) { + v = &vec[i]; + if (v->len == 0) { + continue; + } + nghttp3_buf_wrap_init(&buf, v->base, v->len); + buf.last = buf.end; + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN); + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) { + nghttp3_qpack_decoder *qdec; + nghttp3_buf *chunk; + int rv; + nghttp3_typed_buf tbuf; + size_t len; + + assert(stream->conn); + assert(stream->conn->tx.qdec == stream); + + qdec = &stream->conn->qdec; + + assert(qdec); + + len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec); + if (len == 0) { + return 0; + } + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + nghttp3_qpack_decoder_write_decoder(qdec, chunk); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_outq_is_full(nghttp3_stream *stream) { + /* TODO Verify that the limit is reasonable. */ + return nghttp3_ringbuf_len(&stream->outq) >= 1024; +} + +int nghttp3_stream_outq_add(nghttp3_stream *stream, + const nghttp3_typed_buf *tbuf) { + nghttp3_ringbuf *outq = &stream->outq; + int rv; + nghttp3_typed_buf *dest; + size_t len = nghttp3_ringbuf_len(outq); + + stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf); + + if (len) { + dest = nghttp3_ringbuf_get(outq, len - 1); + if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED && + dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) { + /* If we have already written last entry, adjust outq_idx and + offset so that this entry is eligible to send. */ + if (len == stream->outq_idx) { + --stream->outq_idx; + stream->outq_offset = nghttp3_buf_len(&dest->buf); + } + + dest->buf.last = tbuf->buf.last; + /* TODO Is this required? */ + dest->buf.end = tbuf->buf.end; + + return 0; + } + } + + if (nghttp3_ringbuf_full(outq)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); + rv = nghttp3_ringbuf_reserve(outq, nlen); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(outq); + *dest = *tbuf; + + return 0; +} + +int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) { + nghttp3_ringbuf *chunks = &stream->chunks; + nghttp3_buf *chunk; + size_t len = nghttp3_ringbuf_len(chunks); + uint8_t *p; + int rv; + size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE; + + if (len) { + chunk = nghttp3_ringbuf_get(chunks, len - 1); + if (nghttp3_buf_left(chunk) >= need) { + return 0; + } + } + + for (; n < need; n *= 2) + ; + + p = nghttp3_mem_malloc(stream->mem, n); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + if (nghttp3_ringbuf_full(chunks)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); + rv = nghttp3_ringbuf_reserve(chunks, nlen); + if (rv != 0) { + return rv; + } + } + + chunk = nghttp3_ringbuf_push_back(chunks); + nghttp3_buf_wrap_init(chunk, p, n); + + return 0; +} + +nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) { + nghttp3_ringbuf *chunks = &stream->chunks; + size_t len = nghttp3_ringbuf_len(chunks); + + assert(len); + + return nghttp3_ringbuf_get(chunks, len - 1); +} + +int nghttp3_stream_is_blocked(nghttp3_stream *stream) { + return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) || + (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED); +} + +int nghttp3_stream_is_active(nghttp3_stream *stream) { + return (!nghttp3_stream_outq_write_done(stream) && + !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED)) || + (nghttp3_ringbuf_len(&stream->frq) && + !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); +} + +int nghttp3_stream_require_schedule(nghttp3_stream *stream) { + return nghttp3_stream_is_active(stream) || + nghttp3_tnode_has_active_descendant(&stream->node); +} + +nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, + nghttp3_vec *vec, size_t veccnt) { + nghttp3_ringbuf *outq = &stream->outq; + size_t len = nghttp3_ringbuf_len(outq); + size_t i; + size_t offset = stream->outq_offset; + size_t buflen; + nghttp3_vec *vbegin = vec, *vend = vec + veccnt; + nghttp3_typed_buf *tbuf; + + assert(veccnt > 0); + + for (i = stream->outq_idx; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + buflen = nghttp3_buf_len(&tbuf->buf); + if (offset >= buflen) { + offset -= buflen; + continue; + } + + vec->base = tbuf->buf.pos + offset; + vec->len = buflen - offset; + ++vec; + ++i; + break; + } + + for (; i < len && vec != vend; ++i, ++vec) { + tbuf = nghttp3_ringbuf_get(outq, i); + vec->base = tbuf->buf.pos; + vec->len = nghttp3_buf_len(&tbuf->buf); + } + + /* TODO Rework this if we have finished implementing HTTP + messaging */ + *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len && + (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM); + + return vec - vbegin; +} + +int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { + nghttp3_ringbuf *outq = &stream->outq; + size_t i; + size_t len = nghttp3_ringbuf_len(outq); + size_t offset = stream->outq_offset + n; + size_t buflen; + nghttp3_typed_buf *tbuf; + + for (i = stream->outq_idx; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + buflen = nghttp3_buf_len(&tbuf->buf); + if (offset >= buflen) { + offset -= buflen; + continue; + } + + break; + } + + assert(i < len || offset == 0); + + stream->unsent_bytes -= n; + stream->outq_idx = i; + stream->outq_offset = offset; + + return 0; +} + +int nghttp3_stream_outq_write_done(nghttp3_stream *stream) { + nghttp3_ringbuf *outq = &stream->outq; + size_t len = nghttp3_ringbuf_len(outq); + + return len == 0 || stream->outq_idx >= len; +} + +static int stream_pop_outq_entry(nghttp3_stream *stream, + nghttp3_typed_buf *tbuf) { + nghttp3_ringbuf *chunks = &stream->chunks; + nghttp3_buf *chunk; + + switch (tbuf->type) { + case NGHTTP3_BUF_TYPE_PRIVATE: + nghttp3_buf_free(&tbuf->buf, stream->mem); + break; + case NGHTTP3_BUF_TYPE_ALIEN: + break; + default: + assert(nghttp3_ringbuf_len(chunks)); + + chunk = nghttp3_ringbuf_get(chunks, 0); + + assert(chunk->begin == tbuf->buf.begin); + assert(chunk->end == tbuf->buf.end); + + if (chunk->last == tbuf->buf.last) { + nghttp3_buf_free(chunk, stream->mem); + nghttp3_ringbuf_pop_front(chunks); + } + }; + + nghttp3_ringbuf_pop_front(&stream->outq); + + return 0; +} + +int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, size_t n) { + nghttp3_ringbuf *outq = &stream->outq; + size_t offset = stream->ack_offset + n; + size_t buflen; + size_t npopped = 0; + size_t nack; + nghttp3_typed_buf *tbuf; + int rv; + + for (; nghttp3_ringbuf_len(outq);) { + tbuf = nghttp3_ringbuf_get(outq, 0); + buflen = nghttp3_buf_len(&tbuf->buf); + + if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) { + nack = nghttp3_min(offset, buflen) - stream->ack_done; + if (stream->callbacks.acked_data) { + rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + stream->ack_done += nack; + } + + if (offset >= buflen) { + rv = stream_pop_outq_entry(stream, tbuf); + if (rv != 0) { + return rv; + } + + offset -= buflen; + ++npopped; + stream->ack_done = 0; + + if (stream->outq_idx + 1 == npopped) { + stream->outq_offset = 0; + break; + } + + continue; + } + + break; + } + + assert(stream->outq_idx + 1 >= npopped); + if (stream->outq_idx >= npopped) { + stream->outq_idx -= npopped; + } else { + stream->outq_idx = 0; + } + + stream->ack_offset = offset; + + return 0; +} + +static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) { + if (stream->pp) { + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + return &stream->pp->node; + } + + return &stream->node; +} + +int nghttp3_stream_schedule(nghttp3_stream *stream) { + int rv; + + rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), + stream->unscheduled_nwrite); + if (rv != 0) { + return rv; + } + + stream->unscheduled_nwrite = 0; + + return 0; +} + +int nghttp3_stream_ensure_scheduled(nghttp3_stream *stream) { + if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { + return 0; + } + + return nghttp3_stream_schedule(stream); +} + +void nghttp3_stream_unschedule(nghttp3_stream *stream) { + nghttp3_tnode_unschedule(stream_get_dependency_node(stream)); +} + +int nghttp3_stream_squash(nghttp3_stream *stream) { + nghttp3_tnode *node = stream_get_dependency_node(stream); + + if (!node->parent) { + return 0; + } + return nghttp3_tnode_squash(stream_get_dependency_node(stream)); +} + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data, + size_t datalen) { + nghttp3_ringbuf *inq = &stream->inq; + size_t len = nghttp3_ringbuf_len(inq); + nghttp3_buf *buf; + size_t nwrite; + uint8_t *rawbuf; + size_t bufleft; + int rv; + + if (len) { + buf = nghttp3_ringbuf_get(inq, len - 1); + bufleft = nghttp3_buf_left(buf); + nwrite = nghttp3_min(datalen, bufleft); + buf->last = nghttp3_cpymem(buf->last, data, nwrite); + data += nwrite; + datalen -= nwrite; + } + + for (; datalen;) { + if (nghttp3_ringbuf_full(inq)) { + size_t nlen = + nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2); + rv = nghttp3_ringbuf_reserve(inq, nlen); + if (rv != 0) { + return rv; + } + } + + rawbuf = nghttp3_mem_malloc(stream->mem, 16384); + if (rawbuf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + buf = nghttp3_ringbuf_push_back(inq); + nghttp3_buf_wrap_init(buf, rawbuf, 16384); + bufleft = nghttp3_buf_left(buf); + nwrite = nghttp3_min(datalen, bufleft); + buf->last = nghttp3_cpymem(buf->last, data, nwrite); + data += nwrite; + datalen -= nwrite; + } + + return 0; +} + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) { + nghttp3_ringbuf *inq = &stream->inq; + size_t len = nghttp3_ringbuf_len(inq); + size_t i, n = 0; + nghttp3_buf *buf; + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(inq, i); + n += nghttp3_buf_len(buf); + } + + return n; +} + +void nghttp3_stream_clear_buffered_data(nghttp3_stream *stream) { + nghttp3_ringbuf *inq = &stream->inq; + nghttp3_buf *buf; + + for (; nghttp3_ringbuf_len(inq);) { + buf = nghttp3_ringbuf_get(inq, 0); + nghttp3_buf_free(buf, stream->mem); + nghttp3_ringbuf_pop_front(inq); + } +} + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, + nghttp3_stream_http_event event) { + int rv; + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_NONE: + return NGHTTP3_ERR_H3_INTERNAL_ERROR; + case NGHTTP3_HTTP_STATE_REQ_INITIAL: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_HEADERS_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + /* TODO Better to check status code */ + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_DATA_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_DATA_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + /* TODO Better to check status code */ + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END: + if (event != NGHTTP3_HTTP_EVENT_MSG_END) { + /* TODO Should ignore unexpected frame in this state as per + spec. */ + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_END: + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + case NGHTTP3_HTTP_STATE_RESP_INITIAL: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; + return 0; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + if (stream->rx.http.status_code == -1) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; + return 0; + } + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && + stream->rx.http.status_code / 100 == 2) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_DATA_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_DATA_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && + stream->rx.http.status_code / 100 == 2) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END: + if (event != NGHTTP3_HTTP_EVENT_MSG_END) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_END: + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + default: + assert(0); + } +} + +int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + return 0; + default: + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } +} + +int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) { + return (!nghttp3_stream_uni(stream->node.nid.id) || + stream->type == NGHTTP3_STREAM_TYPE_PUSH); +} + +int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; } + +int nghttp3_client_stream_bidi(int64_t stream_id) { + return (stream_id & 0x3) == 0; +} + +int nghttp3_client_stream_uni(int64_t stream_id) { + return (stream_id & 0x3) == 0x2; +} + +int nghttp3_server_stream_uni(int64_t stream_id) { + return (stream_id & 0x3) == 0x3; +} diff --git a/deps/nghttp3/lib/nghttp3_stream.h b/deps/nghttp3/lib/nghttp3_stream.h new file mode 100644 index 00000000000000..116249777c6a78 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_stream.h @@ -0,0 +1,425 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_STREAM_H +#define NGHTTP3_STREAM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_map.h" +#include "nghttp3_tnode.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_frame.h" +#include "nghttp3_qpack.h" + +#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 + +/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large + enough to fill outgoing single QUIC packet. */ +#define NGHTTP3_MIN_UNSENT_BYTES 4096 + +/* nghttp3_stream_type is unidirectional stream type. */ +typedef enum { + NGHTTP3_STREAM_TYPE_CONTROL = 0x00, + NGHTTP3_STREAM_TYPE_PUSH = 0x01, + NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02, + NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03, + NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX, +} nghttp3_stream_type; + +typedef enum { + NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, + NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS, + NGHTTP3_CTRL_STREAM_STATE_GOAWAY, + NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID, + NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, +} nghttp3_ctrl_stream_state; + +typedef enum { + NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE, + NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_REQ_STREAM_STATE_DATA, + NGHTTP3_REQ_STREAM_STATE_HEADERS, + NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID, + NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE, + NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE, + NGHTTP3_REQ_STREAM_STATE_IGN_FRAME, +} nghttp3_req_stream_state; + +typedef enum { + NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE, + NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_PUSH_STREAM_STATE_DATA, + NGHTTP3_PUSH_STREAM_STATE_HEADERS, + NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME, + NGHTTP3_PUSH_STREAM_STATE_PUSH_ID, + NGHTTP3_PUSH_STREAM_STATE_IGN_REST, +} nghttp3_push_stream_state; + +typedef struct { + int64_t acc; + size_t left; +} nghttp3_varint_read_state; + +typedef struct { + nghttp3_varint_read_state rvint; + nghttp3_frame fr; + int state; + int64_t left; +} nghttp3_stream_read_state; + +typedef enum { + NGHTTP3_STREAM_FLAG_NONE = 0x0000, + NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED = 0x0001, + /* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is + blocked by QUIC flow control. */ + NGHTTP3_STREAM_FLAG_FC_BLOCKED = 0x0002, + /* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application + is temporarily unable to provide data. */ + NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED = 0x0004, + /* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application + finished to feed outgoing data. */ + NGHTTP3_STREAM_FLAG_WRITE_END_STREAM = 0x0008, + /* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is + blocked due to QPACK decoding. */ + NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED = 0x0010, + /* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent + fin. */ + NGHTTP3_STREAM_FLAG_READ_EOF = 0x0020, + /* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. + nghttp3_stream object can still alive because it might be blocked + by QPACK decoder. */ + NGHTTP3_STREAM_FLAG_CLOSED = 0x0040, + /* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is + blocked because the corresponding PUSH_PROMISE has not been + received yet. */ + NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED = 0x0080, + /* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */ + NGHTTP3_STREAM_FLAG_RESET = 0x0200, +} nghttp3_stream_flag; + +typedef enum { + NGHTTP3_HTTP_STATE_NONE, + NGHTTP3_HTTP_STATE_REQ_INITIAL, + NGHTTP3_HTTP_STATE_REQ_BEGIN, + NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN, + NGHTTP3_HTTP_STATE_REQ_HEADERS_END, + NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN, + NGHTTP3_HTTP_STATE_REQ_DATA_END, + NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN, + NGHTTP3_HTTP_STATE_REQ_TRAILERS_END, + NGHTTP3_HTTP_STATE_REQ_END, + NGHTTP3_HTTP_STATE_RESP_INITIAL, + NGHTTP3_HTTP_STATE_RESP_BEGIN, + NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN, + NGHTTP3_HTTP_STATE_RESP_HEADERS_END, + NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN, + NGHTTP3_HTTP_STATE_RESP_DATA_END, + NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN, + NGHTTP3_HTTP_STATE_RESP_TRAILERS_END, + NGHTTP3_HTTP_STATE_RESP_END, +} nghttp3_stream_http_state; + +typedef enum { + NGHTTP3_HTTP_EVENT_DATA_BEGIN, + NGHTTP3_HTTP_EVENT_DATA_END, + NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, + NGHTTP3_HTTP_EVENT_HEADERS_END, + NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN, + NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END, + NGHTTP3_HTTP_EVENT_MSG_END, +} nghttp3_stream_http_event; + +struct nghttp3_stream; +typedef struct nghttp3_stream nghttp3_stream; + +struct nghttp3_push_promise; +typedef struct nghttp3_push_promise nghttp3_push_promise; + +/* + * nghttp3_stream_acked_data is a callback function which is invoked + * when data sent on stream denoted by |stream_id| supplied from + * application is acknowledged by remote endpoint. The number of + * bytes acknowledged is given in |datalen|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller + * immediately. Any values other than 0 is treated as + * NGHTTP3_ERR_CALLBACK_FAILURE. + */ +typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream, + int64_t stream_id, size_t datalen, + void *user_data); + +typedef struct { + nghttp3_stream_acked_data acked_data; +} nghttp3_stream_callbacks; + +struct nghttp3_http_state { + /* status_code is HTTP status code received. This field is used + if connection is initialized as client. */ + int32_t status_code; + /* content_length is the value of received content-length header + field. */ + int64_t content_length; + /* recv_content_length is the number of body bytes received so + far. */ + int64_t recv_content_length; + uint16_t flags; +}; + +typedef struct nghttp3_http_state nghttp3_http_state; + +struct nghttp3_stream { + const nghttp3_mem *mem; + nghttp3_map_entry me; + /* node is a node in dependency tree. For server initiated + unidirectional stream (push), scheduling is done via + corresponding nghttp3_push_promise object pointed by pp. */ + nghttp3_tnode node; + nghttp3_pq_entry qpack_blocked_pe; + nghttp3_stream_callbacks callbacks; + nghttp3_ringbuf frq; + nghttp3_ringbuf chunks; + nghttp3_ringbuf outq; + /* inq stores the stream raw data which cannot be read because + stream is blocked by QPACK decoder. */ + nghttp3_ringbuf inq; + nghttp3_qpack_stream_context qpack_sctx; + /* conn is a reference to underlying connection. It could be NULL + if stream is not a request/push stream. */ + nghttp3_conn *conn; + void *user_data; + /* unsent_bytes is the number of bytes in outq not written yet */ + size_t unsent_bytes; + /* outq_idx is an index into outq where next write is made. */ + size_t outq_idx; + /* outq_offset is write offset relative to the element at outq_idx + in outq. */ + size_t outq_offset; + /* ack_offset is offset acknowledged by peer relative to the first + element in outq. */ + size_t ack_offset; + /* ack_done is the number of bytes notified to an application that + they are acknowledged inside the first outq element if it is of + type NGHTTP3_BUF_TYPE_ALIEN. */ + size_t ack_done; + size_t unscheduled_nwrite; + nghttp3_stream_type type; + nghttp3_stream_read_state rstate; + /* pp is nghttp3_push_promise that this stream fulfills. */ + nghttp3_push_promise *pp; + /* error_code indicates the reason of closure of this stream. */ + uint64_t error_code; + + struct { + nghttp3_stream_http_state hstate; + } tx; + + struct { + nghttp3_stream_http_state hstate; + nghttp3_http_state http; + } rx; + + uint16_t flags; +}; + +typedef struct { + nghttp3_frame fr; + union { + struct { + nghttp3_conn_settings *local_settings; + } settings; + struct { + nghttp3_data_reader dr; + } data; + } aux; +} nghttp3_frame_entry; + +int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_stream_callbacks *callbacks, + const nghttp3_mem *mem); + +void nghttp3_stream_del(nghttp3_stream *stream); + +void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint); + +void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate); + +nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, + const uint8_t *src, size_t srclen, int fin); + +int nghttp3_stream_frq_add(nghttp3_stream *stream, + const nghttp3_frame_entry *frent); + +int nghttp3_stream_fill_outq(nghttp3_stream *stream); + +int nghttp3_stream_write_stream_type(nghttp3_stream *stream); + +int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream); + +nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, + nghttp3_vec *vec, size_t veccnt); + +int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream); + +int nghttp3_stream_outq_is_full(nghttp3_stream *stream); + +int nghttp3_stream_outq_add(nghttp3_stream *stream, + const nghttp3_typed_buf *tbuf); + +int nghttp3_stream_write_headers(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_push_promise(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_header_block(nghttp3_stream *stream, + nghttp3_qpack_encoder *qenc, + nghttp3_stream *qenc_stream, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + int64_t frame_type, int64_t push_id, + const nghttp3_nv *nva, size_t nvlen); + +int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_settings(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need); + +nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream); + +int nghttp3_stream_is_blocked(nghttp3_stream *stream); + +int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n); + +/* + * nghttp3_stream_outq_write_done returns nonzero if all contents in + * outq have been written. + */ +int nghttp3_stream_outq_write_done(nghttp3_stream *stream); + +int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, size_t n); + +/* + * nghttp3_stream_is_active returns nonzero if |stream| is active. In + * other words, it has something to send. This function does not take + * into account its descendants. + */ +int nghttp3_stream_is_active(nghttp3_stream *stream); + +/* + * nghttp3_stream_require_schedule returns nonzero if |stream| should + * be scheduled. In other words, |stream| or its descendants have + * something to send. + */ +int nghttp3_stream_require_schedule(nghttp3_stream *stream); + +/* + * nghttp3_stream_schedule schedules |stream|. This function works + * whether |stream| has already been scheduled or not. If it has been + * scheduled already, it is rescheduled by delaying its pending + * penalty. + */ +int nghttp3_stream_schedule(nghttp3_stream *stream); + +/* + * nghttp3_stream_ensure_scheduled schedules |stream| if it has not + * been scheduled. + */ +int nghttp3_stream_ensure_scheduled(nghttp3_stream *stream); + +void nghttp3_stream_unschedule(nghttp3_stream *stream); + +/* + * nghttp3_stream_squash unschedules |stream| and removes it from + * dependency tree. + */ +int nghttp3_stream_squash(nghttp3_stream *stream); + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src, + size_t srclen); + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream); + +void nghttp3_stream_clear_buffered_data(nghttp3_stream *stream); + +int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream); + +void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream); + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, + nghttp3_stream_http_event event); + +int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); + +/* + * nghttp3_stream_bidi_or_push returns nonzero if |stream| is + * bidirectional or push stream. + */ +int nghttp3_stream_bidi_or_push(nghttp3_stream *stream); + +/* + * nghttp3_stream_uni returns nonzero if stream identified by + * |stream_id| is unidirectional. + */ +int nghttp3_stream_uni(int64_t stream_id); + +/* + * nghttp3_client_stream_bidi returns nonzero if stream identified by + * |stream_id| is client initiated bidirectional stream. + */ +int nghttp3_client_stream_bidi(int64_t stream_id); + +/* + * nghttp3_client_stream_uni returns nonzero if stream identified by + * |stream_id| is client initiated unidirectional stream. + */ +int nghttp3_client_stream_uni(int64_t stream_id); + +/* + * nghttp3_server_stream_uni returns nonzero if stream identified by + * |stream_id| is server initiated unidirectional stream. + */ +int nghttp3_server_stream_uni(int64_t stream_id); + +#endif /* NGHTTP3_STREAM_H */ diff --git a/deps/nghttp3/lib/nghttp3_tnode.c b/deps/nghttp3/lib/nghttp3_tnode.c new file mode 100644 index 00000000000000..b6f6b7874360b7 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_tnode.c @@ -0,0 +1,334 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_tnode.h" + +#include + +#include "nghttp3_macro.h" +#include "nghttp3_stream.h" +#include "nghttp3_conn.h" + +nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, + nghttp3_node_id_type type, int64_t id) { + nid->type = type; + nid->id = id; + return nid; +} + +int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b) { + return a->type == b->type && a->id == b->id; +} + +static int cycle_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe); + const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe); + + if (lhs->cycle == rhs->cycle) { + return lhs->seq < rhs->seq; + } + + return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; +} + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_mem *mem) { + nghttp3_pq_init(&tnode->pq, cycle_less, mem); + + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + tnode->nid = *nid; + tnode->seq = seq; + tnode->cycle = 0; + tnode->pending_penalty = 0; + tnode->weight = weight; + tnode->parent = parent; + tnode->first_child = NULL; + tnode->num_children = 0; + tnode->active = 0; + + if (parent) { + tnode->next_sibling = parent->first_child; + parent->first_child = tnode; + ++parent->num_children; + } else { + tnode->next_sibling = NULL; + } +} + +void nghttp3_tnode_free(nghttp3_tnode *tnode) { nghttp3_pq_free(&tnode->pq); } + +int nghttp3_tnode_is_active(nghttp3_tnode *tnode) { + nghttp3_push_promise *pp; + + switch (tnode->nid.type) { + case NGHTTP3_NODE_ID_TYPE_STREAM: + return nghttp3_stream_is_active( + nghttp3_struct_of(tnode, nghttp3_stream, node)); + case NGHTTP3_NODE_ID_TYPE_PUSH: + pp = nghttp3_struct_of(tnode, nghttp3_push_promise, node); + return pp->stream && nghttp3_stream_is_active(pp->stream); + case NGHTTP3_NODE_ID_TYPE_UT: + /* For unit test */ + return tnode->active; + default: + return 0; + } +} + +static void tnode_unschedule(nghttp3_tnode *tnode) { + nghttp3_tnode *parent; + + for (parent = tnode->parent; parent; tnode = parent, parent = tnode->parent) { + assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&parent->pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + + if (nghttp3_tnode_is_active(parent) || !nghttp3_pq_empty(&parent->pq)) { + return; + } + } +} + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX || + !nghttp3_pq_empty(&tnode->pq)) { + return; + } + + tnode_unschedule(tnode); +} + +void nghttp3_tnode_unschedule_detach(nghttp3_tnode *tnode) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + return; + } + + tnode_unschedule(tnode); +} + +static int tnode_schedule(nghttp3_tnode *tnode, nghttp3_tnode *parent, + uint64_t base_cycle, size_t nwrite) { + uint64_t penalty = + (uint64_t)nwrite * NGHTTP3_MAX_WEIGHT + tnode->pending_penalty; + + tnode->cycle = base_cycle + penalty / tnode->weight; + tnode->pending_penalty = (uint32_t)(penalty % tnode->weight); + + return nghttp3_pq_push(&parent->pq, &tnode->pe); +} + +static uint64_t tnode_get_first_cycle(nghttp3_tnode *tnode) { + nghttp3_tnode *top; + + if (nghttp3_pq_empty(&tnode->pq)) { + return 0; + } + + top = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + return top->cycle; +} + +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, size_t nwrite) { + nghttp3_tnode *parent; + uint64_t cycle; + int rv; + + for (parent = tnode->parent; parent; tnode = parent, parent = tnode->parent) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + cycle = tnode_get_first_cycle(parent); + } else if (nwrite != 0) { + cycle = tnode->cycle; + nghttp3_pq_remove(&parent->pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + } else { + return 0; + } + + rv = tnode_schedule(tnode, parent, cycle, nwrite); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) { + return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX; +} + +nghttp3_tnode *nghttp3_tnode_get_next(nghttp3_tnode *tnode) { + if (nghttp3_pq_empty(&tnode->pq)) { + return NULL; + } + + tnode = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + + for (;;) { + if (nghttp3_tnode_is_active(tnode)) { + return tnode; + } + assert(!nghttp3_pq_empty(&tnode->pq)); + tnode = nghttp3_struct_of(nghttp3_pq_top(&tnode->pq), nghttp3_tnode, pe); + assert(tnode); + } +} + +void nghttp3_tnode_insert(nghttp3_tnode *tnode, nghttp3_tnode *parent) { + assert(tnode->parent == NULL); + assert(tnode->next_sibling == NULL); + assert(tnode->pe.index == NGHTTP3_PQ_BAD_INDEX); + + tnode->next_sibling = parent->first_child; + parent->first_child = tnode; + tnode->parent = parent; + ++parent->num_children; +} + +int nghttp3_tnode_insert_exclusive(nghttp3_tnode *tnode, + nghttp3_tnode *parent) { + nghttp3_tnode **p, *node; + int rv; + + for (node = parent->first_child; node; node = node->next_sibling) { + node->parent = tnode; + + if (node->pe.index == NGHTTP3_PQ_BAD_INDEX) { + continue; + } + + nghttp3_pq_remove(&parent->pq, &node->pe); + node->pe.index = NGHTTP3_PQ_BAD_INDEX; + } + + for (p = &tnode->first_child; *p; p = &(*p)->next_sibling) + ; + + *p = parent->first_child; + parent->first_child = NULL; + tnode->num_children += parent->num_children; + parent->num_children = 0; + + nghttp3_tnode_insert(tnode, parent); + + for (node = *p; node; node = node->next_sibling) { + if (nghttp3_tnode_is_active(node) || + nghttp3_tnode_has_active_descendant(node)) { + rv = nghttp3_tnode_schedule(node, 0); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +void nghttp3_tnode_remove(nghttp3_tnode *tnode) { + nghttp3_tnode *parent = tnode->parent, **p; + + assert(parent); + + if (tnode->pe.index != NGHTTP3_PQ_BAD_INDEX) { + nghttp3_tnode_unschedule_detach(tnode); + } + + for (p = &parent->first_child; *p != tnode; p = &(*p)->next_sibling) + ; + + *p = tnode->next_sibling; + --parent->num_children; + tnode->parent = tnode->next_sibling = NULL; +} + +int nghttp3_tnode_squash(nghttp3_tnode *tnode) { + nghttp3_tnode *parent = tnode->parent, *node, **p, *first, *end; + int rv; + + assert(parent); + + for (p = &parent->first_child; *p != tnode; p = &(*p)->next_sibling) + ; + + *p = tnode->first_child; + + for (; *p; p = &(*p)->next_sibling) { + node = *p; + node->parent = parent; + node->weight = + (uint32_t)(node->weight * tnode->weight / tnode->num_children); + if (node->weight == 0) { + node->weight = 1; + } + + if (node->pe.index == NGHTTP3_PQ_BAD_INDEX) { + continue; + } + + nghttp3_pq_remove(&tnode->pq, &node->pe); + node->pe.index = NGHTTP3_PQ_BAD_INDEX; + } + + *p = tnode->next_sibling; + + first = tnode->first_child; + end = tnode->next_sibling; + + if (tnode->pe.index != NGHTTP3_PQ_BAD_INDEX) { + nghttp3_tnode_unschedule(tnode); + assert(tnode->pe.index == NGHTTP3_PQ_BAD_INDEX); + } + + parent->num_children += tnode->num_children; + tnode->num_children = 0; + tnode->parent = tnode->next_sibling = tnode->first_child = NULL; + + for (node = first; node && node != end; node = node->next_sibling) { + if (nghttp3_tnode_is_active(node) || + nghttp3_tnode_has_active_descendant(node)) { + rv = nghttp3_tnode_schedule(node, 0); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +nghttp3_tnode *nghttp3_tnode_find_ascendant(nghttp3_tnode *tnode, + const nghttp3_node_id *nid) { + for (tnode = tnode->parent; tnode && !nghttp3_node_id_eq(nid, &tnode->nid); + tnode = tnode->parent) + ; + + return tnode; +} + +int nghttp3_tnode_has_active_descendant(nghttp3_tnode *tnode) { + return !nghttp3_pq_empty(&tnode->pq); +} diff --git a/deps/nghttp3/lib/nghttp3_tnode.h b/deps/nghttp3/lib/nghttp3_tnode.h new file mode 100644 index 00000000000000..8e1e5d5bdf98c8 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_tnode.h @@ -0,0 +1,155 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_TNODE_H +#define NGHTTP3_TNODE_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp3_pq.h" + +#define NGHTTP3_DEFAULT_WEIGHT 16 +#define NGHTTP3_MAX_WEIGHT 256 +#define NGHTTP3_TNODE_MAX_CYCLE_GAP ((1llu << 24) * 256 + 255) + +typedef enum { + NGHTTP3_NODE_ID_TYPE_STREAM = 0x00, + NGHTTP3_NODE_ID_TYPE_PUSH = 0x01, + NGHTTP3_NODE_ID_TYPE_PLACEHOLDER = 0x02, + NGHTTP3_NODE_ID_TYPE_ROOT = 0x03, + /* NGHTTP3_NODE_ID_TYPE_UT is defined for unit test */ + NGHTTP3_NODE_ID_TYPE_UT = 0xff, +} nghttp3_node_id_type; + +typedef struct { + nghttp3_node_id_type type; + int64_t id; +} nghttp3_node_id; + +nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, + nghttp3_node_id_type type, int64_t id); + +int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b); + +struct nghttp3_tnode; +typedef struct nghttp3_tnode nghttp3_tnode; + +struct nghttp3_tnode { + nghttp3_pq_entry pe; + nghttp3_pq pq; + nghttp3_tnode *parent; + nghttp3_tnode *first_child; + nghttp3_tnode *next_sibling; + size_t num_children; + nghttp3_node_id nid; + uint64_t seq; + uint64_t cycle; + uint32_t pending_penalty; + uint32_t weight; + /* active is defined for unit test and is nonzero if this node is + active. */ + int active; +}; + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint32_t weight, nghttp3_tnode *parent, + const nghttp3_mem *mem); + +void nghttp3_tnode_free(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_is_active returns nonzero if |tnode| is active. Only + * NGHTTP3_NODE_ID_TYPE_STREAM and NGHTTP3_NODE_ID_TYPE_PUSH (and + * NGHTTP3_NODE_ID_TYPE_UT for unit test) can become active. + */ +int nghttp3_tnode_is_active(nghttp3_tnode *tnode); + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_unschedule_detach works like + * nghttp3_tnode_unschedule, but it removes |tnode| even if tnode->pq + * is not empty. + */ +void nghttp3_tnode_unschedule_detach(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty. + * If |tnode| has already been scheduled, it is rescheduled by the + * amount of |nwrite|. + */ +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, size_t nwrite); + +/* + * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. + */ +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_get_next returns node which has highest priority. + * This function returns NULL if there is no node. + */ +nghttp3_tnode *nghttp3_tnode_get_next(nghttp3_tnode *node); + +/* + * nghttp3_tnode_insert inserts |tnode| as a first child of |parent|. + * |tnode| might have its descendants. + */ +void nghttp3_tnode_insert(nghttp3_tnode *tnode, nghttp3_tnode *parent); + +/* + * nghttp3_tnode_insert_exclusive inserts |tnode| to |parent| as a + * distinct child. The existing direct children of |parent| become + * the children of |tnode|. + */ +int nghttp3_tnode_insert_exclusive(nghttp3_tnode *tnode, nghttp3_tnode *parent); + +/* + * nghttp3_tnode_remove removes |tnode| along with its subtree from + * its parent. + */ +void nghttp3_tnode_remove(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_squash removes |tnode| from its parent. The weight + * of |tnode| is distributed to the direct descendants of |tnode|. + * They are inserted to the former parent of |tnode|. + */ +int nghttp3_tnode_squash(nghttp3_tnode *tnode); + +/* + * nghttp3_tnode_find_ascendant returns an ascendant of |tnode| whose + * node ID is |nid|. If no such node exists, this function returns + * NULL. + */ +nghttp3_tnode *nghttp3_tnode_find_ascendant(nghttp3_tnode *tnode, + const nghttp3_node_id *nid); + +int nghttp3_tnode_has_active_descendant(nghttp3_tnode *tnode); + +#endif /* NGHTTP3_TNODE_H */ diff --git a/deps/nghttp3/lib/nghttp3_vec.c b/deps/nghttp3/lib/nghttp3_vec.c new file mode 100644 index 00000000000000..8d530a060dd502 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_vec.c @@ -0,0 +1,64 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp3_vec.h" +#include "nghttp3_macro.h" + +size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { + size_t i; + size_t res = 0; + + for (i = 0; i < n; ++i) { + res += vec[i].len; + } + + return res; +} + +int nghttp3_vec_empty(const nghttp3_vec *vec, size_t cnt) { + size_t i; + + for (i = 0; i < cnt && vec[i].len == 0; ++i) + ; + + return i == cnt; +} + +void nghttp3_vec_consume(nghttp3_vec **pvec, size_t *pcnt, size_t len) { + nghttp3_vec *v = *pvec; + size_t cnt = *pcnt; + + for (; cnt > 0; --cnt, ++v) { + if (v->len > len) { + v->len -= len; + v->base += len; + break; + } + len -= v->len; + } + + *pvec = v; + *pcnt = cnt; +} diff --git a/deps/nghttp3/lib/nghttp3_vec.h b/deps/nghttp3/lib/nghttp3_vec.h new file mode 100644 index 00000000000000..c1a928e3e1d1ab --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_vec.h @@ -0,0 +1,35 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP3_VEC_H +#define NGHTTP3_VEC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGHTTP3_VEC_H */ diff --git a/deps/nghttp3/lib/nghttp3_version.c b/deps/nghttp3/lib/nghttp3_version.c new file mode 100644 index 00000000000000..dfad4793c4bc11 --- /dev/null +++ b/deps/nghttp3/lib/nghttp3_version.c @@ -0,0 +1,39 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, + NGHTTP3_VERSION}; + +nghttp3_info *nghttp3_version(int least_version) { + if (least_version > NGHTTP3_VERSION_NUM) { + return NULL; + } + return &version; +} diff --git a/deps/nghttp3/nghttp3.gyp b/deps/nghttp3/nghttp3.gyp new file mode 100644 index 00000000000000..3421ba7cf60b15 --- /dev/null +++ b/deps/nghttp3/nghttp3.gyp @@ -0,0 +1,62 @@ +{ + 'target_defaults': { + 'defines': [ + '_U_=' + ] + }, + 'targets': [ + { + 'target_name': 'nghttp3', + 'type': 'static_library', + 'include_dirs': ['lib/includes'], + 'defines': [ + 'BUILDING_NGHTTP3', + 'NGHTTP3_STATICLIB', + ], + 'conditions': [ + ['OS=="win"', { + 'defines': [ + 'WIN32', + '_WINDOWS', + 'HAVE_CONFIG_H', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'CompileAs': '1' + }, + }, + }], + ], + 'direct_dependent_settings': { + 'defines': [ 'NGHTTP3_STATICLIB' ], + 'include_dirs': [ 'lib/includes' ] + }, + 'sources': [ + 'lib/nghttp3_buf.c', + 'lib/nghttp3_conv.c', + 'lib/nghttp3_err.c', + 'lib/nghttp3_gaptr.c', + 'lib/nghttp3_idtr.c', + 'lib/nghttp3_map.c', + 'lib/nghttp3_pq.c', + 'lib/nghttp3_qpack_huffman.c', + 'lib/nghttp3_range.c', + 'lib/nghttp3_ringbuf.c', + 'lib/nghttp3_stream.c', + 'lib/nghttp3_vec.c', + 'lib/nghttp3_conn.c', + 'lib/nghttp3_debug.c', + 'lib/nghttp3_frame.c', + 'lib/nghttp3_http.c', + 'lib/nghttp3_ksl.c', + 'lib/nghttp3_mem.c', + 'lib/nghttp3_qpack.c', + 'lib/nghttp3_qpack_huffman_data.c', + 'lib/nghttp3_rcbuf.c', + 'lib/nghttp3_str.c', + 'lib/nghttp3_tnode.c', + 'lib/nghttp3_version.c' + ] + } + ] +} diff --git a/deps/ngtcp2/COPYING b/deps/ngtcp2/COPYING new file mode 100644 index 00000000000000..9b367cdce71384 --- /dev/null +++ b/deps/ngtcp2/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2016 ngtcp2 contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h new file mode 100644 index 00000000000000..d405e0d1336986 --- /dev/null +++ b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -0,0 +1,561 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_H +#define NGTCP2_CRYPTO_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 +#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16 +#define NGTCP2_CRYPTO_INITIAL_IVLEN 12 + +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx * +ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated + * ciphers and message digests from native TLS session + * |tls_native_handle|. This is used for encrypting/decrypting + * Handshake and Short packets. + * + * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be + * a pointer to SSL object. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +NGTCP2_EXTERN ngtcp2_crypto_aead * +ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_md_hashlen` returns the length of |md| output. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md); + +/** + * @function + * + * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for + * |aead|. + */ +NGTCP2_EXTERN size_t +ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_aead_taglen` returns the length of tag for |aead|. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_aead_taglen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The + * result is the length of |md| and is stored to the buffer pointed by + * |dest|. The caller is responsible to specify the buffer that can + * store the output. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_expand` performs HKDF expand operation. The + * result is |destlen| bytes long and is stored to the buffer pointed + * by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const uint8_t *info, + size_t infolen); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The + * result is |destlen| bytes long and is stored to the buffer pointed + * by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const uint8_t *label, + size_t labellen); + +/** + * @enum + * + * `ngtcp2_crypto_side` indicates which side the application + * implements; client or server. + */ +typedef enum ngtcp2_crypto_side { + /** + * ``NGTCP2_CRYPTO_SIDE_CLIENT`` indicates that the application is + * client. + */ + NGTCP2_CRYPTO_SIDE_CLIENT, + /** + * ``NGTCP2_CRYPTO_SIDE_SERVER`` indicates that the application is + * server. + */ + NGTCP2_CRYPTO_SIDE_SERVER +} ngtcp2_crypto_side; + +/** + * @function + * + * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV + * used to encrypt QUIC packet. + */ +NGTCP2_EXTERN size_t +ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` dervies packet + * protection key. This function writes packet protection key into + * the buffer pointed by |key|. |key| must point to the buffer which + * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This + * function writes packet protection IV into |iv|. |iv| must point to + * the buffer which is at least + * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is + * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is + * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long. + * + * If |hp| is not NULL, this function also derives packet header + * protection key and writes the key into the buffer pointed by |hp|. + * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long. + * |hp|, if not NULL, must have enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( + uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length + * |plaintextlen| and writes the ciphertext into the buffer pointed by + * |dest|. The length of ciphertext is plaintextlen + + * ngtcp2_crypto_aead_taglen(aead) bytes long. |dest| must have + * enough capacity to store the ciphertext. It is allowed to specify + * the same value to |dest| and |plaintext|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest, + const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, + size_t plaintextlen, const uint8_t *key, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_encrypt_cb` is a wrapper function around + * `ngtcp2_crypto_encrypt`. It can be directly passed to encrypt + * callback to ngtcp2_callbacks. + * + * This function returns 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length + * |ciphertextlen| and writes the plaintext into the buffer pointed by + * |dest|. The length of plaintext is ciphertextlen - + * ngtcp2_crypto_aead_taglen(aead) bytes log. |dest| must have enough + * capacity to store the plaintext. It is allowed to specify the same + * value to |dest| and |ciphertext|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_decrypt_cb` is a wrapper function around + * `ngtcp2_crypto_decrypt`. It can be directly passed to decrypt + * callback to ngtcp2_callbacks. + * + * This function returns 0 if it succeeds, or + * :enum:`NGTCP2_ERR_TLS_DECRYPT`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask` generates mask which is used in packet + * header encryption. The mask is written to the buffer pointed by + * |dest|. The length of mask is 5 bytes. |dest| must have enough + * capacity to store the mask. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, + const ngtcp2_crypto_cipher *hp, + const uint8_t *key, + const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around + * `ngtcp2_crypto_hp_mask`. It can be directly passed to hp_mask + * callback to ngtcp2_callbacks. + * + * This function returns 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, + const ngtcp2_crypto_cipher *hp, + const uint8_t *key, + const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_key` derives the rx and tx keys + * from |rx_secret| and |tx_secret| respectively and installs new keys + * to |conn|. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * |level| specifies the encryption level. If |level| is + * NGTCP2_CRYPTO_LEVEL_EARLY, and if |side| is + * NGTCP2_CRYPTO_SIDE_CLIENT, |rx_secret| must be NULL. If |level| is + * NGTCP2_CRYPTO_LEVEL_EARLY, and if |side| is + * NGTCP2_CRYPTO_SIDE_SERVER, |tx_secret| must be NULL. Otherwise, + * |rx_secret| and |tx_secret| must not be NULL. + * + * |secretlen| specifies the length of |rx_secret| and |tx_secret|. + * + * The length of packet protection key and header protection key is + * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection + * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx + * can be obtained by `ngtcp2_crypto_ctx_tls`. + * + * In the first call of this function, it calls + * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message + * digest algorithm. After the successful call of this function, + * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. + * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag + * length. + * + * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a + * remote QUIC transport parameters extension from |tls| and sets it + * to |conn|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_key( + ngtcp2_conn *conn, void *tls, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen, ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_initial_key` derives initial + * keying materials and installs keys to |conn|. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|. The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|. The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|. The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long. The length of packet protection IV is 12 bytes long. + * + * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set + * initial AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_initial_crypto_ctx` to get the object. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + const ngtcp2_cid *client_dcid, ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_update_key` updates traffic keying materials. + * + * The new traffic secret for decryption is written to the buffer + * pointed by |rx_secret|. The length of secret is |secretlen| bytes, + * and |rx_secret| must point to the buffer which has enough capacity. + * + * The new traffic secret for encryption is written to the buffer + * pointed by |tx_secret|. The length of secret is |secretlen| bytes, + * and |tx_secret| must point to the buffer which has enough capacity. + * + * The derived packet protection key for decryption is written to the + * buffer pointed by |rx_key|. The derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. + * + * The derived packet protection key for encryption is written to the + * buffer pointed by |tx_key|. The derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. + * + * |current_rx_secret| and |current_tx_secret| are the current traffic + * secrets for decryption and encryption. |secretlen| specifies the + * length of |rx_secret| and |tx_secret|. + * + * The length of packet protection key and header protection key is + * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection + * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx + * can be obtained by `ngtcp2_conn_get_crypto_ctx`. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, + uint8_t *tx_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_update_key_cb` is a wrapper function around + * `ngtcp2_crypto_update_key`. It can be directly passed to + * update_key field in ngtcp2_callbacks. + * + * This function returns 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of + * length |datalen| in encryption level |crypto_level| and may feed + * outgoing CRYPTO data to |conn|. This function can drive handshake. + * This function can be also used after handshake completes. It is + * allowed to call this function with datalen == 0. In this case, no + * additional read operation is done. + * + * |tls| points to a implementation dependent TLS session object. If + * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or a negative error code. + * The generic error code is -1 if a specific error code is not + * suitable. The error codes less than -10000 are specific to + * underlying TLS implementation. For OpenSSL, the error codes are + * defined in ngtcp2_crypto_openssl.h. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, void *tls, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC + * transport parameters from |tls| and sets it to |conn| using + * `ngtcp2_conn_set_remote_transport_params`. + * + * |tls| points to a implementation dependent TLS session object. If + * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls, + ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_generate_stateless_reset_token` generates a + * stateless reset token using HKDF extraction with |md| using the + * given |cid| and static key |secret| as input. The token will be + * written to the buffer pointed by |token| and it must have a + * capacity of at least NGTCP2_STATELESS_RESET_TOKENLEN bytes. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( + uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const ngtcp2_cid *cid); + +/** + * @function + * + * `ngtcp2_crypto_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE with the given |error_code| to the + * buffer pointed by |dest| of length |destlen|. This function is + * designed for server to close connection without committing the + * state when validating Retry token fails. This function must not be + * used by client. The |dcid| must be the Source Connection ID in + * Initial packet from client. The |scid| must be the Destination + * Connection ID in Initial packet from client. |scid| is used to + * derive initial keying materials. + * + * This function wraps around `ngtcp2_pkt_write_connection_close` for + * easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code); + +/** + * @function + * + * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer + * pointed by |dest| of length |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. + * + * This function wraps around `ngtcp2_pkt_write_retry` for easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize +ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h new file mode 100644 index 00000000000000..7ccb383e3c8069 --- /dev/null +++ b/deps/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_OPENSSL_H +#define NGTCP2_CRYPTO_OPENSSL_H + +#include + +/* OpenSSL specific error codes */ +#define NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP -10001 +#define NGTCP2_CRYPTO_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 + +#endif /* NGTCP2_CRYPTO_OPENSSL_H */ diff --git a/deps/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/crypto/openssl/openssl.c new file mode 100644 index 00000000000000..5e6a8493074a8e --- /dev/null +++ b/deps/ngtcp2/crypto/openssl/openssl.c @@ -0,0 +1,395 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include +#include + +#include +#include +#include + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ctx->aead.native_handle = (void *)EVP_aes_128_gcm(); + ctx->md.native_handle = (void *)EVP_sha256(); + ctx->hp.native_handle = (void *)EVP_aes_128_ctr(); + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + aead->native_handle = (void *)EVP_aes_128_gcm(); + return aead; +} + +static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return EVP_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_aes_128_ccm(); + default: + return NULL; + } +} + +static const EVP_CIPHER *crypto_ssl_get_hp(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_aes_128_ctr(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_ctr(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20(); + default: + return NULL; + } +} + +static const EVP_MD *crypto_ssl_get_md(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + return NULL; + } +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + SSL *ssl = tls_native_handle; + ctx->aead.native_handle = (void *)crypto_ssl_get_aead(ssl); + ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl); + ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl); + return ctx; +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { + return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +static size_t crypto_aead_taglen(const EVP_CIPHER *aead) { + if (aead == EVP_aes_128_gcm() || aead == EVP_aes_256_gcm()) { + return EVP_GCM_TLS_TAG_LEN; + } + if (aead == EVP_chacha20_poly1305()) { + return EVP_CHACHAPOLY_TLS_TAG_LEN; + } + if (aead == EVP_aes_128_ccm()) { + return EVP_CCM_TLS_TAG_LEN; + } + return 0; +} + +size_t ngtcp2_crypto_aead_taglen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_taglen(aead->native_handle); +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + size_t destlen = (size_t)EVP_MD_size(prf); + + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_taglen(cipher); + EVP_CIPHER_CTX *actx; + int rv = 0; + int len; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, + NULL) || + (cipher == EVP_aes_128_ccm() && + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) || + !EVP_EncryptInit_ex(actx, NULL, NULL, key, nonce) || + (cipher == EVP_aes_128_ccm() && + !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || + !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || + !EVP_EncryptFinal_ex(actx, dest + len, &len) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, + dest + plaintextlen)) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_taglen(cipher); + EVP_CIPHER_CTX *actx; + int rv = 0; + int len; + const uint8_t *tag; + + if (taglen > ciphertextlen) { + return -1; + } + + ciphertextlen -= taglen; + tag = ciphertext + ciphertextlen; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, + NULL) || + (cipher == EVP_aes_128_ccm() && + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, + (uint8_t *)tag)) || + !EVP_DecryptInit_ex(actx, NULL, NULL, key, nonce) || + (cipher == EVP_aes_128_ccm() && + !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || + !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || + (cipher != EVP_aes_128_ccm() && + (!EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, + (uint8_t *)tag) || + !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len)))) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key, const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + const EVP_CIPHER *cipher = hp->native_handle; + EVP_CIPHER_CTX *actx; + int rv = 0; + int len; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, hp_key, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + rv = -1; + } + + EVP_CIPHER_CTX_free(actx); + + return rv; +} + +static OSSL_ENCRYPTION_LEVEL +from_ngtcp2_level(ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return ssl_encryption_initial; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return ssl_encryption_handshake; + case NGTCP2_CRYPTO_LEVEL_APP: + return ssl_encryption_application; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return ssl_encryption_early_data; + default: + assert(0); + } +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, void *tls, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + SSL *ssl = tls; + int rv; + int err; + + if (SSL_provide_quic_data(ssl, from_ngtcp2_level(crypto_level), data, + datalen) != 1) { + return -1; + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + rv = SSL_do_handshake(ssl); + if (rv <= 0) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + return NGTCP2_CRYPTO_ERR_TLS_WANT_CLIENT_HELLO_CB; + case SSL_ERROR_WANT_X509_LOOKUP: + return NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + ngtcp2_conn_handshake_completed(conn); + } + + rv = SSL_process_quic_post_handshake(ssl); + if (rv != 1) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls, + ngtcp2_crypto_side side) { + SSL *ssl = tls; + ngtcp2_transport_params_type exttype = + side == NGTCP2_CRYPTO_SIDE_CLIENT + ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS + : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; + const uint8_t *tp; + size_t tplen; + ngtcp2_transport_params params; + int rv; + + SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + + rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; +} diff --git a/deps/ngtcp2/crypto/shared.c b/deps/ngtcp2/crypto/shared.c new file mode 100644 index 00000000000000..6304e59639de47 --- /dev/null +++ b/deps/ngtcp2/crypto/shared.c @@ -0,0 +1,488 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shared.h" + +#include +#include + +#include "ngtcp2_macro.h" + +int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *label, size_t labellen) { + static const uint8_t LABEL[] = "tls13 "; + uint8_t info[256]; + uint8_t *p = info; + + *p++ = (uint8_t)(destlen / 256); + *p++ = (uint8_t)(destlen % 256); + *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen); + memcpy(p, LABEL, sizeof(LABEL) - 1); + p += sizeof(LABEL) - 1; + memcpy(p, label, labellen); + p += labellen; + *p++ = 0; + + return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info, + (size_t)(p - info)); +} + +#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 + +int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, + const ngtcp2_cid *client_dcid, + ngtcp2_crypto_side side) { + static const uint8_t CLABEL[] = "client in"; + static const uint8_t SLABEL[] = "server in"; + uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t *client_secret; + uint8_t *server_secret; + ngtcp2_crypto_ctx ctx; + + if (!initial_secret) { + initial_secret = initial_secret_buf; + } + + ngtcp2_crypto_ctx_initial(&ctx); + + if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data, + client_dcid->datalen, + (const uint8_t *)NGTCP2_INITIAL_SALT, + sizeof(NGTCP2_INITIAL_SALT) - 1) != 0) { + return -1; + } + + if (side == NGTCP2_CRYPTO_SIDE_SERVER) { + client_secret = rx_secret; + server_secret = tx_secret; + } else { + client_secret = tx_secret; + server_secret = rx_secret; + } + + if (ngtcp2_crypto_hkdf_expand_label( + client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, + initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL, + sizeof(CLABEL) - 1) != 0 || + ngtcp2_crypto_hkdf_expand_label( + server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, + initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL, + sizeof(SLABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { + size_t noncelen = ngtcp2_crypto_aead_noncelen(aead); + return ngtcp2_max(8, noncelen); +} + +int ngtcp2_crypto_derive_packet_protection_key( + uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { + static const uint8_t KEY_LABEL[] = "quic key"; + static const uint8_t IV_LABEL[] = "quic iv"; + static const uint8_t HP_KEY_LABEL[] = "quic hp"; + size_t keylen = ngtcp2_crypto_aead_keylen(aead); + size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, + KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { + return -1; + } + + if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, + IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { + return -1; + } + + if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( + hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, + sizeof(HP_KEY_LABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen) { + static const uint8_t LABEL[] = "quic ku"; + + if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen, + LABEL, sizeof(LABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_derive_and_install_key( + ngtcp2_conn *conn, void *tls, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + ngtcp2_crypto_level level, const uint8_t *rx_secret, + const uint8_t *tx_secret, size_t secretlen, ngtcp2_crypto_side side) { + const ngtcp2_crypto_ctx *ctx; + const ngtcp2_crypto_aead *aead; + const ngtcp2_crypto_md *md; + uint8_t rx_keybuf[64], rx_ivbuf[64], rx_hp_keybuf[64]; + uint8_t tx_keybuf[64], tx_ivbuf[64], tx_hp_keybuf[64]; + size_t keylen; + size_t ivlen; + int rv; + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + ctx = ngtcp2_conn_get_crypto_ctx(conn); + + if (!ctx->aead.native_handle) { + ngtcp2_crypto_ctx cctx; + ngtcp2_crypto_ctx_tls(&cctx, tls); + ngtcp2_conn_set_aead_overhead(conn, ngtcp2_crypto_aead_taglen(&cctx.aead)); + ngtcp2_conn_set_crypto_ctx(conn, &cctx); + ctx = ngtcp2_conn_get_crypto_ctx(conn); + } + + aead = &ctx->aead; + md = &ctx->md; + keylen = ngtcp2_crypto_aead_keylen(aead); + ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if ((level != NGTCP2_CRYPTO_LEVEL_EARLY || + side == NGTCP2_CRYPTO_SIDE_SERVER) && + ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, aead, md, rx_secret, secretlen) != 0) { + return -1; + } + + if ((level != NGTCP2_CRYPTO_LEVEL_EARLY || + side == NGTCP2_CRYPTO_SIDE_CLIENT) && + ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, aead, md, tx_secret, secretlen) != 0) { + return -1; + } + + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: + if (side == NGTCP2_CRYPTO_SIDE_CLIENT) { + rv = ngtcp2_conn_install_early_key(conn, tx_key, tx_iv, tx_hp_key, keylen, + ivlen); + } else { + rv = ngtcp2_conn_install_early_key(conn, rx_key, rx_iv, rx_hp_key, keylen, + ivlen); + } + if (rv != 0) { + return -1; + } + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + rv = ngtcp2_conn_install_handshake_key(conn, rx_key, rx_iv, rx_hp_key, + tx_key, tx_iv, tx_hp_key, keylen, + ivlen); + if (rv != 0) { + return -1; + } + break; + case NGTCP2_CRYPTO_LEVEL_APP: + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls, side); + if (rv != 0) { + return -1; + } + + rv = ngtcp2_conn_install_key(conn, rx_secret, tx_secret, rx_key, rx_iv, + rx_hp_key, tx_key, tx_iv, tx_hp_key, secretlen, + keylen, ivlen); + if (rv != 0) { + return -1; + } + + break; + default: + return -1; + } + + return 0; +} + +int ngtcp2_crypto_derive_and_install_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + const ngtcp2_cid *client_dcid, ngtcp2_crypto_side side) { + uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_aead retry_aead; + int rv; + + ngtcp2_crypto_ctx_initial(&ctx); + + if (!rx_secret) { + rx_secret = rx_secretbuf; + } + if (!tx_secret) { + tx_secret = tx_secretbuf; + } + if (!initial_secret) { + initial_secret = initial_secretbuf; + } + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx); + + if (ngtcp2_crypto_derive_initial_secrets(rx_secret, tx_secret, initial_secret, + client_dcid, side) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + rv = ngtcp2_conn_install_initial_key( + conn, rx_key, rx_iv, rx_hp_key, tx_key, tx_iv, tx_hp_key, + NGTCP2_CRYPTO_INITIAL_KEYLEN, NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + return -1; + } + + ngtcp2_conn_set_retry_aead(conn, ngtcp2_crypto_aead_retry(&retry_aead)); + + return 0; +} + +int ngtcp2_crypto_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, + uint8_t *tx_secret, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, + size_t secretlen) { + const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn); + const ngtcp2_crypto_aead *aead = &ctx->aead; + const ngtcp2_crypto_md *md = &ctx->md; + + if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, + secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, + rx_secret, secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret, + secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, + tx_secret, secretlen) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_encrypt(dest, aead, plaintext, plaintextlen, key, nonce, + noncelen, ad, adlen) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_decrypt(dest, aead, ciphertext, ciphertextlen, key, nonce, + noncelen, ad, adlen) != 0) { + return NGTCP2_ERR_TLS_DECRYPT; + } + return 0; +} + +int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key, const uint8_t *sample) { + if (ngtcp2_crypto_hp_mask(dest, hp, hp_key, sample) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_update_key_cb(ngtcp2_conn *conn, uint8_t *rx_secret, + uint8_t *tx_secret, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, + size_t secretlen, void *user_data) { + (void)conn; + (void)user_data; + + if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_key, rx_iv, + tx_key, tx_iv, current_rx_secret, + current_tx_secret, secretlen) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const ngtcp2_cid *cid) { + uint8_t buf[64]; + int rv; + + assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf)); + assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf)); + + rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data, + cid->datalen); + if (rv != 0) { + return -1; + } + + memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN); + + return 0; +} + +ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, + const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + uint64_t error_code) { + uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + ngtcp2_crypto_ctx ctx; + ngtcp2_ssize spktlen; + + ngtcp2_crypto_ctx_initial(&ctx); + + if (ngtcp2_crypto_derive_initial_secrets(rx_secret, tx_secret, initial_secret, + scid, + NGTCP2_CRYPTO_SIDE_SERVER) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + spktlen = ngtcp2_pkt_write_connection_close( + dest, destlen, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, + &ctx.aead, tx_key, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, tx_hp_key); + if (spktlen < 0) { + return -1; + } + + return spktlen; +} + +ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, + const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen) { + ngtcp2_crypto_aead aead; + ngtcp2_ssize spktlen; + + ngtcp2_crypto_aead_retry(&aead); + + spktlen = ngtcp2_pkt_write_retry(dest, destlen, dcid, scid, odcid, token, + tokenlen, ngtcp2_crypto_encrypt_cb, &aead); + if (spktlen < 0) { + return -1; + } + + return spktlen; +} diff --git a/deps/ngtcp2/crypto/shared.h b/deps/ngtcp2/crypto/shared.h new file mode 100644 index 00000000000000..87f3d8928f0a2e --- /dev/null +++ b/deps/ngtcp2/crypto/shared.h @@ -0,0 +1,72 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_SHARED_H +#define NGTCP2_SHARED_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/** + * @function + * + * `ngtcp2_crypto_derive_initial_secrets` derives initial secrets. + * |rx_secret| and |tx_secret| must point to the buffer of at least 32 + * bytes capacity. rx for read and tx for write. This function + * writes rx and tx secrets into |rx_secret| and |tx_secret| + * respectively. The length of secret is 32 bytes long. + * |client_dcid| is the destination connection ID in first Initial + * packet of client. If |initial_secret| is not NULL, the initial + * secret is written to it. It must point to the buffer which has at + * least 32 bytes capacity. The initial secret is 32 bytes long. + * |side| specifies the side of application. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_initial_secrets(uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, + const ngtcp2_cid *client_dcid, + ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_update_traffic_secret` derives the next generation + * of the traffic secret. |secret| specifies the current secret and + * its length is given in |secretlen|. The length of new key is the + * same as the current key. This function writes new key into the + * buffer pointed by |dest|. |dest| must have the enough capacity to + * store the new key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen); + +#endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/lib/includes/config.h b/deps/ngtcp2/lib/includes/config.h new file mode 100644 index 00000000000000..0aee7749bae78e --- /dev/null +++ b/deps/ngtcp2/lib/includes/config.h @@ -0,0 +1,39 @@ + +/* Edited to match src/node.h. */ +#include + +#ifdef _WIN32 +#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) +typedef intptr_t ssize_t; +# define _SSIZE_T_ +# define _SSIZE_T_DEFINED +#endif +#else // !_WIN32 +# include // size_t, ssize_t +#endif // _WIN32 + +#ifdef _MSC_VER +# include +# define __builtin_popcount __popcnt +#endif + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_UNISTD_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h new file mode 100644 index 00000000000000..a2f7d67a5ce2bd --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -0,0 +1,2889 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_H +#define NGTCP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include +#include +#include + +#include + +#ifdef NGTCP2_STATICLIB +# define NGTCP2_EXTERN +#elif defined(WIN32) +# ifdef BUILDING_NGTCP2 +# define NGTCP2_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGTCP2 */ +# define NGTCP2_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGTCP2 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGTCP2 +# define NGTCP2_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGTCP2 */ +# define NGTCP2_EXTERN +# endif /* !BUILDING_NGTCP2 */ +#endif /* !defined(WIN32) */ + +typedef ptrdiff_t ngtcp2_ssize; + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void conn_new() { + * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * } + */ +typedef struct ngtcp2_mem { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + ngtcp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + ngtcp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + ngtcp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + ngtcp2_realloc realloc; +} ngtcp2_mem; + +/* NGTCP2_PROTO_VER is the supported QUIC protocol version. */ +#define NGTCP2_PROTO_VER 0xff00001bu +/* NGTCP2_PROTO_VER_MAX is the highest QUIC version the library + supports. */ +#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER + +/* NGTCP2_ALPN_H3 is a serialized form of HTTP/3 ALPN protocol + identifier this library supports. Notice that the first byte is + the length of the following protocol identifier. */ +#define NGTCP2_ALPN_H3 "\x5h3-27" + +#define NGTCP2_MAX_PKTLEN_IPV4 1252 +#define NGTCP2_MAX_PKTLEN_IPV6 1232 + +/* NGTCP2_MIN_INITIAL_PKTLEN is the minimum UDP packet size for a + packet sent by client which contains its first Initial packet. */ +#define NGTCP2_MIN_INITIAL_PKTLEN 1200 + +/* NGTCP2_STATELESS_RESET_TOKENLEN is the length of Stateless Reset + Token. */ +#define NGTCP2_STATELESS_RESET_TOKENLEN 16 + +/* NGTCP2_MIN_STATELESS_RESET_RANDLEN is the minimum length of random + bytes (Unpredictable Bits) in Stateless Retry packet */ +#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 + +/* NGTCP2_INITIAL_SALT is a salt value which is used to derive initial + secret. */ +#define NGTCP2_INITIAL_SALT \ + "\xc3\xee\xf7\x12\xc7\x2e\xbb\x5a\x11\xa7\xd2\x43\x2b\xb4\x63\x65\xbe\xf9" \ + "\xf5\x02" + +/* NGTCP2_HP_MASKLEN is the length of header protection mask. */ +#define NGTCP2_HP_MASKLEN 5 + +/* NGTCP2_HP_SAMPLELEN is the number bytes sampled when encrypting a + packet header. */ +#define NGTCP2_HP_SAMPLELEN 16 + +/* NGTCP2_SECONDS is a count of tick which corresponds to 1 second. */ +#define NGTCP2_SECONDS ((uint64_t)1000000000ULL) + +/* NGTCP2_MILLISECONDS is a count of tick which corresponds to 1 + millisecond. */ +#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL) + +/* NGTCP2_MICROSECONDS is a count of tick which corresponds to 1 + microsecond. */ +#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL) + +/* NGTCP2_NANOSECONDS is a count of tick which corresponds to 1 + nanosecond. */ +#define NGTCP2_NANOSECONDS ((uint64_t)1ULL) + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_lib_error : int { +#else +typedef enum ngtcp2_lib_error { +#endif + NGTCP2_ERR_INVALID_ARGUMENT = -201, + NGTCP2_ERR_UNKNOWN_PKT_TYPE = -202, + NGTCP2_ERR_NOBUF = -203, + NGTCP2_ERR_PROTO = -205, + NGTCP2_ERR_INVALID_STATE = -206, + NGTCP2_ERR_ACK_FRAME = -207, + NGTCP2_ERR_STREAM_ID_BLOCKED = -208, + NGTCP2_ERR_STREAM_IN_USE = -209, + NGTCP2_ERR_STREAM_DATA_BLOCKED = -210, + NGTCP2_ERR_FLOW_CONTROL = -211, + NGTCP2_ERR_CONNECTION_ID_LIMIT = -212, + NGTCP2_ERR_STREAM_LIMIT = -213, + NGTCP2_ERR_FINAL_SIZE = -214, + NGTCP2_ERR_CRYPTO = -215, + NGTCP2_ERR_PKT_NUM_EXHAUSTED = -216, + NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM = -217, + NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM = -218, + NGTCP2_ERR_FRAME_ENCODING = -219, + NGTCP2_ERR_TLS_DECRYPT = -220, + NGTCP2_ERR_STREAM_SHUT_WR = -221, + NGTCP2_ERR_STREAM_NOT_FOUND = -222, + NGTCP2_ERR_STREAM_STATE = -226, + NGTCP2_ERR_RECV_VERSION_NEGOTIATION = -229, + NGTCP2_ERR_CLOSING = -230, + NGTCP2_ERR_DRAINING = -231, + NGTCP2_ERR_TRANSPORT_PARAM = -234, + NGTCP2_ERR_DISCARD_PKT = -235, + NGTCP2_ERR_PATH_VALIDATION_FAILED = -236, + NGTCP2_ERR_CONN_ID_BLOCKED = -237, + NGTCP2_ERR_INTERNAL = -238, + NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED = -239, + NGTCP2_ERR_WRITE_STREAM_MORE = -240, + NGTCP2_ERR_RETRY = -241, + NGTCP2_ERR_FATAL = -500, + NGTCP2_ERR_NOMEM = -501, + NGTCP2_ERR_CALLBACK_FAILURE = -502, +} ngtcp2_lib_error; + +typedef enum ngtcp2_pkt_flag { + NGTCP2_PKT_FLAG_NONE = 0, + NGTCP2_PKT_FLAG_LONG_FORM = 0x01, + NGTCP2_PKT_FLAG_KEY_PHASE = 0x04 +} ngtcp2_pkt_flag; + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_pkt_type : int { +#else +typedef enum ngtcp2_pkt_type { +#endif + /* NGTCP2_PKT_VERSION_NEGOTIATION is defined by libngtcp2 for + convenience. */ + NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0, + NGTCP2_PKT_INITIAL = 0x0, + NGTCP2_PKT_0RTT = 0x1, + NGTCP2_PKT_HANDSHAKE = 0x2, + NGTCP2_PKT_RETRY = 0x3, + /* NGTCP2_PKT_SHORT is defined by libngtcp2 for convenience. */ + NGTCP2_PKT_SHORT = 0x70 +} ngtcp2_pkt_type; + +/* QUIC transport error code. */ +#define NGTCP2_NO_ERROR 0x0u +#define NGTCP2_INTERNAL_ERROR 0x1u +#define NGTCP2_SERVER_BUSY 0x2u +#define NGTCP2_FLOW_CONTROL_ERROR 0x3u +#define NGTCP2_STREAM_LIMIT_ERROR 0x4u +#define NGTCP2_STREAM_STATE_ERROR 0x5u +#define NGTCP2_FINAL_SIZE_ERROR 0x6u +#define NGTCP2_FRAME_ENCODING_ERROR 0x7u +#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u +#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u +#define NGTCP2_PROTOCOL_VIOLATION 0xau +#define NGTCP2_INVALID_TOKEN 0xbu +#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu +#define NGTCP2_KEY_UPDATE_ERROR 0xeu +#define NGTCP2_CRYPTO_ERROR 0x100u + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_path_validation_result : int { +#else +typedef enum ngtcp2_path_validation_result { +#endif + NGTCP2_PATH_VALIDATION_RESULT_SUCCESS, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE, +} ngtcp2_path_validation_result; + +/* + * ngtcp2_tstamp is a timestamp with NGTCP2_DURATION_TICK resolution. + */ +typedef uint64_t ngtcp2_tstamp; + +/* + * ngtcp2_duration is a period of time in NGTCP2_DURATION_TICK + * resolution. + */ +typedef uint64_t ngtcp2_duration; + +/* NGTCP2_MAX_CIDLEN is the maximum length of Connection ID. */ +#define NGTCP2_MAX_CIDLEN 20 +/* NGTCP2_MIN_CIDLEN is the minimum length of Connection ID. */ +#define NGTCP2_MIN_CIDLEN 1 + +/** + * @struct + * + * ngtcp2_cid holds a Connection ID. + */ +typedef struct ngtcp2_cid { + size_t datalen; + uint8_t data[NGTCP2_MAX_CIDLEN]; +} ngtcp2_cid; + +/** + * @struct + * + * ngtcp2_vec is struct iovec compatible structure to reference + * arbitrary array of bytes. + */ +typedef struct ngtcp2_vec { + /* base points to the data. */ + uint8_t *base; + /* len is the number of bytes which the buffer pointed by base + contains. */ + size_t len; +} ngtcp2_vec; + +/** + * @function + * + * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte + * string pointed by |data| and its length is |datalen|. |datalen| + * must be at least :enum:`NGTCP2_MIN_CIDLEN`, and at most + * :enum:`NGTCP2_MAX_CIDLEN`. + */ +NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, + size_t datalen); + +typedef struct ngtcp2_pkt_hd { + ngtcp2_cid dcid; + ngtcp2_cid scid; + int64_t pkt_num; + uint8_t *token; + size_t tokenlen; + /** + * pkt_numlen is the number of bytes spent to encode pkt_num. + */ + size_t pkt_numlen; + /** + * len is the sum of pkt_numlen and the length of QUIC packet + * payload. + */ + size_t len; + uint32_t version; + uint8_t type; + uint8_t flags; +} ngtcp2_pkt_hd; + +typedef struct ngtcp2_pkt_stateless_reset { + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; + const uint8_t *rand; + size_t randlen; +} ngtcp2_pkt_stateless_reset; + +/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ +#define NGTCP2_RETRY_TAGLEN 16 + +typedef struct ngtcp2_pkt_retry { + ngtcp2_cid odcid; + const uint8_t *token; + size_t tokenlen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; +} ngtcp2_pkt_retry; + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_transport_param_id : uint16_t { +#else +typedef enum ngtcp2_transport_param_id { +#endif + NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID = 0x0000, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, + NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE = 0x0003, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, + NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, + NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, + NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, + NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, + NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, + NGTCP2_TRANSPORT_PARAM_ID_MAX = UINT16_MAX +} ngtcp2_transport_param_id; + +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_transport_params_type : int { +#else +typedef enum ngtcp2_transport_params_type { +#endif + NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS +} ngtcp2_transport_params_type; + +/** + * @enum + * + * ngtcp2_rand_ctx is a context where generated random value is used. + */ +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_rand_ctx : int { +#else +typedef enum ngtcp2_rand_ctx { +#endif + NGTCP2_RAND_CTX_NONE, + /** + * NGTCP2_RAND_CTX_PATH_CHALLENGE indicates that random value is + * used for PATH_CHALLENGE. + */ + NGTCP2_RAND_CTX_PATH_CHALLENGE +} ngtcp2_rand_ctx; + +#define NGTCP2_MAX_PKT_SIZE 65527 + +/** + * @macro + * + * NGTCP2_DEFAULT_ACK_DELAY_EXPONENT is a default value of scaling + * factor of ACK Delay field in ACK frame. + */ +#define NGTCP2_DEFAULT_ACK_DELAY_EXPONENT 3 + +/** + * @macro + * + * NGTCP2_DEFAULT_MAX_ACK_DELAY is a default value of the maximum + * amount of time in nanoseconds by which endpoint delays sending + * acknowledgement. + */ +#define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS) + +/** + * @macro + * + * NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT is the default value of + * active_connection_id_limit transport parameter value if omitted. + */ +#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2 + +/** + * @macro + * + * NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS is TLS extension type of + * quic_transport_parameters. + */ +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u + +typedef struct ngtcp2_preferred_addr { + ngtcp2_cid cid; + uint16_t ipv4_port; + uint16_t ipv6_port; + uint8_t ipv4_addr[4]; + uint8_t ipv6_addr[16]; + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_preferred_addr; + +typedef struct ngtcp2_transport_params { + ngtcp2_preferred_addr preferred_address; + /* original_connection_id is the client initial connection ID. + Server must specify this field and set + original_connection_id_present to nonzero if it sent Retry + packet. */ + ngtcp2_cid original_connection_id; + /* initial_max_stream_data_bidi_local is the size of flow control + window of locally initiated stream. This is the number of bytes + that the remote endpoint can send and the local endpoint must + ensure that it has enough buffer to receive them. */ + uint64_t initial_max_stream_data_bidi_local; + /* initial_max_stream_data_bidi_remote is the size of flow control + window of remotely initiated stream. This is the number of bytes + that the remote endpoint can send and the local endpoint must + ensure that it has enough buffer to receive them. */ + uint64_t initial_max_stream_data_bidi_remote; + /* initial_max_stream_data_uni is the size of flow control window of + remotely initiated unidirectional stream. This is the number of + bytes that the remote endpoint can send and the local endpoint + must ensure that it has enough buffer to receive them. */ + uint64_t initial_max_stream_data_uni; + /* initial_max_data is the connection level flow control window. */ + uint64_t initial_max_data; + /* initial_max_streams_bidi is the number of concurrent streams that + the remote endpoint can create. */ + uint64_t initial_max_streams_bidi; + /* initial_max_streams_uni is the number of concurrent + unidirectional streams that the remote endpoint can create. */ + uint64_t initial_max_streams_uni; + /* max_idle_timeout is a duration during which sender allows + quiescent. */ + ngtcp2_duration max_idle_timeout; + uint64_t max_packet_size; + /* active_connection_id_limit is the maximum number of Connection ID + that sender can store. */ + uint64_t active_connection_id_limit; + uint64_t ack_delay_exponent; + ngtcp2_duration max_ack_delay; + uint8_t stateless_reset_token_present; + uint8_t disable_active_migration; + uint8_t original_connection_id_present; + uint8_t preferred_address_present; + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_transport_params; + +/* user_data is the same object passed to ngtcp2_conn_client_new or + ngtcp2_conn_server_new. */ +typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...); + +typedef void (*ngtcp2_qlog_write)(void *user_data, const void *data, + size_t datalen); + +typedef struct ngtcp2_qlog_settings { + /* odcid is Original Destination Connection ID sent by client. It + is used as group_id and ODCID fields. */ + ngtcp2_cid odcid; + /* write is a callback function to write qlog. Setting NULL + disables qlog. */ + ngtcp2_qlog_write write; +} ngtcp2_qlog_settings; + +typedef struct ngtcp2_settings { + /* transport_params is the QUIC transport parameters to send. */ + ngtcp2_transport_params transport_params; + ngtcp2_qlog_settings qlog; + /* initial_ts is an initial timestamp given to the library. */ + ngtcp2_tstamp initial_ts; + /* log_printf is a function that the library uses to write logs. + NULL means no logging output. */ + ngtcp2_printf log_printf; + /* token is a token received in Client Initial packet and + successfully validated. Only server application may specify this + field. Server then verifies that all Client Initial packets have + this token. `ngtcp2_conn_server_new` makes a copy of token. */ + ngtcp2_vec token; +} ngtcp2_settings; + +/** + * @struct + * + * ngtcp2_rcvry_stat holds various statistics, and computed data for + * recovery from packet loss. + * + * Everything is NGTCP2_DURATION_TICK resolution. + */ +typedef struct ngtcp2_rcvry_stat { + ngtcp2_duration latest_rtt; + ngtcp2_duration min_rtt; + ngtcp2_duration smoothed_rtt; + ngtcp2_duration rttvar; + size_t pto_count; + ngtcp2_tstamp loss_detection_timer; + /* last_tx_pkt_ts corresponds to + time_of_last_sent_ack_eliciting_packet in + draft-ietf-quic-recovery-25. It is indexed by + ngtcp2_crypto_level. No last_tx_pkt_ts for 0RTT packet. */ + ngtcp2_tstamp last_tx_pkt_ts[3]; +} ngtcp2_rcvry_stat; + +typedef struct ngtcp2_cc_stat { + uint64_t cwnd; + uint64_t ssthresh; + ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t bytes_in_flight; +} ngtcp2_cc_stat; + +/** + * @struct + * + * ngtcp2_addr is the endpoint address. + */ +typedef struct ngtcp2_addr { + /* addrlen is the length of addr. */ + size_t addrlen; + /* addr points to the buffer which contains endpoint address. It is + opaque to the ngtcp2 library. */ + uint8_t *addr; + /* user_data is an arbitrary data and opaque to the library. */ + void *user_data; +} ngtcp2_addr; + +/** + * @struct + * + * ngtcp2_path is the network endpoints where a packet is sent and + * received. + */ +typedef struct ngtcp2_path { + /* local is the address of local endpoint. */ + ngtcp2_addr local; + /* remote is the address of remote endpoint. */ + ngtcp2_addr remote; +} ngtcp2_path; + +/** + * @struct + * + * ngtcp2_path_storage is a convenient struct to have buffers to store + * the longest addresses. + */ +typedef struct ngtcp2_path_storage { + uint8_t local_addrbuf[128]; + uint8_t remote_addrbuf[128]; + ngtcp2_path path; +} ngtcp2_path_storage; + +/** + * @struct + * + * `ngtcp2_crypto_md` is a wrapper around native message digest + * object. + * + * If libngtcp2_crypto_openssl is linked, native_handle must be a + * pointer to EVP_MD. + */ +typedef struct ngtcp2_crypto_md { + void *native_handle; +} ngtcp2_crypto_md; + +/** + * @struct + * + * `ngtcp2_crypto_aead` is a wrapper around native AEAD object. + * + * If libngtcp2_crypto_openssl is linked, native_handle must be a + * pointer to EVP_CIPHER. + */ +typedef struct ngtcp2_crypto_aead { + void *native_handle; +} ngtcp2_crypto_aead; + +/** + * @struct + * + * `ngtcp2_crypto_cipher` is a wrapper around native cipher object. + * + * If libngtcp2_crypto_openssl is linked, native_handle must be a + * pointer to EVP_CIPHER. + */ +typedef struct ngtcp2_crypto_cipher { + void *native_handle; +} ngtcp2_crypto_cipher; + +/** + * @function + * + * `ngtcp2_crypto_ctx` is a convenient structure to bind all crypto + * related objects in one place. Use `ngtcp2_crypto_ctx_initial` to + * initialize this struct for Initial packet encryption. For + * Handshake and Shortpackets, use `ngtcp2_crypto_ctx_tls`. + */ +typedef struct ngtcp2_crypto_ctx { + ngtcp2_crypto_aead aead; + ngtcp2_crypto_md md; + ngtcp2_crypto_cipher hp; +} ngtcp2_crypto_ctx; + +/** + * @function + * + * `ngtcp2_encode_transport_params` encodes |params| in |dest| of + * length |destlen|. + * + * This function returns the number of written, or one of the + * following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT`: + * |exttype| is invalid. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( + uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, + const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_decode_transport_params` decodes transport parameters in + * |data| of length |datalen|, and stores the result in the object + * pointed by |params|. + * + * If the optional parameters are missing, the default value is + * assigned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :enum:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT`: + * |exttype| is invalid. + */ +NGTCP2_EXTERN int +ngtcp2_decode_transport_params(ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_pkt_decode_version_cid` extracts QUIC version, Destination + * Connection ID and Source Connection ID from the packet pointed by + * |data| of length |datalen|. This function can handle Connection ID + * up to 255 bytes unlike `ngtcp2_pkt_decode_hd_long` or + * `ngtcp2_pkt_decode_hd_short` which are only capable of handling + * Connection ID less than or equal to :macro:`NGTCP2_MAX_CIDLEN`. + * Longer Connection ID is only valid if the version is unsupported + * QUIC version. + * + * If the given packet is Long packet, this function extracts the + * version from the packet and assigns it to |*pversion|. It also + * extracts the pointer to the Destination Connection ID and its + * length and assigns them to |*pdcid| and |*pdcidlen| respectively. + * Similarly, it extracts the pointer to the Source Connection ID and + * its length and assigns them to |*pscid| and |*pscidlen| + * respectively. + * + * If the given packet is Short packet, |*pversion| will be + * :macro:`NGTCP2_PROTO_VER`, |*pscid| will be NULL, and |*pscidlen| + * will be 0. Because the Short packet does not have the length of + * Destination Connection ID, the caller has to pass the length in + * |short_dcidlen|. This function extracts the pointer to the + * Destination Connection ID and assigns it to |*pdcid|. + * |short_dcidlen| is assigned to |*pdcidlen|. + * + * This function returns 0 or 1 if it succeeds. It returns 1 if + * Version Negotiation packet should be sent. Otherwise, one of the + * following negative error code: + * + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` + * The function could not decode the packet header. + */ +NGTCP2_EXTERN int +ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, + size_t *pdcidlen, const uint8_t **pscid, + size_t *pscidlen, const uint8_t *data, + size_t datalen, size_t short_dcidlen); + +/** + * @function + * + * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in + * |pkt| of length |pktlen|. This function only parses the input just + * before packet number field. + * + * This function does not verify that length field is correct. In + * other words, this function succeeds even if length > |pktlen|. + * + * This function can handle Connection ID up to + * :enum:`NGTCP2_MAX_CIDLEN`. Consider to use + * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. + * + * This function handles Version Negotiation specially. If version + * field is 0, |pkt| must contain Version Negotiation packet. Version + * Negotiation packet has random type in wire format. For + * convenience, this function sets + * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` to dest->type, and set + * dest->payloadlen and dest->pkt_num to 0. Version Negotiation + * packet occupies a single packet. + * + * It stores the result in the object pointed by |dest|, and returns + * the number of bytes decoded to read the packet header if it + * succeeds, or one of the following error codes: + * + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` + * Packet is too short; or it is not a long header + * :enum:`NGTCP2_ERR_UNKNOWN_PKT_TYPE` + * Packet type is unknown + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, + const uint8_t *pkt, + size_t pktlen); + +/** + * @function + * + * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in + * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in + * packet header. Short packet does not encode the length of + * connection ID, thus we need the input from the outside. This + * function only parses the input just before packet number field. + * This function can handle Connection ID up to + * :enum:`NGTCP2_MAX_CIDLEN`. Consider to use + * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It + * stores the result in the object pointed by |dest|, and returns the + * number of bytes decoded to read the packet header if it succeeds, + * or one of the following error codes: + * + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` + * Packet is too short; or it is not a short header + * :enum:`NGTCP2_ERR_UNKNOWN_PKT_TYPE` + * Packet type is unknown + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, + const uint8_t *pkt, + size_t pktlen, + size_t dcidlen); + +/** + * @function + * + * `ngtcp2_pkt_write_stateless_reset` writes Stateless Reset packet in + * the buffer pointed by |dest| whose length is |destlen|. + * |stateless_reset_token| is a pointer to the Stateless Reset Token, + * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` + * bytes long. |rand| specifies the random octets preceding Stateless + * Reset Token. The length of |rand| is specified by |randlen| which + * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes + * long. + * + * If |randlen| is too long to write them all in the buffer, |rand| is + * written to the buffer as much as possible, and is truncated. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` + * |randlen| is strictly less than + * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( + uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen); + +/** + * @function + * + * `ngtcp2_pkt_write_version_negotiation` writes Version Negotiation + * packet in the buffer pointed by |dest| whose length is |destlen|. + * |unused_random| should be generated randomly. |dcid| is the + * destination connection ID which appears in a packet as a source + * connection ID sent by client which caused version negotiation. + * Similarly, |scid| is the source connection ID which appears in a + * packet as a destination connection ID sent by client. |sv| is a + * list of supported versions, and |nsv| specifies the number of + * supported versions included in |sv|. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( + uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, + size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, + size_t nsv); + +/** + * @function + * + * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is + * the first byte of Long packet header. + */ +NGTCP2_EXTERN uint8_t ngtcp2_pkt_get_type_long(uint8_t c); + +struct ngtcp2_conn; + +typedef struct ngtcp2_conn ngtcp2_conn; + +/** + * @functypedef + * + * :type:`ngtcp2_client_initial` is invoked when client application + * asks TLS stack to produce first TLS cryptographic handshake data. + * + * This implementation of this callback must get the first handshake + * data from TLS stack and pass it to ngtcp2 library using + * `ngtcp2_conn_submit_crypto_data` function. Make sure that before + * calling `ngtcp2_conn_submit_crypto_data` function, client + * application must create initial packet protection keys and IVs, and + * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` + * and + * + * This callback function must return 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + * + * TODO: Define error code for TLS stack failure. Suggestion: + * NGTCP2_ERR_CRYPTO. + */ +typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_client_initial` is invoked when server receives + * Initial packet from client. An server application must implement + * this callback, and generate initial keys and IVs for both + * transmission and reception. Install them using + * `ngtcp2_conn_set_initial_key`. |dcid| is the destination + * connection ID which client generated randomly. It is used to + * derive initial packet protection keys. + * + * The callback function must return 0 if it succeeds. If an error + * occurs, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the + * library call return immediately. + * + * TODO: Define error code for TLS stack failure. Suggestion: + * NGTCP2_ERR_CRYPTO. + */ +typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, + const ngtcp2_cid *dcid, + void *user_data); + +/** + * @enum + * + * ngtcp2_crypto_level is encryption level. + */ +#if defined(__cplusplus) && __cplusplus >= 201103L +typedef enum ngtcp2_crypto_level : int { +#else +typedef enum ngtcp2_crypto_level { +#endif + /** + * NGTCP2_CRYPTO_LEVEL_INITIAL is Initial Keys encryption level. + */ + NGTCP2_CRYPTO_LEVEL_INITIAL, + /** + * NGTCP2_CRYPTO_LEVEL_HANDSHAKE is Handshake Keys encryption level. + */ + NGTCP2_CRYPTO_LEVEL_HANDSHAKE, + /** + * NGTCP2_CRYPTO_LEVEL_APP is Application Data (1-RTT) Keys + * encryption level. + */ + NGTCP2_CRYPTO_LEVEL_APP, + /** + * NGTCP2_CRYPTO_LEVEL_EARLY is Early Data (0-RTT) Keys encryption + * level. + */ + NGTCP2_CRYPTO_LEVEL_EARLY +} ngtcp2_crypto_level; + +/** + * @functypedef + * + * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is + * received. The received data is pointed to by |data|, and its length + * is |datalen|. The |offset| specifies the offset where |data| is + * positioned. |user_data| is the arbitrary pointer passed to + * `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. The ngtcp2 + * library ensures that the crypto data is passed to the application + * in the increasing order of |offset|. |datalen| is always strictly + * greater than 0. |crypto_level| indicates the encryption level + * where this data is received. Crypto data can never be received in + * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY`. + * + * The application should provide the given data to TLS stack. + * + * The callback function must return 0 if it succeeds. If TLS stack + * reported error, return :enum:`NGTCP2_ERR_CRYPTO`. If application + * encounters fatal error, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` + * which makes the library call return immediately. If the other + * value is returned, it is treated as + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_handshake_completed` is invoked when QUIC + * cryptographic handshake has completed. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC + * cryptographic handshake is confirmed. The handshake confirmation + * means that both endpoints agree that handshake has finished. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_version_negotiation` is invoked when Version + * Negotiation packet is received. |hd| is the pointer to the QUIC + * packet header object. The vector |sv| of |nsv| elements contains + * the QUIC version the server supports. Since Version Negotiation is + * only sent by server, this callback function is used by client only. + * + * The callback function must return 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received. + * This callback is client only. + * + * Application must regenerate packet protection key, IV, and header + * protection key for Initial packets using the destination connection + * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling + * `ngtcp2_conn_install_initial_key()`. + * + * 0-RTT data accepted by the ngtcp2 library will be retransmitted by + * the library automatically. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + const ngtcp2_pkt_retry *retry, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the + * application to encrypt packet payload. The packet payload to + * encrypt is passed as |plaintext| of length |plaintextlen|. The + * encryption key is passed as |key|. The nonce is passed as |nonce| + * of length |noncelen|. The ad, Additional Data to AEAD, is passed + * as |ad| of length |adlen|. + * + * The implementation of this callback must encrypt |plaintext| using + * the negotiated cipher suite and write the ciphertext into the + * buffer pointed by |dest|. |dest| has enough capacity to store the + * ciphertext. + * + * |dest| and |plaintext| may point to the same buffer. + * + * The callback function must return 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen); + +/** + * @functypedef + * + * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the + * application to decrypt packet payload. The packet payload to + * decrypt is passed as |ciphertext| of length |ciphertextlen|. The + * decryption key is passed as |key| of length |keylen|. The nonce is + * passed as |nonce| of length |noncelen|. The ad, Additional Data to + * AEAD, is passed as |ad| of length |adlen|. + * + * The implementation of this callback must decrypt |ciphertext| using + * the negotiated cipher suite and write the ciphertext into the + * buffer pointed by |dest|. |dest| has enough capacity to store the + * cleartext. + * + * |dest| and |ciphertext| may point to the same buffer. + * + * The callback function must return 0 if it succeeds. If TLS stack + * fails to decrypt data, return :enum:`NGTCP2_ERR_TLS_DECRYPT`. For + * any other errors, return :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which + * makes the library call return immediately. + */ +typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *key, const uint8_t *nonce, + size_t noncelen, const uint8_t *ad, size_t adlen); + +/** + * @functypedef + * + * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the + * application to produce mask to encrypt or decrypt packet header. + * The key is passed as |hp_key|. The sample is passed as |sample|. + * + * The implementation of this callback must produce a mask using the + * header protection cipher suite specified by QUIC specification and + * write the result into the buffer pointed by |dest|. The length of + * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that + * |dest| has enough capacity. + * + * The callback function must return 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key, const uint8_t *sample); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stream_data` is invoked when stream data is + * received. The stream is specified by |stream_id|. If |fin| is + * nonzero, this portion of the data is the last data in this stream. + * |offset| is the offset where this data begins. The library ensures + * that data is passed to the application in the non-decreasing order + * of |offset|. The data is passed as |data| of length |datalen|. + * |datalen| may be 0 if and only if |fin| is nonzero. + * + * The callback function must return 0 if it succeeds, or + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, int64_t stream_id, + int fin, uint64_t offset, + const uint8_t *data, size_t datalen, + void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_open` is a callback function which is called + * when remote stream is opened by peer. This function is not called + * if stream is opened by implicitly (we might reconsider this + * behaviour). + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_close` is invoked when a stream is closed. + * This callback is not called when QUIC connection is closed before + * existing streams are closed. |app_error_code| indicates the error + * code of this closure. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_reset` is invoked when a stream identified by + * |stream_id| is reset by a remote endpoint. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_acked_stream_data_offset` is a callback function + * which is called when stream data is acked, and application can free + * the data. The acked range of data is [offset, offset + datalen). + * For a given stream_id, this callback is called sequentially in + * increasing order of |offset|. |datalen| is normally strictly + * greater than 0. One exception is that when a packet which includes + * STREAM frame which has fin flag set, and 0 length data, this + * callback is invoked with 0 passed as |datalen|. + * + * If a stream is closed prematurely and stream data is still + * in-flight, this callback function is not called for those data. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_acked_stream_data_offset)(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t offset, size_t datalen, + void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_acked_crypto_offset` is a callback function which is + * called when crypto stream data is acknowledged, and application can + * free the data. |crypto_level| indicates the encryption level where + * this data was sent. Crypto data never be sent in + * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY`. This works like + * :type:`ngtcp2_acked_stream_data_offset` but crypto stream has no + * stream_id and stream_user_data, and |datalen| never become 0. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, size_t datalen, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stateless_reset` is a callback function which is + * called when Stateless Reset packet is received. The stateless + * reset details are given in |sr|. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_recv_stateless_reset)(ngtcp2_conn *conn, + const ngtcp2_pkt_stateless_reset *sr, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_extend_max_streams` is a callback function which is + * called every time max stream ID is strictly extended. + * |max_streams| is the cumulative number of streams which an endpoint + * can open. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_extend_max_streams)(ngtcp2_conn *conn, + uint64_t max_streams, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_extend_max_stream_data` is a callback function which + * is invoked when max stream data is extended. |stream_id| + * identifies the stream. |max_data| is a cumulative number of bytes + * the endpoint can send on this stream. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_rand` is a callback function to get randomized byte + * string from application. Application must fill random |destlen| + * bytes to the buffer pointed by |dest|. |ctx| provides the context + * how the provided random byte string is used. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_rand)(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + ngtcp2_rand_ctx ctx, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_get_new_connection_id` is a callback function to ask + * an application for new connection ID. Application must generate + * new unused connection ID with the exact |cidlen| bytes and store it + * in |cid|. It also has to generate stateless reset token into + * |token|. The length of stateless reset token is + * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that + * the buffer pointed by |cid| has the sufficient space to store the + * token. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_get_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_remove_connection_id` is a callback function which + * notifies the application that connection ID |cid| is no longer used + * by remote endpoint. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn, + const ngtcp2_cid *cid, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_update_key` is a callback function which tells the + * application that it must generate new packet protection keying + * materials. The current set of secrets are given as + * |current_rx_secret| and |current_tx_secret| of length |secretlen|. + * They are decryption and encryption secrets respectively. + * + * The application has to generate new secrets and keys for both + * encryption and decryption, and write decryption secret, key and IV + * to the buffer pointed by |rx_secret|, |rx_key| and |rx_iv| + * respectively. Similarly, write encryption secret, key and IV to + * the buffer pointed by |tx_secret|, |tx_key| and |tx_iv|. All given + * buffers have the enough capacity to store secret, key and IV. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_update_key)(ngtcp2_conn *conn, uint8_t *rx_secret, + uint8_t *tx_secret, uint8_t *rx_key, + uint8_t *rx_iv, uint8_t *tx_key, + uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, + size_t secretlen, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_path_validation` is a callback function which tells + * the application the outcome of path validation. |path| is the path + * that was validated. If |res| is + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, the path validation + * succeeded. If |res| is + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, the path validation + * failed. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_path_validation_result res, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_select_preferred_addr` is a callback function which + * asks a client application to choose server address from preferred + * addresses |paddr| received from server. An application should + * write preferred address in |dest|. If an application denies the + * preferred addresses, just leave |dest| unmodified (or set dest->len + * to 0) and return 0. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, + ngtcp2_addr *dest, + const ngtcp2_preferred_addr *paddr, + void *user_data); + +typedef enum ngtcp2_connection_id_status_type { + /* NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE indicates that a local + endpoint starts using new destination Connection ID. */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + /* NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE indicates that a + local endpoint stops using a given destination Connection ID. */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE +} ngtcp2_connection_id_status_type; + +/** + * @functypedef + * + * :type:`ngtcp2_connection_id_status` is a callback function which is + * called when the status of Connection ID changes. + * + * |token| is the associated stateless reset token and it is NULL if + * no token is present. + * + * |type| is the one of the value defined in + * :enum:`ngtcp2_connection_id_status_type`. The new value might be + * added in the future release. + * + * The callback function must return 0 if it succeeds. Returning + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token, + void *user_data); + +typedef struct ngtcp2_conn_callbacks { + /** + * client_initial is a callback function which is invoked when + * client asks TLS stack to produce first TLS cryptographic + * handshake message. This callback function must be specified. + */ + ngtcp2_client_initial client_initial; + /** + * recv_client_initial is a callback function which is invoked when + * a server receives the first packet from client. This callback + * function must be specified. + */ + ngtcp2_recv_client_initial recv_client_initial; + /** + * recv_crypto_data is a callback function which is invoked when + * cryptographic data (CRYPTO frame, in other words, TLS message) is + * received. This callback function must be specified. + */ + ngtcp2_recv_crypto_data recv_crypto_data; + /** + * handshake_completed is a callback function which is invoked when + * QUIC cryptographic handshake has completed. This callback + * function is optional. + */ + ngtcp2_handshake_completed handshake_completed; + /** + * recv_version_negotiation is a callback function which is invoked + * when Version Negotiation packet is received by a client. This + * callback function is optional. + */ + ngtcp2_recv_version_negotiation recv_version_negotiation; + /** + * encrypt is a callback function which is invoked to encrypt a QUIC + * packet. This callback function must be specified. + */ + ngtcp2_encrypt encrypt; + /** + * decrypt is a callback function which is invoked to decrypt a QUIC + * packet. This callback function must be specified. + */ + ngtcp2_decrypt decrypt; + /** + * hp_mask is a callback function which is invoked to get a mask to + * encrypt or decrypt packet header. This callback function must be + * specified. + */ + ngtcp2_hp_mask hp_mask; + /** + * recv_stream_data is a callback function which is invoked when + * STREAM data, which includes application data, is received. This + * callback function is optional. + */ + ngtcp2_recv_stream_data recv_stream_data; + /** + * acked_crypto_offset is a callback function which is invoked when + * CRYPTO data is acknowledged by a remote endpoint. It tells an + * application the largest offset of acknowledged CRYPTO data + * without a gap so that the application can free memory for the + * data. This callback function is optional. + */ + ngtcp2_acked_crypto_offset acked_crypto_offset; + /** + * acked_stream_data_offset is a callback function which is invoked + * when STREAM data, which includes application data, is + * acknowledged by a remote endpoint. It tells an application the + * largest offset of acknowledged STREAM data without a gap so that + * application can free memory for the data. This callback function + * is optional. + */ + ngtcp2_acked_stream_data_offset acked_stream_data_offset; + /** + * stream_open is a callback function which is invoked when new + * remote stream is opened by a remote endpoint. This callback + * function is optional. + */ + ngtcp2_stream_open stream_open; + /** + * stream_close is a callback function which is invoked when a + * stream is closed. This callback function is optional. + */ + ngtcp2_stream_close stream_close; + /** + * recv_stateless_reset is a callback function which is invoked when + * Stateless Reset packet is received. This callback function is + * optional. + */ + ngtcp2_recv_stateless_reset recv_stateless_reset; + /** + * recv_retry is a callback function which is invoked when a client + * receives Retry packet. For client, this callback function must + * be specified. Server never receive Retry packet. + */ + ngtcp2_recv_retry recv_retry; + /** + * extend_max_local_streams_bidi is a callback function which is + * invoked when the number of bidirectional stream which a local + * endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_local_streams_bidi; + /** + * extend_max_local_streams_uni is a callback function which is + * invoked when the number of unidirectional stream which a local + * endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_local_streams_uni; + /** + * rand is a callback function which is invoked when the library + * needs unpredictable sequence of random data. This callback + * function must be specified. + */ + ngtcp2_rand rand; + /** + * get_new_connection_id is a callback function which is invoked + * when the library needs new connection ID. This callback function + * must be specified. + */ + ngtcp2_get_new_connection_id get_new_connection_id; + /** + * remove_connection_id is a callback function which notifies an + * application that connection ID is no longer used by a remote + * endpoint. This callback function is optional. + */ + ngtcp2_remove_connection_id remove_connection_id; + /** + * update_key is a callback function which is invoked when the + * library tells an application that it must update keying materials + * and install new keys. This function must be specified. + */ + ngtcp2_update_key update_key; + /** + * path_validation is a callback function which is invoked when path + * validation completed. This function is optional. + */ + ngtcp2_path_validation path_validation; + /** + * select_preferred_addr is a callback function which is invoked + * when the library asks a client to select preferred address + * presented by a server. This function is optional. + */ + ngtcp2_select_preferred_addr select_preferred_addr; + /** + * stream_reset is a callback function which is invoked when a + * stream is reset by a remote endpoint. This callback function is + * optional. + */ + ngtcp2_stream_reset stream_reset; + /** + * extend_max_remote_streams_bidi is a callback function which is + * invoked when the number of bidirectional streams which a remote + * endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_remote_streams_bidi; + /** + * extend_max_remote_streams_uni is a callback function which is + * invoked when the number of unidirectional streams which a remote + * endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_remote_streams_uni; + /** + * extend_max_stream_data is callback function which is invoked when + * the maximum offset of STREAM data that a local endpoint can send + * is increased. This callback function is optional. + */ + ngtcp2_extend_max_stream_data extend_max_stream_data; + /** + * dcid_status is a callback function which is invoked when the new + * destination Connection ID is activated or the activated + * destination Connection ID is now deactivated. + */ + ngtcp2_connection_id_status dcid_status; + /** + * handshake_confirmed is a callback function which is invoked when + * both endpoints agree that handshake has finished. This field is + * ignored by server because handshake_completed indicates the + * handshake confirmation for server. + */ + ngtcp2_handshake_confirmed handshake_confirmed; +} ngtcp2_conn_callbacks; + +/** + * @function + * + * `ngtcp2_pkt_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE frame with the given |error_code| to + * the buffer pointed by |dest| of length |destlen|. All encryption + * parameters are for Initial packet encryption. The packet number is + * always 0. + * + * The primary use case of this function is for server to send + * CONNECTION_CLOSE frame in Initial packet to close connection + * without committing the state when validating Retry token fails. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key); + +/** + * @function + * + * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed + * by |dest| whose length is |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, + size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_accept` is used by server implementation, and decides + * whether packet |pkt| of length |pktlen| is acceptable for initial + * packet from client. + * + * If it is acceptable, it returns 0. If it is not acceptable, and + * Version Negotiation packet is required to send, it returns 1. + * Otherwise, it returns -1. + * + * If |dest| is not NULL, and the return value is 0 or 1, the decoded + * packet header is stored to the object pointed by |dest|. + */ +NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen); + +/** + * @function + * + * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and + * initializes it as client. |dcid| is randomized destination + * connection ID. |scid| is source connection ID. |version| is a + * QUIC version to use. |path| is the network path where this QUIC + * connection is being established and must not be NULL. |callbacks|, + * and |settings| must not be NULL, and the function make a copy of + * each of them. |user_data| is the arbitrary pointer which is passed + * to the user-defined callback functions. If |mem| is NULL, the + * memory allocator returned by `ngtcp2_mem_default()` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int +ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_conn_callbacks *callbacks, + const ngtcp2_settings *settings, const ngtcp2_mem *mem, + void *user_data); + +/** + * @function + * + * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and + * initializes it as server. |dcid| is a destination connection ID. + * |scid| is a source connection ID. |path| is the network path where + * this QUIC connection is being established and must not be NULL. + * |version| is a QUIC version to use. |callbacks|, and |settings| + * must not be NULL, and the function make a copy of each of them. + * |user_data| is the arbitrary pointer which is passed to the + * user-defined callback functions. If |mem| is NULL, the memory + * allocator returned by `ngtcp2_mem_default()` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int +ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_conn_callbacks *callbacks, + const ngtcp2_settings *settings, const ngtcp2_mem *mem, + void *user_data); + +/** + * @function + * + * `ngtcp2_conn_del` frees resources allocated for |conn|. It also + * frees memory pointed by |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of + * length |pktlen| and processes it. |path| is the network path the + * packet is delivered and must not be NULL. This function performs + * QUIC handshake as well. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or negative error codes. + * In general, if the error code which satisfies + * ngtcp2_erro_is_fatal(err) != 0 is returned, the application should + * just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. If :enum:`NGTCP2_ERR_RETRY` is returned, + * application must be a server and it must perform address validation + * by sending Retry packet and close the connection. + */ +NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, + const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_pkt` is equivalent to calling + * `ngtcp2_conn_writev_stream` without specifying stream data and + * :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, + ngtcp2_path *path, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC + * handshake has completed. + */ +NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake + * has completed. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_install_initial_key` installs packet protection keying + * materials for Initial packets. |rx_key| of length |keylen|, IV + * |rx_iv| of length |rx_ivlen|, and packet header protection key + * |rx_hp_key| of length |keylen| to decrypt incoming Initial packets. + * Similarly, |tx_key|, |tx_iv| and |tx_hp_key| are for encrypt + * outgoing packets and are the same length with the rx counterpart . + * If they have already been set, they are overwritten. + * + * After receiving Retry packet, the DCID most likely changes. In + * that case, client application must generate these keying materials + * again based on new DCID and install them again. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( + ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_handshake_key` installs packet protection + * keying materials for Handshake packets. |rx_key| of length + * |keylen|, IV |rx_iv| of length |rx_ivlen|, and packet header + * protection key |rx_hp_key| of length |keylen| to decrypt incoming + * Handshake packets. Similarly, |tx_key|, |tx_iv| and |tx_hp_key| + * are for encrypt outgoing packets and are the same length with the + * rx counterpart. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_handshake_key( + ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_set_aead_overhead` tells the ngtcp2 library the length + * of AEAD tag which the negotiated cipher suites defines. This + * function must be called before encrypting or decrypting the + * incoming packets other than Initial packets. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, + size_t aead_overhead); + +/** + * @function + * + * `ngtcp2_conn_get_aead_overhead` returns the aead overhead passed to + * `ngtcp2_conn_set_aead_overhead`. If `ngtcp2_conn_set_aead_overhead` hasn't + * been called yet this function returns 0. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_aead_overhead(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_install_early_key` installs packet protection key + * |key| of length |keylen|, IV |iv| of length |ivlen|, and packet + * header protection key |hp_key| of length |keylen| to encrypt (for + * client)or decrypt (for server) 0RTT packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, + const uint8_t *key, + const uint8_t *iv, + const uint8_t *hp_key, + size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_key` installs packet protection keying + * materials for Short packets. |rx_secret| of length |secretlen| is + * the decryption secret. |rx_key| of length |keylen|, IV |rx_iv| of + * length |rx_ivlen|, and packet header protection key |rx_hp_key| of + * length |keylen| to decrypt incoming Short packets. Similarly, + * |tx_secret| of length |secretlen| is the encryption secret. + * |tx_key|, |tx_iv| and |tx_hp_key| are used to encrypt outgoing + * packets and are the same length with the rx counterpart. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_key( + ngtcp2_conn *conn, const uint8_t *rx_secret, const uint8_t *tx_secret, + const uint8_t *rx_key, const uint8_t *rx_iv, const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, const uint8_t *tx_hp_key, + size_t secretlen, size_t keylen, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_initiate_key_update` initiates the key update. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_INVALID_STATE` + * The previous key update has not been confirmed yet; or key + * update is too frequent; or new keys are not available yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_set_tls_error` sets the TLS related error in |conn|. + * In general, error code should be propagated via return value, but + * sometimes ngtcp2 API is called inside callback function of TLS + * stack and it does not allow to return ngtcp2 error code directly. + * In this case, implementation can set the error code (e.g., + * NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM) using this function. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr); + +/** + * @function + * + * `ngtcp2_conn_get_tls_error` returns the value set by + * `ngtcp2_conn_set_tls_error`. If no value is set, this function + * returns 0. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point + * of loss detection timer. Application should call + * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt` + * (or `ngtcp2_conn_writev_stream`) when it expires. It returns + * UINT64_MAX if loss detection timer is not armed. + */ +NGTCP2_EXTERN ngtcp2_tstamp +ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of + * delayed protected ACK. Application should call + * ngtcp2_conn_cancel_expired_ack_delay_timer() and + * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it + * expires. It returns UINT64_MAX if there is no expiry. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_expiry` returns the next expiry time. This + * function returns the timestamp such that + * min(ngtcp2_conn_loss_detection_expiry(conn), + * ngtcp2_conn_ack_delay_expiry(conn), other timers in |conn|). + * + * Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or + * `ngtcp2_conn_writev_stream`) if expiry time is passed. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_handle_expiry` handles expired timer. It do nothing + * if timer is not expired. + */ +NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK + * delay timer. |ts| is the current time. This function must be + * called when ngtcp2_conn_ack_delay_expiry() <= ts. + */ +NGTCP2_EXTERN void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_idle_expiry` returns the time when a connection + * should be closed if it continues to be idle. If idle timeout is + * disabled, this function returns UINT64_MAX. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO). + */ +NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_remote_transport_params` sets transport parameter + * |params| to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_PROTO` + * If |conn| is server, and negotiated_version field is not the + * same as the used version. + */ +NGTCP2_EXTERN int +ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_get_remote_transport_params` fills settings values in + * |params|. original_connection_id and + * original_connection_id_present are always zero filled. + */ +NGTCP2_EXTERN void +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as + * transport parameter previously received from a server. The + * parameters are used to send 0-RTT data. QUIC requires that client + * application should remember transport parameter as well as session + * ticket. + * + * At least following fields must be set: + * + * * initial_max_stream_id_bidi + * * initial_max_stream_id_uni + * * initial_max_stream_data_bidi_local + * * initial_max_stream_data_bidi_remote + * * initial_max_stream_data_uni + * * initial_max_data + */ +NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_get_local_transport_params` fills settings values in + * |params|. + */ +NGTCP2_EXTERN void +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream. The + * |stream_user_data| is the user data specific to the stream. The + * open stream ID is stored in |*pstream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_ID_BLOCKED` + * The remote peer does not allow |stream_id| yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, + int64_t *pstream_id, + void *stream_user_data); + +/** + * @function + * + * `ngtcp2_conn_open_uni_stream` opens new unidirectional stream. The + * |stream_user_data| is the user data specific to the stream. The + * open stream ID is stored in |*pstream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_ID_BLOCKED` + * The remote peer does not allow |stream_id| yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, + int64_t *pstream_id, + void *stream_user_data); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream` closes stream denoted by |stream_id| + * abruptly. |app_error_code| is one of application error codes, and + * indicates the reason of shutdown. Successful call of this function + * does not immediately erase the state of the stream. The actual + * deletion is done when the remote endpoint sends acknowledgement. + * Calling this function is equivalent to call + * `ngtcp2_conn_shutdown_stream_read`, and + * `ngtcp2_conn_shutdown_stream_write` sequentially. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_write` closes write-side of stream + * denoted by |stream_id| abruptly. |app_error_code| is one of + * application error codes, and indicates the reason of shutdown. If + * this function succeeds, no application data is sent to the remote + * endpoint. It discards all data which has not been acknowledged + * yet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_read` closes read-side of stream + * denoted by |stream_id| abruptly. |app_error_code| is one of + * application error codes, and indicates the reason of shutdown. If + * this function succeeds, no application data is forwarded to an + * application layer. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @enum + * + * ngtcp2_write_stream_flag defines extra behaviour for + * `ngtcp2_conn_writev_stream()`. + */ +typedef enum ngtcp2_write_stream_flag { + NGTCP2_WRITE_STREAM_FLAG_NONE = 0x00, + /** + * NGTCP2_WRITE_STREAM_FLAG_MORE indicates that more stream data may + * come and should be coalesced into the same packet if possible. + */ + NGTCP2_WRITE_STREAM_FLAG_MORE = 0x01 +} ngtcp2_write_stream_flag; + +/** + * @function + * + * `ngtcp2_conn_write_stream` is just like + * `ngtcp2_conn_writev_stream`. The only difference is that it + * conveniently accepts a single buffer. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( + ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, + ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, int fin, + const uint8_t *data, size_t datalen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_writev_stream` writes a packet containing stream data + * of stream denoted by |stream_id|. The buffer of the packet is + * pointed by |dest| of length |destlen|. This function performs QUIC + * handshake as well. + * + * Specifying -1 to |stream_id| means no new stream data to send. + * + * If |path| is not NULL, this function stores the network path with + * which the packet should be sent. Each addr field must point to the + * buffer which is at least 128 bytes. ``sizeof(struct + * sockaddr_storage)`` is enough. The assignment might not be done if + * nothing is written to |dest|. + * + * If the all given data is encoded as STREAM frame in |dest|, and if + * |fin| is nonzero, fin flag is set in outgoing STREAM frame. + * Otherwise, fin flag in STREAM frame is not set. + * + * This packet may contain frames other than STREAM frame. The packet + * might not contain STREAM frame if other frames occupy the packet. + * In that case, |*pdatalen| would be -1 if |pdatalen| is not NULL. + * + * If |fin| is nonzero, and 0 length STREAM frame is successfully + * serialized, |*pdatalen| would be 0. + * + * The number of data encoded in STREAM frame is stored in |*pdatalen| + * if it is not NULL. The caller must keep the portion of data + * covered by |*pdatalen| bytes in tact until + * :type:`ngtcp2_acked_stream_data_offset` indicates that they are + * acknowledged by a remote endpoint or the stream is closed. + * + * If |flags| equals to :enum:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this + * function produces a single payload of UDP packet. If the given + * stream data is small (e.g., few bytes), the packet might be + * severely under filled. Too many small packet might increase + * overall packet processing costs. Unless there are retransmissions, + * by default, application can only send 1 STREAM frame in one QUIC + * packet. In order to include more than 1 STREAM frame in one QUIC + * packet, specify :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. + * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the + * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * outcomes: + * + * - The function returns the written length of packet just like + * without :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because + * packet is nearly full and the library decided to make a complete + * packet. + * + * - The function returns :enum:`NGTCP2_ERR_WRITE_STREAM_MORE`. This + * indicates that application can call this function with different + * stream data to pack them into the same packet. Application has + * to specify the same |conn|, |path|, |dest|, |destlen|, + * |pdatalen|, and |ts| parameters, otherwise the behaviour is + * undefined. The application can change |flags|. + * + * - The function returns :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` which + * indicates that stream is blocked because of flow control. + * + * - The other error might be returned just like without + * :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE`. + * + * When application sees :enum:`NGTCP2_ERR_WRITE_STREAM_MORE`, it must + * not call other ngtcp2 API functions (application can still call + * `ngtcp2_conn_write_connection_close` or + * `ngtcp2_conn_write_application_close` to handle error from this + * function). Just keep calling `ngtcp2_conn_writev_stream` or + * `ngtcp2_conn_write_pkt` until it returns a positive number (which + * indicates a complete packet is ready). + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + * :enum:`NGTCP2_ERR_STREAM_SHUT_WR` + * Stream is half closed (local); or stream is being reset. + * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + * :enum:`NGTCP2_ERR_STREAM_DATA_BLOCKED` + * Stream is blocked because of flow control. + * :enum:`NGTCP2_ERR_WRITE_STREAM_MORE` + * (Only when :enum:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified) + * Application can call this function to pack more stream data + * into the same packet. See above to know how it works. + * + * In general, if the error code which satisfies + * ngtcp2_err_is_fatal(err) != 0 is returned, the application should + * just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( + ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, + ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, int fin, + const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * a CONNECTION_CLOSE frame in the buffer pointed by |dest| whose + * capacity is |datalen|. + * + * If |path| is not NULL, this function stores the network path with + * which the packet should be sent. Each addr field must point to the + * buffer which is at least 128 bytes. ``sizeof(struct + * sockaddr_storage)`` is enough. The assignment might not be done if + * nothing is written to |dest|. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :enum:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close( + ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, + uint64_t error_code, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close` writes a packet which + * contains a APPLICATION_CLOSE frame in the buffer pointed by |dest| + * whose capacity is |datalen|. + * + * If |path| is not NULL, this function stores the network path with + * which the packet should be sent. Each addr field must point to the + * buffer which is at least 128 bytes. ``sizeof(struct + * sockaddr_storage)`` is enough. The assignment might not be done if + * nothing is written to |dest|. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :enum:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending APPLICATION_CLOSE. + * :enum:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :enum:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close( + ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen, + uint64_t app_error_code, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in + * closing period. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in + * draining period. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_extend_max_stream_offset` extends stream's max stream + * data value by |datalen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream was not found + */ +NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, + int64_t stream_id, + size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_offset` extends max data offset by + * |datalen|. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, + size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum + * local bidirectional streams that a remote endpoint can open by |n|. + * + * The library does not increase maximum stream limit automatically. + * The exception is when a stream is closed without + * :type:`ngtcp2_stream_open` callback being called. In this case, + * stream limit is increased automatically. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, + size_t n); + +/** + * @function + * + * `ngtcp2_conn_extend_max_streams_uni` extends the number of maximum + * local unidirectional streams that a remote endpoint can open by + * |n|. + * + * The library does not increase maximum stream limit automatically. + * The exception is when a stream is closed without + * :type:`ngtcp2_stream_open` callback being called. In this case, + * stream limit is increased automatically. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, + size_t n); + +/** + * @function + * + * `ngtcp2_conn_get_bytes_in_flight` returns the number of bytes which + * is the sum of outgoing QUIC packet length in flight. This does not + * include a packet which only includes ACK frames. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_bytes_in_flight(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to destination + * connection ID. If no destination connection ID is present, the + * return value is not ``NULL``, and its datalen field is 0. + */ +NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_num_scid` returns the number of source connection + * IDs which the local endpoint has provided to the peer and have not + * retired. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_scid` writes the all source connection IDs which + * the local endpoint has provided to the peer and have not retired in + * |dest|. The buffer pointed by |dest| must have + * ``sizeof(ngtcp2_cid) * n`` bytes available, where n is the return + * value of `ngtcp2_conn_get_num_scid()`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest); + +/** + * @function + * + * `ngtcp2_conn_get_num_active_dcid` returns the number of the active + * destination connection ID. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn); + +/** + * @struct + * + * :type:`ngtcp2_cid_token` is the convenient struct to store + * Connection ID, its associated path, and stateless reset token. + */ +typedef struct ngtcp2_cid_token { + /* seq is the sequence number of this Connection ID. */ + uint64_t seq; + /* cid is Connection ID. */ + ngtcp2_cid cid; + /* ps is the path which is associated to this Connection ID. */ + ngtcp2_path_storage ps; + /* token is the stateless reset token for this Connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /* token_resent is nonzero if token contains stateless reset + token. */ + uint8_t token_present; +} ngtcp2_cid_token; + +/** + * @function + * + * `ngtcp2_conn_get_active_dcid` writes the all active destination + * connection IDs and tokens to |dest|. The buffer pointed by |dest| + * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n + * is the return value of `ngtcp2_conn_get_num_active_dcid()`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, + ngtcp2_cid_token *dest); + +/** + * @function + * + * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + */ +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was + * rejected by a server. + */ +NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_rcvry_stat` returns a pointer to the object which + * stores recovery information. + */ +NGTCP2_EXTERN const ngtcp2_rcvry_stat * +ngtcp2_conn_get_rcvry_stat(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_cc_stat` returns a pointer to the object which + * stores congestion controller information. + */ +NGTCP2_EXTERN const ngtcp2_cc_stat *ngtcp2_conn_get_cc_stat(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer + * returned from `ngtcp2_conn_earliest_expiry` fires. + * + * Application should call `ngtcp2_conn_handshake` if handshake has + * not completed, otherwise `ngtcp2_conn_write_pkt` (or + * `ngtcp2_conn_write_stream` if it has data to send) to send TLP/RTO + * probe packets. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_submit_crypto_data` submits crypto stream data |data| + * of length |datalen| to the library for transmission. The + * encryption level is given in |crypto_level|. + * + * Application should keep the buffer pointed by |data| alive until + * the data is acknowledged. The acknowledgement is notified by + * :type:`ngtcp2_acked_crypto_offset` callback. + */ +NGTCP2_EXTERN int +ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, const size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to + * |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, + const ngtcp2_addr *addr); + +/** + * @function + * + * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr| + * to |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, + const ngtcp2_addr *addr); + +/** + * @function + * + * `ngtcp2_conn_get_remote_addr` returns the remote endpoint address + * set in |conn|. + */ +NGTCP2_EXTERN const ngtcp2_addr *ngtcp2_conn_get_remote_addr(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_initiate_migration` starts connection migration to the + * given |path| which must not be NULL. Only client can initiate + * migration. This function does immediate migration; it does not + * probe peer reachability from a new local address. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_ERR_INVALID_STATE` + * Migration is disabled. + * :enum:`NGTCP2_ERR_CONN_ID_BLOCKED` + * No unused connection ID is available. + * :enum:`NGTCP2_ERR_INVALID_ARGUMENT` + * |path| equals the current path. + * :enum:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_max_local_streams_uni` returns the cumulative + * number of streams which local endpoint can open. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_max_data_left` returns the number of bytes that + * this local endpoint can send in this connection. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for Initial packet + * encryption. The passed data will be passed to + * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and + * :type:`ngtcp2_hp_mask` callbacks. + */ +NGTCP2_EXTERN void +ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_initial_crypto_ctx` returns + * :type:`ngtcp2_crypto_ctx` object for Initial packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for + * 0RTT/Handshake/Short packet encryption. In other words, this + * crypto context is used for all packets except for Initial packets. + * The passed data will be passed to :type:`ngtcp2_encrypt`, + * :type:`ngtcp2_decrypt` and :type:`ngtcp2_hp_mask` callbacks. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_set_retry_aead` sets |aead| for Retry integrity tag + * verification. It must be AEAD_AES_128_GCM. This function must be + * called if |conn| is initialized as client. Server does not verify + * the tag and has no need to call this function. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` + * object for 0RTT/Handshake/Short packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); + +typedef enum ngtcp2_connection_close_error_code_type { + /* NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT indicates the + error code is QUIC transport error code. */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + /* NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION indicates the + error code is application error code. */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, +} ngtcp2_connection_close_error_code_type; + +typedef struct ngtcp2_connection_close_error_code { + /* error_code is the error code for connection closure. */ + uint64_t error_code; + /* type is the type of error_code. */ + ngtcp2_connection_close_error_code_type type; +} ngtcp2_connection_close_error_code; + +/** + * @function + * + * `ngtcp2_conn_get_connection_close_error_code` stores the received + * connection close error code in |ccec|. + */ +NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code( + ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec); + +/** + * @function + * + * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| denotes the + * stream which a local endpoint issues. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `ngtcp2_strerror` returns the text representation of |liberr|. + */ +NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); + +/** + * @function + * + * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. + */ +NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); + +/** + * @function + * + * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC + * transport error code which corresponds to |liberr|. + */ +NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); + +/** + * @function + * + * `ngtcp2_addr_init` initializes |dest| with the given arguments and + * returns |dest|. + */ +NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const void *addr, + size_t addrlen, void *user_data); + +/** + * @function + * + * `ngtcp2_path_storage_init` initializes |ps| with the given + * arguments. This function copies |local_addr| and |remote_addr|. + */ +NGTCP2_EXTERN void +ngtcp2_path_storage_init(ngtcp2_path_storage *ps, const void *local_addr, + size_t local_addrlen, void *local_user_data, + const void *remote_addr, size_t remote_addrlen, + void *remote_user_data); + +/** + * @function + * + * `ngtcp2_path_storage_zero` initializes |ps| with the zero length + * addresses. + */ +NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); + +/** + * @function + * + * `ngtcp2_settings_default` initializes |settings| with the default + * values. First this function fills |settings| with 0 and set the + * default value to the following fields: + * + * * max_packet_size = NGTCP2_MAX_PKT_SIZE + * * ack_delay_component = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT + * * max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY + */ +NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); + +/* + * @function + * + * `ngtcp2_mem_default` returns the default, system standard memory + * allocator. + */ +NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void); + +/** + * @macro + * + * The age of :type:`ngtcp2_info` + */ +#define NGTCP2_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `ngtcp2_version()` returns. It holds + * information about the particular ngtcp2 version. + */ +typedef struct ngtcp2_info { + /** + * Age of this struct. This instance of ngtcp2 sets it to + * :macro:`NGTCP2_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGTCP2_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGTCP2_VERSION` string (since age ==1) + */ + const char *version_str; + /* -------- the above fields all exist when age == 1 */ +} ngtcp2_info; + +/** + * @function + * + * Returns a pointer to a ngtcp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); + +/** + * @function + * + * `ngtcp2_is_bidi_stream` returns nonzero if |stream_id| denotes + * bidirectional stream. + */ +NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/lib/includes/ngtcp2/version.h new file mode 100644 index 00000000000000..85850725bfa9e2 --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/version.h @@ -0,0 +1,45 @@ +/* + * ngtcp2 + * + * Copyright (c) 2016 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VERSION_H +#define VERSION_H + +/** + * @macro + * + * Version number of the ngtcp2 library release. + */ +#define NGTCP2_VERSION "0.1.90" + +/** + * @macro + * + * Numerical representation of the version number of the ngtcp2 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGTCP2_VERSION_NUM 0x00015a + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/includes/ngtcp2/version.h.in b/deps/ngtcp2/lib/includes/ngtcp2/version.h.in new file mode 100644 index 00000000000000..fc7459636dd140 --- /dev/null +++ b/deps/ngtcp2/lib/includes/ngtcp2/version.h.in @@ -0,0 +1,45 @@ +/* + * ngtcp2 + * + * Copyright (c) 2016 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VERSION_H +#define VERSION_H + +/** + * @macro + * + * Version number of the ngtcp2 library release. + */ +#define NGTCP2_VERSION "@PACKAGE_VERSION@" + +/** + * @macro + * + * Numerical representation of the version number of the ngtcp2 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGTCP2_VERSION_NUM @PACKAGE_VERSION_NUM@ + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/lib/ngtcp2_acktr.c new file mode 100644 index 00000000000000..e27d05c787d659 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_acktr.c @@ -0,0 +1,331 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_acktr.h" + +#include + +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { + *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); + if (*ent == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*ent)->pkt_num = pkt_num; + (*ent)->len = 1; + (*ent)->tstamp = tstamp; + + return 0; +} + +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *lhs->i > *rhs->i; +} + +int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, + const ngtcp2_mem *mem) { + int rv; + + rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), + mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); + if (rv != 0) { + ngtcp2_ringbuf_free(&acktr->acks); + return rv; + } + + acktr->log = log; + acktr->mem = mem; + acktr->flags = NGTCP2_ACKTR_FLAG_NONE; + acktr->first_unacked_ts = UINT64_MAX; + acktr->rx_npkt = 0; + + return 0; +} + +void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { + ngtcp2_ksl_it it; + + if (acktr == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); + } + ngtcp2_ksl_free(&acktr->ents); + + ngtcp2_ringbuf_free(&acktr->acks); +} + +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *ent, *prev_ent, *delent; + int rv; + int added = 0; + ngtcp2_ksl_key key, old_key; + + if (ngtcp2_ksl_len(&acktr->ents)) { + it = ngtcp2_ksl_lower_bound(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &pkt_num)); + if (ngtcp2_ksl_it_end(&it)) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + assert(ent->pkt_num >= pkt_num + (int64_t)ent->len); + + if (ent->pkt_num == pkt_num + (int64_t)ent->len) { + ++ent->len; + added = 1; + } + } else { + ent = ngtcp2_ksl_it_get(&it); + + assert(ent->pkt_num != pkt_num); + + if (ngtcp2_ksl_it_begin(&it)) { + if (ent->pkt_num + 1 == pkt_num) { + ngtcp2_ksl_update_key(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), + ngtcp2_ksl_key_ptr(&old_key, &pkt_num)); + ent->pkt_num = pkt_num; + ent->tstamp = ts; + ++ent->len; + added = 1; + } + } else { + ngtcp2_ksl_it_prev(&it); + prev_ent = ngtcp2_ksl_it_get(&it); + + assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len); + + if (ent->pkt_num + 1 == pkt_num) { + if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { + prev_ent->len += ent->len + 1; + ngtcp2_ksl_remove(&acktr->ents, NULL, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + ngtcp2_acktr_entry_del(ent, acktr->mem); + added = 1; + } else { + ngtcp2_ksl_update_key(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), + ngtcp2_ksl_key_ptr(&old_key, &pkt_num)); + ent->pkt_num = pkt_num; + ent->tstamp = ts; + ++ent->len; + added = 1; + } + } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { + ++prev_ent->len; + added = 1; + } + } + } + } + + if (!added) { + rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); + if (rv != 0) { + return rv; + } + rv = ngtcp2_ksl_insert(&acktr->ents, NULL, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num), ent); + if (rv != 0) { + ngtcp2_acktr_entry_del(ent, acktr->mem); + return rv; + } + } + + if (active_ack) { + acktr->flags |= NGTCP2_ACKTR_FLAG_ACTIVE_ACK; + if (acktr->first_unacked_ts == UINT64_MAX) { + acktr->first_unacked_ts = ts; + } + } + + if (ngtcp2_ksl_len(&acktr->ents) > NGTCP2_ACKTR_MAX_ENT) { + it = ngtcp2_ksl_end(&acktr->ents); + ngtcp2_ksl_it_prev(&it); + delent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&acktr->ents, NULL, + ngtcp2_ksl_key_ptr(&key, &delent->pkt_num)); + ngtcp2_acktr_entry_del(delent, acktr->mem); + } + + return 0; +} + +void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_lower_bound(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + assert(*ngtcp2_ksl_it_key(&it).i == (int64_t)ent->pkt_num); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&acktr->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + ngtcp2_acktr_entry_del(ent, acktr->mem); + } +} + +ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) { + return ngtcp2_ksl_begin(&acktr->ents); +} + +ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, + int64_t pkt_num, + int64_t largest_ack) { + ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks); + + ent->largest_ack = largest_ack; + ent->pkt_num = pkt_num; + + return ent; +} + +/* + * acktr_remove removes |ent| from |acktr|. The iterator which points + * to the entry next to |ent| is assigned to |it|. + */ +static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, + ngtcp2_acktr_entry *ent) { + ngtcp2_ksl_key key; + + ngtcp2_ksl_remove(&acktr->ents, it, ngtcp2_ksl_key_ptr(&key, &ent->pkt_num)); + ngtcp2_acktr_entry_del(ent, acktr->mem); +} + +static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, + size_t ack_ent_offset) { + ngtcp2_acktr_ack_entry *ack_ent; + ngtcp2_acktr_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + assert(ngtcp2_ringbuf_len(rb)); + + ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset); + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + it = ngtcp2_ksl_lower_bound(&acktr->ents, + ngtcp2_ksl_key_ptr(&key, &ack_ent->largest_ack)); + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + acktr_remove(acktr, &it, ent); + } + + if (ngtcp2_ksl_len(&acktr->ents)) { + assert(ngtcp2_ksl_it_end(&it)); + + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + if (ent->pkt_num > ack_ent->largest_ack && + ack_ent->largest_ack >= ent->pkt_num - (int64_t)(ent->len - 1)) { + ent->len = (size_t)(ent->pkt_num - ack_ent->largest_ack); + } + } + + ngtcp2_ringbuf_resize(rb, ack_ent_offset); +} + +void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) { + ngtcp2_acktr_ack_entry *ent; + int64_t largest_ack = fr->largest_ack, min_ack; + size_t i, j; + ngtcp2_ringbuf *rb = &acktr->acks; + size_t nacks = ngtcp2_ringbuf_len(rb); + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + for (j = 0; j < nacks; ++j) { + ent = ngtcp2_ringbuf_get(rb, j); + if (largest_ack >= ent->pkt_num) { + break; + } + } + if (j == nacks) { + return; + } + + min_ack = largest_ack - (int64_t)fr->first_ack_blklen; + + if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) { + acktr_on_ack(acktr, rb, j); + return; + } + + for (i = 0; i < fr->num_blks && j < nacks; ++i) { + largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; + min_ack = largest_ack - (int64_t)fr->blks[i].blklen; + + for (;;) { + if (ent->pkt_num > largest_ack) { + ++j; + if (j == nacks) { + return; + } + ent = ngtcp2_ringbuf_get(rb, j); + continue; + } + if (ent->pkt_num < min_ack) { + break; + } + acktr_on_ack(acktr, rb, j); + return; + } + } + + return; +} + +void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { + acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | + NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | + NGTCP2_ACKTR_FLAG_CANCEL_TIMER); + acktr->first_unacked_ts = UINT64_MAX; + acktr->rx_npkt = 0; +} + +int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, + ngtcp2_tstamp ts) { + return acktr->first_unacked_ts <= ts - max_ack_delay; +} + +void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { + acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK; +} diff --git a/deps/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/lib/ngtcp2_acktr.h new file mode 100644 index 00000000000000..0efd2156ac9117 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_acktr.h @@ -0,0 +1,215 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ACKTR_H +#define NGTCP2_ACKTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_ringbuf.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pkt.h" + +/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry + which ngtcp2_acktr stores. */ +#define NGTCP2_ACKTR_MAX_ENT 1024 + +/* NGTCP2_NUM_IMMEDIATE_ACK_PKT is the maximum number of received + packets which triggers the immediate ACK. */ +#define NGTCP2_NUM_IMMEDIATE_ACK_PKT 2 + +struct ngtcp2_acktr_entry; +typedef struct ngtcp2_acktr_entry ngtcp2_acktr_entry; + +struct ngtcp2_log; +typedef struct ngtcp2_log ngtcp2_log; + +/* + * ngtcp2_acktr_entry is a range of packets which need to be acked. + */ +struct ngtcp2_acktr_entry { + /* pkt_num is the largest packet number to acknowledge in this + range. */ + int64_t pkt_num; + /* len is the consecutive packets started from pkt_num which + includes pkt_num itself counting in decreasing order. So pkt_num + = 987 and len = 2, this entry includes packet 987 and 986. */ + size_t len; + /* tstamp is the timestamp when a packet denoted by pkt_num is + received. */ + ngtcp2_tstamp tstamp; +}; + +/* + * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it + * with the given parameters. The pointer to the allocated object is + * stored to |*ent|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); + +/* + * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It + * deallocates memory pointed by |ent|. + */ +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); + +typedef struct { + /* largest_ack is the largest packet number in outgoing ACK frame */ + int64_t largest_ack; + /* pkt_num is the packet number that ACK frame is included. */ + int64_t pkt_num; +} ngtcp2_acktr_ack_entry; + +typedef enum { + NGTCP2_ACKTR_FLAG_NONE = 0x00, + /* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate + acknowledgement is required. */ + NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK = 0x01, + /* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are + pending protected packet to be acknowledged. */ + NGTCP2_ACKTR_FLAG_ACTIVE_ACK = 0x02, + /* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received + acknowledgement for ACK which acknowledges the last handshake + packet from client (which contains TLSv1.3 Finished message). */ + NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK = 0x80, + /* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is + expired and canceled. */ + NGTCP2_ACKTR_FLAG_CANCEL_TIMER = 0x0100, +} ngtcp2_acktr_flag; + +/* + * ngtcp2_acktr tracks received packets which we have to send ack. + */ +typedef struct { + ngtcp2_ringbuf acks; + /* ents includes ngtcp2_acktr_entry sorted by decreasing order of + packet number. */ + ngtcp2_ksl ents; + ngtcp2_log *log; + const ngtcp2_mem *mem; + /* flags is bitwise OR of zero, or more of ngtcp2_ack_flag. */ + uint16_t flags; + /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added + first time after the last outgoing ACK frame. */ + ngtcp2_tstamp first_unacked_ts; + /* rx_npkt is the number of packets received without sending ACK. */ + size_t rx_npkt; +} ngtcp2_acktr; + +/* + * ngtcp2_acktr_init initializes |acktr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, + const ngtcp2_mem *mem); + +/* + * ngtcp2_acktr_free frees resources allocated for |acktr|. It frees + * any ngtcp2_acktr_entry added to |acktr|. + */ +void ngtcp2_acktr_free(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|. + * |active_ack| is nonzero if |pkt_num| is retransmittable packet. + * + * This function assumes that |acktr| does not contain |pkt_num|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * OUt of memory. + */ +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, + ngtcp2_tstamp ts); + +/* + * ngtcp2_acktr_forget removes all entries which have the packet + * number that is equal to or less than ent->pkt_num. This function + * assumes that |acktr| includes |ent|. + */ +void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent); + +/* + * ngtcp2_acktr_get returns the pointer to pointer to the entry which + * has the largest packet number to be acked. If there is no entry, + * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. + */ +ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest + * acknowledged packet number is |largest_ack|. |pkt_num| is the + * packet number of a packet in which ACK frame is included. This + * function returns a pointer to the object it adds. + */ +ngtcp2_acktr_ack_entry * +ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack); + +/* + * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|. + * |pkt_num| is a packet number which includes |fr|. If we receive + * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack, + * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed. + */ +void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr); + +/* + * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated. + */ +void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should + * be generated actively. + */ +int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, + ngtcp2_tstamp ts); + +/* + * ngtcp2_acktr_immediate_ack tells |acktr| that immediate + * acknowledgement is required. + */ +void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr); + +#endif /* NGTCP2_ACKTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/lib/ngtcp2_addr.c new file mode 100644 index 00000000000000..13a787bdea0302 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_addr.c @@ -0,0 +1,57 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_addr.h" + +#include + +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const void *addr, + size_t addrlen, void *user_data) { + dest->addrlen = addrlen; + dest->addr = (uint8_t *)addr; + dest->user_data = user_data; + return dest; +} + +void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { + dest->addrlen = src->addrlen; + if (src->addrlen) { + memcpy(dest->addr, src->addr, src->addrlen); + } + dest->user_data = src->user_data; +} + +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const void *addr, + size_t addrlen) { + dest->addrlen = addrlen; + if (addrlen) { + memcpy(dest->addr, addr, addrlen); + } +} + +int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { + return a->addrlen == b->addrlen && memcmp(a->addr, b->addr, a->addrlen) == 0; +} + +int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; } diff --git a/deps/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/lib/ngtcp2_addr.h new file mode 100644 index 00000000000000..db8b7144082a14 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_addr.h @@ -0,0 +1,60 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ADDR_H +#define NGTCP2_ADDR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * ngtcp2_addr_copy copies |src| to |dest|. This function assumes + * that dest->addr points to a buffer which have sufficient size to + * store the copy. + */ +void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); + +/* + * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the + * buffer pointed by dest->addr. dest->len is updated to have + * |addrlen|. This function assumes that dest->addr points to a + * buffer which have sufficient size to store the copy. + */ +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const void *addr, size_t addrlen); + +/* + * ngtcp2_addr_eq returns nonzero if |a| equals |b|. + */ +int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); + +/* + * ngtcp2_addr_empty returns nonzero if |addr| has zero length + * address. + */ +int ngtcp2_addr_empty(const ngtcp2_addr *addr); + +#endif /* NGTCP2_ADDR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/lib/ngtcp2_buf.c new file mode 100644 index 00000000000000..373f23d91aed7c --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_buf.c @@ -0,0 +1,44 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_buf.h" + +void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { + buf->begin = buf->pos = buf->last = begin; + buf->end = begin + len; +} + +void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; } + +size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->last); +} + +size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { + return (size_t)(buf->last - buf->pos); +} + +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->begin); +} diff --git a/deps/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/lib/ngtcp2_buf.h new file mode 100644 index 00000000000000..fe3d06a1c9733d --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_buf.h @@ -0,0 +1,79 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_BUF_H +#define NGTCP2_BUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct { + /* begin points to the beginning of the buffer. */ + uint8_t *begin; + /* end points to the one beyond of the last byte of the buffer */ + uint8_t *end; + /* pos pointers to the start of data. Typically, this points to the + point that next data should be read. Initially, it points to + |begin|. */ + uint8_t *pos; + /* last points to the one beyond of the last data of the buffer. + Typically, new data is written at this point. Initially, it + points to |begin|. */ + uint8_t *last; +} ngtcp2_buf; + +/* + * ngtcp2_buf_init initializes |buf| with the given buffer. + */ +void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len); + +/* + * ngtcp2_buf_reset resets pos and last fields to match begin field to + * make ngtcp2_buf_len(buf) return 0. + */ +void ngtcp2_buf_reset(ngtcp2_buf *buf); + +/* + * ngtcp2_buf_left returns the number of additional bytes which can be + * written to the underlying buffer. In other words, it returns + * buf->end - buf->last. + */ +size_t ngtcp2_buf_left(const ngtcp2_buf *buf); + +/* + * ngtcp2_buf_len returns the number of bytes left to read. In other + * words, it returns buf->last - buf->pos. + */ +size_t ngtcp2_buf_len(const ngtcp2_buf *buf); + +/* + * ngtcp2_buf_cap returns the capacity of the buffer. In other words, + * it returns buf->end - buf->begin. + */ +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); + +#endif /* NGTCP2_BUF_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/lib/ngtcp2_cc.c new file mode 100644 index 00000000000000..02864bffa0ec41 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cc.c @@ -0,0 +1,137 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_cc.h" +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_rst.h" + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_tstamp ts_sent) { + pkt->pkt_num = pkt_num; + pkt->pktlen = pktlen; + pkt->ts_sent = ts_sent; + + return pkt; +} + +void ngtcp2_default_cc_init(ngtcp2_default_cc *cc, ngtcp2_cc_stat *ccs, + ngtcp2_rst *rst, ngtcp2_log *log) { + cc->log = log; + cc->ccs = ccs; + cc->rst = rst; + cc->max_delivery_rate = 0.; + cc->min_rtt = 0; + cc->min_rtt_ts = 0; + cc->target_cwnd = 0; +} + +void ngtcp2_default_cc_free(ngtcp2_default_cc *cc) { (void)cc; } + +static int default_cc_in_congestion_recovery(ngtcp2_default_cc *cc, + ngtcp2_tstamp sent_time) { + return sent_time <= cc->ccs->congestion_recovery_start_ts; +} + +void ngtcp2_default_cc_on_pkt_acked(ngtcp2_default_cc *cc, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_cc_stat *ccs = cc->ccs; + + if (default_cc_in_congestion_recovery(cc, pkt->ts_sent)) { + return; + } + + if (cc->target_cwnd && ccs->cwnd >= cc->target_cwnd) { + return; + } + + if (ccs->cwnd < ccs->ssthresh) { + ccs->cwnd += pkt->pktlen; + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " acked, slow start cwnd=%lu", pkt->pkt_num, + ccs->cwnd); + return; + } + + ccs->cwnd += NGTCP2_MAX_DGRAM_SIZE * pkt->pktlen / ccs->cwnd; +} + +void ngtcp2_default_cc_congestion_event(ngtcp2_default_cc *cc, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts) { + ngtcp2_cc_stat *ccs = cc->ccs; + + if (default_cc_in_congestion_recovery(cc, ts_sent)) { + return; + } + ccs->congestion_recovery_start_ts = ts; + ccs->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS; + ccs->cwnd = ngtcp2_max(ccs->cwnd, NGTCP2_MIN_CWND); + ccs->ssthresh = ccs->cwnd; + + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "reduce cwnd because of packet loss cwnd=%lu", ccs->cwnd); +} + +void ngtcp2_default_cc_handle_persistent_congestion(ngtcp2_default_cc *cc, + ngtcp2_duration loss_window, + ngtcp2_duration pto) { + ngtcp2_cc_stat *ccs = cc->ccs; + ngtcp2_duration congestion_period = + pto * NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + + if (loss_window >= congestion_period) { + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "persistent congestion loss_window=%" PRIu64 + " congestion_period=%" PRIu64, + loss_window, congestion_period); + + ccs->cwnd = NGTCP2_MIN_CWND; + } +} + +void ngtcp2_default_cc_on_ack_recv(ngtcp2_default_cc *cc, + ngtcp2_duration latest_rtt, + ngtcp2_tstamp ts) { + /* TODO Use sliding window */ + if (latest_rtt && (cc->min_rtt == 0 || cc->min_rtt > latest_rtt)) { + cc->min_rtt = latest_rtt; + cc->min_rtt_ts = ts; + } + + /* TODO Use sliding window */ + cc->max_delivery_rate = + ngtcp2_max(cc->max_delivery_rate, cc->rst->rs.delivery_rate); + + if (cc->min_rtt && cc->max_delivery_rate > 1e-9) { + uint64_t target_cwnd = + (uint64_t)(2.89 * cc->max_delivery_rate * (double)cc->min_rtt); + cc->target_cwnd = ngtcp2_max(NGTCP2_MIN_CWND, target_cwnd); + + ngtcp2_log_info(cc->log, NGTCP2_LOG_EVENT_RCV, + "target_cwnd=%lu max_delivery_rate=%.02f min_rtt=%lu", + cc->target_cwnd, cc->max_delivery_rate * 1000000000, + cc->min_rtt); + } +} diff --git a/deps/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/lib/ngtcp2_cc.h new file mode 100644 index 00000000000000..33f2815c78f688 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cc.h @@ -0,0 +1,92 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CC_H +#define NGTCP2_CC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#define NGTCP2_MAX_DGRAM_SIZE 1200 +#define NGTCP2_MIN_CWND (2 * NGTCP2_MAX_DGRAM_SIZE) +#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1 +#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3 + +struct ngtcp2_log; +typedef struct ngtcp2_log ngtcp2_log; + +struct ngtcp2_rst; +typedef struct ngtcp2_rst ngtcp2_rst; + +/* ngtcp2_cc_pkt is a convenient structure to include acked/lost/sent + packet. */ +typedef struct { + /* pkt_num is the packet number */ + int64_t pkt_num; + /* pktlen is the length of packet. */ + size_t pktlen; + /* ts_sent is the timestamp when packet is sent. */ + ngtcp2_tstamp ts_sent; +} ngtcp2_cc_pkt; + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_tstamp ts_sent); + +/* ngtcp2_default_cc is the default congestion controller. */ +struct ngtcp2_default_cc { + ngtcp2_log *log; + ngtcp2_cc_stat *ccs; + ngtcp2_rst *rst; + double max_delivery_rate; + ngtcp2_duration min_rtt; + ngtcp2_tstamp min_rtt_ts; + uint64_t target_cwnd; +}; + +typedef struct ngtcp2_default_cc ngtcp2_default_cc; + +void ngtcp2_default_cc_init(ngtcp2_default_cc *cc, ngtcp2_cc_stat *ccs, + ngtcp2_rst *rst, ngtcp2_log *log); + +void ngtcp2_default_cc_free(ngtcp2_default_cc *cc); + +void ngtcp2_default_cc_on_pkt_acked(ngtcp2_default_cc *cc, + const ngtcp2_cc_pkt *pkt); + +void ngtcp2_default_cc_congestion_event(ngtcp2_default_cc *cc, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts); + +void ngtcp2_default_cc_handle_persistent_congestion(ngtcp2_default_cc *cc, + ngtcp2_duration loss_window, + ngtcp2_duration pto); + +void ngtcp2_default_cc_on_ack_recv(ngtcp2_default_cc *cc, + ngtcp2_duration latest_rtt, + ngtcp2_tstamp ts); + +#endif /* NGTCP2_CC_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/lib/ngtcp2_cid.c new file mode 100644 index 00000000000000..b0670a9c6f0579 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cid.c @@ -0,0 +1,118 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_cid.h" + +#include +#include + +#include "ngtcp2_path.h" +#include "ngtcp2_str.h" + +void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; } + +void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) { + assert(datalen <= NGTCP2_MAX_CIDLEN); + + cid->datalen = datalen; + if (datalen) { + ngtcp2_cpymem(cid->data, data, datalen); + } +} + +int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other) { + return cid->datalen == other->datalen && + 0 == memcmp(cid->data, other->data, cid->datalen); +} + +int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) { + int s = lhs->datalen < rhs->datalen; + size_t n = s ? lhs->datalen : rhs->datalen; + int c = memcmp(lhs->data, rhs->data, n); + + return c < 0 || (c == 0 && s); +} + +int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; } + +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + scid->pe.index = NGTCP2_PQ_BAD_INDEX; + scid->seq = seq; + scid->cid = *cid; + scid->ts_retired = UINT64_MAX; + scid->flags = NGTCP2_SCID_FLAG_NONE; + if (token) { + memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else { + memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + } +} + +void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { + ngtcp2_scid_init(dest, src->seq, &src->cid, src->token); + dest->ts_retired = src->ts_retired; + dest->flags = src->flags; +} + +void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + dcid->seq = seq; + dcid->cid = *cid; + if (token) { + memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else { + memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + } + ngtcp2_path_storage_zero(&dcid->ps); + dcid->ts_retired = UINT64_MAX; +} + +void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { + ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token); + ngtcp2_path_copy(&dest->ps.path, &src->ps.path); + dest->ts_retired = src->ts_retired; +} + +void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); + + dest->ts_retired = src->ts_retired; +} + +int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token) { + + if (dcid->seq == seq) { + return ngtcp2_cid_eq(&dcid->cid, cid) && + memcmp(dcid->token, token, + NGTCP2_STATELESS_RESET_TOKENLEN) == 0 + ? 0 + : NGTCP2_ERR_PROTO; + } + + return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; +} diff --git a/deps/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/lib/ngtcp2_cid.h new file mode 100644 index 00000000000000..fe5576ae468efa --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_cid.h @@ -0,0 +1,137 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CID_H +#define NGTCP2_CID_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pq.h" +#include "ngtcp2_path.h" + +typedef enum { + NGTCP2_SCID_FLAG_NONE, + NGTCP2_SCID_FLAG_USED = 0x01, + NGTCP2_SCID_FLAG_RETIRED = 0x02, +} ngtcp2_scid_flag; + +typedef struct { + ngtcp2_pq_entry pe; + /* seq is the sequence number associated to the CID. */ + uint64_t seq; + /* cid is a connection ID */ + ngtcp2_cid cid; + /* ts_retired is the timestamp when peer tells that this CID is + retired. */ + ngtcp2_tstamp ts_retired; + /* flags is the bitwise OR of zero or more of ngtcp2_scid_flag. */ + uint8_t flags; + /* token is a stateless reset token associated to this CID. + Actually, the stateless reset token is tied to the connection, + not to the particular connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_scid; + +typedef struct { + /* seq is the sequence number associated to the CID. */ + uint64_t seq; + /* cid is a connection ID */ + ngtcp2_cid cid; + /* path is a path which cid is bound to. The addresses are zero + length if cid has not been bound to a particular path yet. */ + ngtcp2_path_storage ps; + /* ts_retired is the timestamp when peer tells that this CID is + retired. */ + ngtcp2_tstamp ts_retired; + /* token is a stateless reset token associated to this CID. + Actually, the stateless reset token is tied to the connection, + not to the particular connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_dcid; + +/* ngtcp2_cid_zero makes |cid| zero-length. */ +void ngtcp2_cid_zero(ngtcp2_cid *cid); + +/* + * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same + * connection ID. + */ +int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other); + +/* + * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller + * than |rhs|. + */ +int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs); + +/* + * ngtcp2_cid_empty returns nonzero if |cid| includes empty connection + * ID. + */ +int ngtcp2_cid_empty(const ngtcp2_cid *cid); + +/* + * ngtcp2_scid_init initializes |scid| with the given parameters. If + * |token| is NULL, the function fills scid->token it with 0. |token| + * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token); + +/* + * ngtcp2_scid_copy copies |src| into |dest|. + */ +void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src); + +/* + * ngtcp2_dcid_init initializes |dcid| with the given parameters. If + * |token| is NULL, the function fills dcid->token it with 0. |token| + * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token); + +/* + * ngtcp2_dcid_copy copies |src| into |dest|. + */ +void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src); + +/* + * ngtcp2_dcid_copy_no_path behaves like ngtcp2_dcid_copy, but it does + * not copy path. + */ +void ngtcp2_dcid_copy_no_path(ngtcp2_dcid *dest, const ngtcp2_dcid *src); + +/* + * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|, + * |token|) tuple against |dcid|. + */ +int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token); + +#endif /* NGTCP2_CID_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/lib/ngtcp2_conn.c new file mode 100644 index 00000000000000..eca7f04ca33a23 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conn.c @@ -0,0 +1,9502 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conn.h" + +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_log.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_addr.h" +#include "ngtcp2_path.h" +#include "ngtcp2_rcvry.h" + +/* + * conn_local_stream returns nonzero if |stream_id| indicates that it + * is the stream initiated by local endpoint. + */ +static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { + return (uint8_t)(stream_id & 1) == conn->server; +} + +/* + * bidi_stream returns nonzero if |stream_id| is a bidirectional + * stream ID. + */ +static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } + +static int conn_call_recv_client_initial(ngtcp2_conn *conn, + const ngtcp2_cid *dcid) { + int rv; + + assert(conn->callbacks.recv_client_initial); + + rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_handshake_completed(ngtcp2_conn *conn) { + int rv; + + if (!conn->callbacks.handshake_completed) { + return 0; + } + + rv = conn->callbacks.handshake_completed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, + int fin, uint64_t offset, + const uint8_t *data, size_t datalen) { + int rv; + + if (!conn->callbacks.recv_stream_data) { + return 0; + } + + rv = conn->callbacks.recv_stream_data(conn, strm->stream_id, fin, offset, + data, datalen, conn->user_data, + strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen) { + int rv; + + assert(conn->callbacks.recv_crypto_data); + + rv = conn->callbacks.recv_crypto_data(conn, crypto_level, offset, data, + datalen, conn->user_data); + switch (rv) { + case 0: + case NGTCP2_ERR_CRYPTO: + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + case NGTCP2_ERR_PROTO: + case NGTCP2_ERR_CALLBACK_FAILURE: + return rv; + default: + return NGTCP2_ERR_CALLBACK_FAILURE; + } +} + +static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) { + int rv; + + if (!conn->callbacks.stream_open) { + return 0; + } + + rv = conn->callbacks.stream_open(conn, strm->stream_id, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + + if (!conn->callbacks.stream_close) { + return 0; + } + + rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code, + conn->user_data, strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *stream_user_data) { + int rv; + + if (!conn->callbacks.stream_reset) { + return 0; + } + + rv = conn->callbacks.stream_reset(conn, stream_id, final_size, app_error_code, + conn->user_data, stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_local_streams_bidi) { + return 0; + } + + rv = conn->callbacks.extend_max_local_streams_bidi(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_local_streams_uni) { + return 0; + } + + rv = conn->callbacks.extend_max_local_streams_uni(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen) { + int rv; + + assert(conn->callbacks.get_new_connection_id); + + rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_remove_connection_id(ngtcp2_conn *conn, + const ngtcp2_cid *cid) { + int rv; + + if (!conn->callbacks.remove_connection_id) { + return 0; + } + + rv = conn->callbacks.remove_connection_id(conn, cid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_path_validation_result res) { + int rv; + + if (!conn->callbacks.path_validation) { + return 0; + } + + rv = conn->callbacks.path_validation(conn, path, res, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_select_preferred_addr(ngtcp2_conn *conn, + ngtcp2_addr *dest) { + int rv; + + if (!conn->callbacks.select_preferred_addr) { + return 0; + } + + assert(conn->remote.transport_params.preferred_address_present); + + rv = conn->callbacks.select_preferred_addr( + conn, dest, &conn->remote.transport_params.preferred_address, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_remote_streams_bidi(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_remote_streams_bidi) { + return 0; + } + + rv = conn->callbacks.extend_max_remote_streams_bidi(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_remote_streams_uni(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_remote_streams_uni) { + return 0; + } + + rv = conn->callbacks.extend_max_remote_streams_uni(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_stream_data(ngtcp2_conn *conn, + ngtcp2_strm *strm, + int64_t stream_id, + uint64_t datalen) { + int rv; + + if (!conn->callbacks.extend_max_stream_data) { + return 0; + } + + rv = conn->callbacks.extend_max_stream_data( + conn, stream_id, datalen, conn->user_data, strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_dcid_status(ngtcp2_conn *conn, + ngtcp2_connection_id_status_type type, + const ngtcp2_dcid *dcid) { + int rv; + + if (!conn->callbacks.dcid_status) { + return 0; + } + + rv = conn->callbacks.dcid_status( + conn, (int)type, dcid->seq, &dcid->cid, + ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL + : dcid->token, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) { + return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + dcid); +} + +static int conn_call_deactivate_dcid(ngtcp2_conn *conn, + const ngtcp2_dcid *dcid) { + return conn_call_dcid_status( + conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); +} + +static int crypto_offset_less(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + return *lhs->i < *rhs->i; +} + +static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_crypto_level crypto_level, + ngtcp2_rst *rst, ngtcp2_default_cc *cc, ngtcp2_log *log, + ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + int rv; + + memset(pktns, 0, sizeof(*pktns)); + + rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); + if (rv != 0) { + return rv; + } + + pktns->tx.last_pkt_num = -1; + pktns->rx.max_pkt_num = -1; + + rv = ngtcp2_acktr_init(&pktns->acktr, log, mem); + if (rv != 0) { + goto fail_acktr_init; + } + + rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, + NULL, mem); + if (rv != 0) { + goto fail_crypto_init; + } + + rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, + sizeof(uint64_t), mem); + if (rv != 0) { + goto fail_tx_frq_init; + } + + ngtcp2_rtb_init(&pktns->rtb, crypto_level, &pktns->crypto.strm, rst, cc, log, + qlog, mem); + + return 0; + +fail_tx_frq_init: + ngtcp2_strm_free(&pktns->crypto.strm); +fail_crypto_init: + ngtcp2_acktr_free(&pktns->acktr); +fail_acktr_init: + ngtcp2_gaptr_free(&pktns->rx.pngap); + + return rv; +} + +static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_crypto_level crypto_level, + ngtcp2_rst *rst, ngtcp2_default_cc *cc, ngtcp2_log *log, + ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + int rv; + + *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); + if (*ppktns == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = pktns_init(*ppktns, crypto_level, rst, cc, log, qlog, mem); + if (rv != 0) { + ngtcp2_mem_free(mem, *ppktns); + } + + return rv; +} + +static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { + ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe); + ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe); + + if (ls->cycle == rs->cycle) { + return ls->stream_id < rs->stream_id; + } + + return rs->cycle - ls->cycle <= 1; +} + +static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { + ngtcp2_pkt_chain *next; + + for (; pc;) { + next = pc->next; + ngtcp2_pkt_chain_del(pc, mem); + pc = next; + } +} + +static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + delete_buffed_pkts(pktns->rx.buffed_pkts, mem); + + ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); + + ngtcp2_vec_del(pktns->crypto.rx.hp_key, mem); + ngtcp2_vec_del(pktns->crypto.tx.hp_key, mem); + + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + ngtcp2_frame_chain_del(frc, mem); + } + + ngtcp2_ksl_free(&pktns->crypto.tx.frq); + ngtcp2_rtb_free(&pktns->rtb); + ngtcp2_strm_free(&pktns->crypto.strm); + ngtcp2_acktr_free(&pktns->acktr); + ngtcp2_gaptr_free(&pktns->rx.pngap); +} + +static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { + if (pktns == NULL) { + return; + } + + pktns_free(pktns, mem); + + ngtcp2_mem_free(mem, pktns); +} + +static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return ngtcp2_cid_less(lhs->ptr, rhs->ptr); +} + +static int ts_retired_less(const ngtcp2_pq_entry *lhs, + const ngtcp2_pq_entry *rhs) { + const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe); + const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe); + + return a->ts_retired < b->ts_retired; +} + +static void rcvry_stat_reset(ngtcp2_rcvry_stat *rcs) { + memset(rcs, 0, sizeof(*rcs)); + rcs->min_rtt = UINT64_MAX; + // Initializes them with UINT64_MAX. + memset(rcs->last_tx_pkt_ts, 0xff, sizeof(rcs->last_tx_pkt_ts)); +} + +static void cc_stat_reset(ngtcp2_cc_stat *ccs) { + memset(ccs, 0, sizeof(*ccs)); + ccs->cwnd = ngtcp2_min(10 * NGTCP2_MAX_DGRAM_SIZE, + ngtcp2_max(2 * NGTCP2_MAX_DGRAM_SIZE, 14720)); + ccs->ssthresh = UINT64_MAX; +} + +static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) { + ngtcp2_ksl_it it; + + for (it = ngtcp2_ksl_begin(scids); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_mem_free(mem, ngtcp2_ksl_it_get(&it)); + } +} + +static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_conn_callbacks *callbacks, + const ngtcp2_settings *settings, const ngtcp2_mem *mem, + void *user_data, int server) { + int rv; + ngtcp2_scid *scident; + ngtcp2_ksl_key key; + const ngtcp2_transport_params *params = &settings->transport_params; + uint8_t *buf; + + assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + *pconn = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_conn)); + if (*pconn == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_conn; + } + + rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid), mem); + if (rv != 0) { + goto fail_dcid_unused_init; + } + + rv = + ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, + sizeof(ngtcp2_dcid), mem); + if (rv != 0) { + goto fail_dcid_retired_init; + } + + rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); + if (rv != 0) { + goto fail_scid_set_init; + } + + ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem); + + rv = ngtcp2_map_init(&(*pconn)->strms, mem); + if (rv != 0) { + goto fail_strms_init; + } + + ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); + + rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); + if (rv != 0) { + goto fail_remote_bidi_idtr_init; + } + + rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); + if (rv != 0) { + goto fail_remote_uni_idtr_init; + } + + rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, + sizeof(ngtcp2_path_challenge_entry), mem); + if (rv != 0) { + goto fail_rx_path_challenge_init; + } + + ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, + settings->initial_ts, user_data); + ngtcp2_qlog_init(&(*pconn)->qlog, settings->qlog.write, settings->initial_ts, + user_data); + if ((*pconn)->qlog.write) { + buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); + if (buf == NULL) { + goto fail_qlog_buf; + } + ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); + } + + ngtcp2_rst_init(&(*pconn)->rst); + + ngtcp2_default_cc_init(&(*pconn)->cc, &(*pconn)->ccs, &(*pconn)->rst, + &(*pconn)->log); + + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_CRYPTO_LEVEL_INITIAL, + &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + mem); + if (rv != 0) { + goto fail_in_pktns_init; + } + + rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, + &(*pconn)->rst, &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, + mem); + if (rv != 0) { + goto fail_hs_pktns_init; + } + + rv = pktns_init(&(*pconn)->pktns, NGTCP2_CRYPTO_LEVEL_APP, &(*pconn)->rst, + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + if (rv != 0) { + goto fail_pktns_init; + } + + (*pconn)->local.settings = *settings; + + if (server && settings->token.len) { + buf = ngtcp2_mem_malloc(mem, settings->token.len); + if (buf == NULL) { + goto fail_token; + } + memcpy(buf, settings->token.base, settings->token.len); + (*pconn)->local.settings.token.base = buf; + } else { + (*pconn)->local.settings.token.base = NULL; + (*pconn)->local.settings.token.len = 0; + } + + if (params->active_connection_id_limit == 0) { + (*pconn)->local.settings.transport_params.active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + } + + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_scident; + } + + ngtcp2_scid_init(scident, 0, scid, + params->stateless_reset_token_present + ? params->stateless_reset_token + : NULL); + + rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); + if (rv != 0) { + goto fail_scid_set_insert; + } + + scident = NULL; + + if (server && params->preferred_address_present) { + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_scident; + } + + ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, + params->preferred_address.stateless_reset_token); + + rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scident->cid), scident); + if (rv != 0) { + goto fail_scid_set_insert; + } + + scident = NULL; + + (*pconn)->scid.last_seq = 1; + } + + ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); + ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); + + (*pconn)->oscid = *scid; + (*pconn)->callbacks = *callbacks; + (*pconn)->version = version; + (*pconn)->mem = mem; + (*pconn)->user_data = user_data; + (*pconn)->rx.unsent_max_offset = (*pconn)->rx.max_offset = + params->initial_max_data; + (*pconn)->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi; + (*pconn)->remote.bidi.max_streams = params->initial_max_streams_bidi; + (*pconn)->remote.uni.unsent_max_streams = params->initial_max_streams_uni; + (*pconn)->remote.uni.max_streams = params->initial_max_streams_uni; + (*pconn)->idle_ts = settings->initial_ts; + (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; + + rcvry_stat_reset(&(*pconn)->rcs); + cc_stat_reset(&(*pconn)->ccs); + + ngtcp2_qlog_start(&(*pconn)->qlog, &settings->qlog.odcid, server); + ngtcp2_qlog_parameters_set_transport_params( + &(*pconn)->qlog, &(*pconn)->local.settings.transport_params, + /* local = */ 1); + + return 0; + +fail_scid_set_insert: + ngtcp2_mem_free(mem, scident); +fail_scident: + ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); +fail_token: + pktns_free(&(*pconn)->pktns, mem); +fail_pktns_init: + pktns_del((*pconn)->hs_pktns, mem); +fail_hs_pktns_init: + pktns_del((*pconn)->in_pktns, mem); +fail_in_pktns_init: + ngtcp2_default_cc_free(&(*pconn)->cc); + ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); +fail_qlog_buf: + ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); +fail_rx_path_challenge_init: + ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); +fail_remote_uni_idtr_init: + ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); +fail_remote_bidi_idtr_init: + ngtcp2_map_free(&(*pconn)->strms); +fail_strms_init: + delete_scid(&(*pconn)->scid.set, mem); + ngtcp2_ksl_free(&(*pconn)->scid.set); +fail_scid_set_init: + ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); +fail_dcid_retired_init: + ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); +fail_dcid_unused_init: + ngtcp2_mem_free(mem, *pconn); +fail_conn: + return rv; +} + +int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, + const ngtcp2_conn_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_mem *mem, void *user_data) { + int rv; + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, + user_data, 0); + if (rv != 0) { + return rv; + } + (*pconn)->rcid = *dcid; + (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; + (*pconn)->local.bidi.next_stream_id = 0; + (*pconn)->local.uni.next_stream_id = 2; + + return 0; +} + +int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, + const ngtcp2_conn_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_mem *mem, void *user_data) { + int rv; + ngtcp2_transport_params *params; + + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, mem, + user_data, 1); + if (rv != 0) { + return rv; + } + (*pconn)->server = 1; + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; + (*pconn)->local.bidi.next_stream_id = 1; + (*pconn)->local.uni.next_stream_id = 3; + + params = &(*pconn)->local.settings.transport_params; + if (dcid->datalen == 0) { + /* Client uses zero-length Connection ID */ + params->active_connection_id_limit = 0; + } + + return 0; +} + +/* + * conn_fc_credits returns the number of bytes allowed to be sent to + * the given stream. Both connection and stream level flow control + * credits are considered. + */ +static size_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { + return ngtcp2_min(strm->tx.max_offset - strm->tx.offset, + conn->tx.max_offset - conn->tx.offset); +} + +/* + * conn_enforce_flow_control returns the number of bytes allowed to be + * sent to the given stream. |len| might be shorted because of + * available flow control credits. + */ +static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, + size_t len) { + size_t fc_credits = conn_fc_credits(conn, strm); + return ngtcp2_min(len, fc_credits); +} + +static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) { + const ngtcp2_mem *mem = ptr; + ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me); + + ngtcp2_strm_free(s); + ngtcp2_mem_free(mem, s); + + return 0; +} + +void ngtcp2_conn_del(ngtcp2_conn *conn) { + if (conn == NULL) { + return; + } + + ngtcp2_qlog_end(&conn->qlog); + + ngtcp2_mem_free(conn->mem, conn->token.begin); + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); + ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); + + ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); + ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem); + ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem); + ngtcp2_vec_del(conn->early.hp_key, conn->mem); + ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + + pktns_free(&conn->pktns, conn->mem); + pktns_del(conn->hs_pktns, conn->mem); + pktns_del(conn->in_pktns, conn->mem); + + ngtcp2_default_cc_free(&conn->cc); + + ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); + + ngtcp2_ringbuf_free(&conn->rx.path_challenge); + + ngtcp2_pv_del(conn->pv); + + ngtcp2_idtr_free(&conn->remote.uni.idtr); + ngtcp2_idtr_free(&conn->remote.bidi.idtr); + ngtcp2_mem_free(conn->mem, conn->tx.ack); + ngtcp2_pq_free(&conn->tx.strmq); + ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem); + ngtcp2_map_free(&conn->strms); + + ngtcp2_pq_free(&conn->scid.used); + delete_scid(&conn->scid.set, conn->mem); + ngtcp2_ksl_free(&conn->scid.set); + ngtcp2_ringbuf_free(&conn->dcid.retired); + ngtcp2_ringbuf_free(&conn->dcid.unused); + + ngtcp2_mem_free(conn->mem, conn); +} + +/* + * conn_ensure_ack_blks makes sure that conn->tx.ack->ack.blks can + * contain at least |n| additional ngtcp2_ack_blk. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_ensure_ack_blks(ngtcp2_conn *conn, size_t n) { + ngtcp2_frame *fr; + size_t max = conn->tx.max_ack_blks; + + if (n <= max) { + return 0; + } + + max *= 2; + + assert(max >= n); + + fr = ngtcp2_mem_realloc(conn->mem, conn->tx.ack, + sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * max); + if (fr == NULL) { + return NGTCP2_ERR_NOMEM; + } + + conn->tx.ack = fr; + conn->tx.max_ack_blks = max; + + return 0; +} + +/* + * conn_compute_ack_delay computes ACK delay for outgoing protected + * ACK. + */ +static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) { + ngtcp2_duration initial_delay = + conn->local.settings.transport_params.max_ack_delay; + + if (conn->rcs.smoothed_rtt == 0) { + return initial_delay; + } + + return ngtcp2_min(initial_delay, conn->rcs.smoothed_rtt / 8); +} + +/* + * conn_create_ack_frame creates ACK frame, and assigns its pointer to + * |*pfr| if there are any received packets to acknowledge. If there + * are no packets to acknowledge, this function returns 0, and |*pfr| + * is untouched. The caller is advised to set |*pfr| to NULL before + * calling this function, and check it after this function returns. + * If |nodelay| is nonzero, delayed ACK timer is ignored. + * + * The memory for ACK frame is dynamically allocated by this function. + * A caller is responsible to free it. + * + * Call ngtcp2_acktr_commit_ack after a created ACK frame is + * successfully serialized into a packet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, + ngtcp2_acktr *acktr, uint8_t type, + ngtcp2_tstamp ts, ngtcp2_duration ack_delay, + uint64_t ack_delay_exponent) { + /* TODO Measure an actual size of ACK bloks to find the best default + value. */ + const size_t initial_max_ack_blks = 8; + int64_t last_pkt_num; + ngtcp2_ack_blk *blk; + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *rpkt; + ngtcp2_ack *ack; + size_t blk_idx; + int rv; + + if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) { + ack_delay = 0; + } + + if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) { + return 0; + } + + it = ngtcp2_acktr_get(acktr); + if (ngtcp2_ksl_it_end(&it)) { + ngtcp2_acktr_commit_ack(acktr); + return 0; + } + + if (conn->tx.ack == NULL) { + conn->tx.ack = ngtcp2_mem_malloc( + conn->mem, + sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_blk) * initial_max_ack_blks); + if (conn->tx.ack == NULL) { + return NGTCP2_ERR_NOMEM; + } + conn->tx.max_ack_blks = initial_max_ack_blks; + } + + ack = &conn->tx.ack->ack; + + rpkt = ngtcp2_ksl_it_get(&it); + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + ack->type = NGTCP2_FRAME_ACK; + ack->largest_ack = rpkt->pkt_num; + ack->first_ack_blklen = rpkt->len - 1; + if (type == NGTCP2_PKT_SHORT) { + ack->ack_delay_unscaled = ts - rpkt->tstamp; + ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / + (1UL << ack_delay_exponent); + } else { + ack->ack_delay_unscaled = 0; + ack->ack_delay = 0; + } + ack->num_blks = 0; + + ngtcp2_ksl_it_next(&it); + + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + if (ack->num_blks == NGTCP2_MAX_ACK_BLKS) { + break; + } + + rpkt = ngtcp2_ksl_it_get(&it); + + blk_idx = ack->num_blks++; + rv = conn_ensure_ack_blks(conn, ack->num_blks); + if (rv != 0) { + return rv; + } + ack = &conn->tx.ack->ack; + blk = &ack->blks[blk_idx]; + blk->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); + blk->blklen = rpkt->len - 1; + + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + } + + /* TODO Just remove entries which cannot fit into a single ACK frame + for now. */ + if (!ngtcp2_ksl_it_end(&it)) { + ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it)); + } + + *pfr = conn->tx.ack; + + return 0; +} + +/* + * conn_ppe_write_frame writes |fr| to |ppe|. If |hd_logged| is not + * NULL and |*hd_logged| is zero, packet header is logged, and 1 is + * assigned to |*hd_logged|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too small. + */ +static int conn_ppe_write_frame_hd_log(ngtcp2_conn *conn, ngtcp2_ppe *ppe, + int *hd_logged, const ngtcp2_pkt_hd *hd, + ngtcp2_frame *fr) { + int rv; + + rv = ngtcp2_ppe_encode_frame(ppe, fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + if (hd_logged && !*hd_logged) { + *hd_logged = 1; + ngtcp2_log_tx_pkt_hd(&conn->log, hd); + ngtcp2_qlog_pkt_sent_start(&conn->qlog, hd); + } + + ngtcp2_log_tx_fr(&conn->log, hd, fr); + ngtcp2_qlog_write_frame(&conn->qlog, fr); + + return 0; +} + +/* + * conn_ppe_write_frame writes |fr| to |ppe|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too small. + */ +static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe, + const ngtcp2_pkt_hd *hd, ngtcp2_frame *fr) { + return conn_ppe_write_frame_hd_log(conn, ppe, NULL, hd, fr); +} + +/* + * conn_on_pkt_sent is called when new non-ACK-only packet is sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb, + ngtcp2_rtb_entry *ent) { + int rv; + + /* This function implements OnPacketSent, but it handles only + non-ACK-only packet. */ + rv = ngtcp2_rtb_add(rtb, ent); + if (rv != 0) { + return rv; + } + + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + conn->rcs.last_tx_pkt_ts[rtb->crypto_level] = ent->ts; + } + ngtcp2_conn_set_loss_detection_timer(conn, ent->ts); + + return 0; +} + +/* + * pktns_select_pkt_numlen selects shortest packet number encoding for + * the next packet number based on the largest acknowledged packet + * number. It returns the number of bytes to encode the packet + * number. + */ +static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { + int64_t pkt_num = pktns->tx.last_pkt_num + 1; + ngtcp2_rtb *rtb = &pktns->rtb; + int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; + + if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) { + return 4; + } + + n = n * 2 + 1; + + if (n > 0xffffff) { + return 4; + } + if (n > 0xffff) { + return 3; + } + if (n > 0xff) { + return 2; + } + return 1; +} + +/* + * conn_cwnd_left returns the number of bytes the local endpoint can + * sent at this time. + */ +static uint64_t conn_cwnd_left(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = ngtcp2_conn_get_bytes_in_flight(conn); + uint64_t cwnd = + conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? NGTCP2_MIN_CWND + : conn->ccs.cwnd; + + /* We might send more than bytes_in_flight if probe packets are + involved. */ + if (bytes_in_flight >= cwnd) { + return 0; + } + return cwnd - bytes_in_flight; +} + +/* + * conn_retry_early_payloadlen returns the estimated wire length of + * the first STREAM frame of 0-RTT packet which should be + * retransmitted due to Retry frame + */ +static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { + ngtcp2_frame_chain *frc; + ngtcp2_strm *strm; + + for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { + strm = ngtcp2_conn_tx_strmq_top(conn); + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + frc = ngtcp2_strm_streamfrq_top(strm); + return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + + NGTCP2_STREAM_OVERHEAD; + } + + return 0; +} + +/* + * conn_cryptofrq_top returns the element which sits on top of the + * queue. The queue must not be empty. + */ +static ngtcp2_frame_chain *conn_cryptofrq_top(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + ngtcp2_ksl_it it; + (void)conn; + + assert(ngtcp2_ksl_len(&pktns->crypto.tx.frq)); + + it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); + return ngtcp2_ksl_it_get(&it); +} + +static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_frame_chain **pfrc) { + ngtcp2_frame_chain *frc, *nfrc; + ngtcp2_crypto *fr, *nfr; + uint64_t offset, end_offset; + size_t idx, end_idx; + size_t base_offset, end_base_offset; + ngtcp2_ksl_it gapit; + ngtcp2_range gap; + ngtcp2_rtb *rtb = &pktns->rtb; + ngtcp2_vec *v; + int rv; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + *pfrc = NULL; + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);) { + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.crypto; + + ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, + ngtcp2_ksl_key_ptr(&key, &fr->offset)); + + idx = 0; + offset = fr->offset; + base_offset = 0; + + gapit = + ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset, offset); + gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr; + if (gap.begin < offset) { + gap.begin = offset; + } + + for (; idx < fr->datacnt && offset < gap.begin; ++idx) { + v = &fr->data[idx]; + if (offset + v->len > gap.begin) { + base_offset = gap.begin - offset; + break; + } + + offset += v->len; + } + + if (idx == fr->datacnt) { + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + + assert(gap.begin == offset + base_offset); + + end_idx = idx; + end_offset = offset; + end_base_offset = 0; + + for (; end_idx < fr->datacnt; ++end_idx) { + v = &fr->data[end_idx]; + if (end_offset + v->len > gap.end) { + end_base_offset = gap.end - end_offset; + break; + } + + end_offset += v->len; + } + + if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { + *pfrc = frc; + return 0; + } + + if (fr->datacnt == end_idx) { + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + fr->data[0].base += base_offset; + fr->data[0].len -= base_offset; + + *pfrc = frc; + return 0; + } + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, + conn->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + memcpy(nfr->data, fr->data + end_idx, + sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); + + assert(nfr->data[0].len > end_base_offset); + + nfr->offset = end_offset + end_base_offset; + nfr->datacnt = fr->datacnt - end_idx; + nfr->data[0].base += end_base_offset; + nfr->data[0].len -= end_base_offset; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset), nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + if (end_base_offset) { + ++end_idx; + } + + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + if (end_base_offset) { + assert(fr->data[fr->datacnt - 1].len > end_base_offset); + fr->data[fr->datacnt - 1].len = end_base_offset; + } + fr->data[0].base += base_offset; + fr->data[0].len -= base_offset; + + *pfrc = frc; + return 0; + } + + return 0; +} +static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, + ngtcp2_pktns *pktns, size_t left) { + ngtcp2_crypto *fr, *nfr; + ngtcp2_frame_chain *frc, *nfrc; + int rv; + size_t nmerged; + size_t datalen; + ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT]; + ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT]; + size_t acnt, bcnt; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + rv = conn_cryptofrq_unacked_pop(conn, pktns, &frc); + if (rv != 0) { + return rv; + } + if (frc == NULL) { + *pfrc = NULL; + return 0; + } + + fr = &frc->fr.crypto; + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (datalen > left) { + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + bcnt = 0; + ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_CRYPTO_DATACNT); + + assert(acnt > 0); + assert(bcnt > 0); + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + nfr->type = NGTCP2_FRAME_CRYPTO; + nfr->offset = fr->offset + left; + nfr->datacnt = bcnt; + ngtcp2_vec_copy(nfr->data, b, bcnt); + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset), nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, conn->mem); + + *pfrc = nfrc; + + return 0; + } + + left -= datalen; + + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + for (; left && ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); + nfrc = ngtcp2_ksl_it_get(&it); + nfr = &nfrc->fr.crypto; + + if (nfr->offset != fr->offset + datalen) { + assert(fr->offset + datalen < nfr->offset); + break; + } + + rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + if (nfrc == NULL) { + break; + } + + nfr = &nfrc->fr.crypto; + + nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, + NGTCP2_MAX_CRYPTO_DATACNT); + if (nmerged == 0) { + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset), nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + break; + } + + datalen += nmerged; + left -= nmerged; + + if (nfr->datacnt == 0) { + ngtcp2_frame_chain_del(nfrc, conn->mem); + continue; + } + + nfr->offset += nmerged; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset), nfrc); + if (rv != 0) { + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + break; + } + + if (acnt == fr->datacnt) { + assert(acnt > 0); + fr->data[acnt - 1] = a[acnt - 1]; + + *pfrc = frc; + return 0; + } + + assert(acnt > fr->datacnt); + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, conn->mem); + + *pfrc = nfrc; + + return 0; +} + +/* + * conn_verify_dcid verifies that destination connection ID in |hd| is + * valid for the connection. If it is successfully verified and the + * remote endpoint uses new DCID in the packet, nonzero value is + * assigned to |*pnew_cid_used| if it is not NULL. Otherwise 0 is + * assigned to it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + * |dcid| is not known to the local endpoint. + */ +static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, + const ngtcp2_pkt_hd *hd) { + ngtcp2_ksl_key key; + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + int rv; + + it = ngtcp2_ksl_lower_bound(&conn->scid.set, + ngtcp2_ksl_key_ptr(&key, &hd->dcid)); + if (ngtcp2_ksl_it_end(&it)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + scid = ngtcp2_ksl_it_get(&it); + if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) { + scid->flags |= NGTCP2_SCID_FLAG_USED; + + if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) { + rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe); + if (rv != 0) { + return rv; + } + } + + if (pnew_cid_used) { + *pnew_cid_used = 1; + } + } else if (pnew_cid_used) { + *pnew_cid_used = 0; + } + + return 0; +} + +/* + * conn_should_pad_pkt returns nonzero if the packet should be padded. + * |type| is the type of packet. |left| is the space left in packet + * buffer. |early_datalen| is the number of bytes which will be sent + * in the next, coalesced 0-RTT packet. + */ +static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, + size_t early_datalen) { + size_t min_payloadlen; + + if (conn->server || type != NGTCP2_PKT_INITIAL) { + return 0; + } + + if (!conn->early.ckm || early_datalen == 0) { + return 1; + } + min_payloadlen = ngtcp2_min(early_datalen, 128); + + return left < + /* TODO Assuming that pkt_num is encoded in 1 byte. */ + NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + + conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + + min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; +} + +static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + conn->idle_ts = ts; + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; +} + +static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + conn->idle_ts = ts; + conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; +} + +/* + * conn_write_handshake_pkt writes handshake packet in the buffer + * pointed by |dest| whose length is |destlen|. |type| specifies long + * packet type. It should be either NGTCP2_PKT_INITIAL or + * NGTCP2_PKT_HANDSHAKE_PKT. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_handshake_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, uint8_t type, + size_t early_datalen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_frame_chain *frq = NULL, **pfrc = &frq; + ngtcp2_frame_chain *nfrc; + ngtcp2_frame *ackfr = NULL, lfr; + ngtcp2_ssize spktlen; + ngtcp2_crypto_cc cc; + ngtcp2_rtb_entry *rtbent; + ngtcp2_pktns *pktns; + size_t left; + uint8_t rtb_entry_flags = NGTCP2_RTB_FLAG_NONE; + int pkt_empty = 1; + int padded = 0; + int hd_logged = 0; + + switch (type) { + case NGTCP2_PKT_INITIAL: + if (!conn->in_pktns) { + return 0; + } + assert(conn->in_pktns->crypto.tx.ckm); + pktns = conn->in_pktns; + cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + break; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { + return 0; + } + pktns = conn->hs_pktns; + cc.aead_overhead = conn->crypto.aead_overhead; + break; + default: + assert(0); + } + + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_key = pktns->crypto.tx.hp_key; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, + &conn->dcid.current.cid, &conn->oscid, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), + conn->version, 0); + + if (type == NGTCP2_PKT_INITIAL && ngtcp2_buf_len(&conn->token)) { + hd.token = conn->token.pos; + hd.tokenlen = ngtcp2_buf_len(&conn->token); + } + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return 0; + } + + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(&ppe); + left = ngtcp2_pkt_crypto_max_datalen( + conn_cryptofrq_top(conn, pktns)->fr.crypto.offset, left, left); + + if (left == (size_t)-1) { + break; + } + + rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + if (nfrc == NULL) { + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= + NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_CRYPTO_PKT; + } + + rv = conn_create_ack_frame(conn, &ackfr, &pktns->acktr, type, ts, + /* ack_delay = */ 0, + NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + if (ackfr) { + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack); + pkt_empty = 0; + } + } + + /* Don't send any PING frame if client Initial has not been + acknowledged yet. */ + if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && + pktns->rtb.probe_pkt_left && + (type != NGTCP2_PKT_INITIAL || + ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING | NGTCP2_RTB_FLAG_PROBE; + pkt_empty = 0; + } + } + + if (!pkt_empty) { + if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + /* The intention of smaller limit is get more chance to measure + RTT samples in early phase. */ + if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } + } else { + ++pktns->tx.num_non_ack_pkt; + } + } else { + pktns->tx.num_non_ack_pkt = 0; + } + } + + if (pkt_empty) { + return 0; + } + + /* If we cannot write another packet, then we need to add padding to + Initial here. */ + if (conn_should_pad_pkt(conn, type, ngtcp2_ppe_left(&ppe), early_datalen)) { + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } + + if (lfr.padding.len) { + padded = 1; + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + spktlen = ngtcp2_ppe_final(&ppe, NULL); + if (spktlen < 0) { + assert(ngtcp2_err_is_fatal((int)spktlen)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return spktlen; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen); + + if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { + rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, + rtb_entry_flags, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); + if (rv != 0) { + ngtcp2_rtb_entry_del(rtbent, conn->mem); + return rv; + } + + if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && + (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { + conn_restart_timer_on_write(conn, ts); + } + } + + if (pktns->rtb.probe_pkt_left && + (rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + --pktns->rtb.probe_pkt_left; + } + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++pktns->tx.last_pkt_num; + + return spktlen; +} + +/* + * conn_write_ack_pkt writes QUIC packet for type |type| which only + * includes ACK frame in the buffer pointed by |dest| whose length is + * |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, uint8_t type, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_frame *ackfr; + ngtcp2_pktns *pktns; + ngtcp2_duration ack_delay; + uint64_t ack_delay_exponent; + + assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + + switch (type) { + case NGTCP2_PKT_INITIAL: + assert(conn->server); + pktns = conn->in_pktns; + ack_delay = 0; + ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + break; + case NGTCP2_PKT_HANDSHAKE: + pktns = conn->hs_pktns; + ack_delay = 0; + ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + break; + case NGTCP2_PKT_SHORT: + pktns = &conn->pktns; + ack_delay = conn_compute_ack_delay(conn); + ack_delay_exponent = + conn->local.settings.transport_params.ack_delay_exponent; + break; + default: + assert(0); + } + + if (!pktns->crypto.tx.ckm) { + return 0; + } + + ackfr = NULL; + rv = conn_create_ack_frame(conn, &ackfr, &pktns->acktr, type, ts, ack_delay, + ack_delay_exponent); + if (rv != 0) { + return rv; + } + + if (!ackfr) { + return 0; + } + + return ngtcp2_conn_write_single_frame_pkt(conn, dest, destlen, type, + &conn->dcid.current.cid, ackfr, + NGTCP2_RTB_FLAG_NONE, ts); +} + +/* + * conn_write_handshake_ack_pkts writes packets which contain ACK + * frame only. This function writes at most 2 packets for each + * Initial and Handshake packet. + */ +static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize res = 0, nwrite = 0; + + /* Actullay client never send ACK for server Initial. This is + because once it gets server Initial, it gets Handshake tx key and + discards Initial key. */ + if (conn->server && conn->in_pktns) { + nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (conn->hs_pktns->crypto.tx.ckm) { + nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + res += nwrite; + } + + return res; +} + +/* + * conn_write_client_initial writes Initial packet in the buffer + * pointed by |dest| whose length is |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, + size_t early_datalen, + ngtcp2_tstamp ts) { + int rv; + + assert(conn->callbacks.client_initial); + + rv = conn->callbacks.client_initial(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, + early_datalen, ts); +} + +/* + * conn_write_handshake_pkts writes Initial and Handshake packets in + * the buffer pointed by |dest| whose length is |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, + size_t early_datalen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; + + nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, + early_datalen, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + if (!conn->server && nwrite) { + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + + nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_HANDSHAKE, + 0, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + + return res; +} + +static ngtcp2_ssize conn_write_server_handshake(ngtcp2_conn *conn, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; + + nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + + /* Acknowledge 0-RTT packet here. */ + if (conn->pktns.crypto.tx.ckm) { + nwrite = conn_write_ack_pkt(conn, dest, destlen, NGTCP2_PKT_SHORT, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + } + + return res; +} + +/* + * conn_initial_stream_rx_offset returns the initial maximum offset of + * data for a stream denoted by |stream_id|. + */ +static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn, + int64_t stream_id) { + int local_stream = conn_local_stream(conn, stream_id); + + if (bidi_stream(stream_id)) { + if (local_stream) { + return conn->local.settings.transport_params + .initial_max_stream_data_bidi_local; + } + return conn->local.settings.transport_params + .initial_max_stream_data_bidi_remote; + } + + if (local_stream) { + return 0; + } + return conn->local.settings.transport_params.initial_max_stream_data_uni; +} + +/* + * conn_should_send_max_stream_data returns nonzero if MAX_STREAM_DATA + * frame should be send for |strm|. + */ +static int conn_should_send_max_stream_data(ngtcp2_conn *conn, + ngtcp2_strm *strm) { + uint64_t win = conn_initial_stream_rx_offset(conn, strm->stream_id); + uint64_t inc = strm->rx.unsent_max_offset - strm->rx.max_offset; + + return win < 2 * inc || inc >= 10 * NGTCP2_MAX_DGRAM_SIZE; +} + +/* + * conn_should_send_max_data returns nonzero if MAX_DATA frame should + * be sent. + */ +static int conn_should_send_max_data(ngtcp2_conn *conn) { + uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset; + + return conn->local.settings.transport_params.initial_max_data < 2 * inc || + inc >= 10 * NGTCP2_MAX_DGRAM_SIZE; +} + +/* + * conn_required_num_new_connection_id returns the number of + * additional connection ID the local endpoint has to provide to the + * remote endpoint. + */ +static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { + size_t n, len = ngtcp2_ksl_len(&conn->scid.set); + + if (len >= NGTCP2_MAX_SCID_POOL_SIZE) { + return 0; + } + + /* len includes retired CID. We don't provide extra CID if doing so + excceds NGTCP2_MAX_SCID_POOL_SIZE. */ + + n = conn->remote.transport_params.active_connection_id_limit + + conn->scid.num_retired; + + return ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; +} + +/* + * conn_enqueue_new_connection_id generates additional connection IDs + * and prepares to send them to the remote endpoint. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { + size_t i, need = conn_required_num_new_connection_id(conn); + size_t cidlen = conn->oscid.datalen; + ngtcp2_cid cid; + uint64_t seq; + int rv; + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + ngtcp2_frame_chain *nfrc; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_scid *scid; + ngtcp2_ksl_key key; + ngtcp2_ksl_it it; + + for (i = 0; i < need; ++i) { + rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen); + if (rv != 0) { + return rv; + } + + if (cid.datalen != cidlen) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* Assert uniqueness */ + it = + ngtcp2_ksl_lower_bound(&conn->scid.set, ngtcp2_ksl_key_ptr(&key, &cid)); + if (!ngtcp2_ksl_it_end(&it) && + ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it).ptr, &cid)) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + seq = ++conn->scid.last_seq; + + scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid)); + if (scid == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_scid_init(scid, seq, &cid, token); + + rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scid->cid), scid); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, scid); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + nfrc->fr.new_connection_id.seq = seq; + nfrc->fr.new_connection_id.retire_prior_to = 0; + nfrc->fr.new_connection_id.cid = cid; + memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token, + sizeof(token)); + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + } + + return 0; +} + +/* + * conn_compute_pto computes the current PTO. + */ +static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_duration var = ngtcp2_max(4 * rcs->rttvar, NGTCP2_GRANULARITY); + ngtcp2_duration max_ack_delay = + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) + ? conn->remote.transport_params.max_ack_delay + : NGTCP2_DEFAULT_MAX_ACK_DELAY; + return rcs->smoothed_rtt + var + max_ack_delay; +} + +/* + * conn_remove_retired_connection_id removes the already retired + * connection ID. It waits RTT * 2 before actually removing a + * connection ID after it receives RETIRE_CONNECTION_ID from peer to + * catch reordered packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_remove_retired_connection_id(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + ngtcp2_duration timeout = conn_compute_pto(conn); + ngtcp2_scid *scid; + ngtcp2_ksl_key key; + ngtcp2_dcid *dcid; + int rv; + + for (; !ngtcp2_pq_empty(&conn->scid.used);) { + scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); + + if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { + break; + } + + assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED); + + rv = conn_call_remove_connection_id(conn, &scid->cid); + if (rv != 0) { + return rv; + } + + ngtcp2_ksl_remove(&conn->scid.set, NULL, + ngtcp2_ksl_key_ptr(&key, &scid->cid)); + ngtcp2_pq_pop(&conn->scid.used); + ngtcp2_mem_free(conn->mem, scid); + + assert(conn->scid.num_retired); + --conn->scid.num_retired; + } + + for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + if (dcid->ts_retired + timeout >= ts) { + break; + } + + rv = conn_call_deactivate_dcid(conn, dcid); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.retired); + } + + return 0; +} + +/* + * conn_min_short_pktlen returns the minimum length of Short packet + * this endpoint sends. + */ +static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { + return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; +} + +typedef enum { + NGTCP2_WRITE_PKT_FLAG_NONE = 0x00, + /* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet + should be padded */ + NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING = 0x01, + /* NGTCP2_WRITE_PKT_FLAG_STREAM_MORE indicates that more stream DATA + may come and it should be encoded into the current packet. */ + NGTCP2_WRITE_PKT_FLAG_STREAM_MORE = 0x02, +} ngtcp2_write_pkt_flag; + +/* + * conn_write_pkt writes a protected packet in the buffer pointed by + * |dest| whose length if |destlen|. |type| specifies the type of + * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. + * + * This function can send new stream data. In order to send stream + * data, specify the underlying stream to |data_strm|. If |fin| is + * set to nonzero, it signals that the given data is the final portion + * of the stream. |datav| vector of length |datavcnt| specify stream + * data to send. If no stream data to send, set |strm| to NULL. The + * number of bytes sent to the stream is assigned to |*pdatalen|. If + * 0 length STREAM data is sent, 0 is assigned to |*pdatalen|. The + * caller should initialize |*pdatalen| to -1. + * + * If |require_padding| is nonzero, padding bytes are added to occupy + * the remaining packet payload. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_STREAM_DATA_BLOCKED + * Stream data could not be written because of flow control. + */ +static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, + uint8_t type, ngtcp2_strm *data_strm, + int fin, const ngtcp2_vec *datav, + size_t datavcnt, uint8_t flags, + ngtcp2_tstamp ts) { + int rv = 0; + ngtcp2_crypto_cc *cc = &conn->pkt.cc; + ngtcp2_ppe *ppe = &conn->pkt.ppe; + ngtcp2_pkt_hd *hd = &conn->pkt.hd; + ngtcp2_frame *ackfr = NULL, lfr; + ngtcp2_ssize nwrite; + ngtcp2_frame_chain **pfrc, *nfrc, *frc; + ngtcp2_rtb_entry *ent; + ngtcp2_strm *strm; + int pkt_empty = 1; + size_t ndatalen = 0; + int send_stream = 0; + int stream_blocked = 0; + ngtcp2_pktns *pktns = &conn->pktns; + size_t left; + size_t datalen = ngtcp2_vec_len(datav, datavcnt); + ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; + size_t datacnt; + uint8_t rtb_entry_flags = NGTCP2_RTB_FLAG_NONE; + int hd_logged = 0; + ngtcp2_path_challenge_entry *pcent; + uint8_t hd_flags; + int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; + int stream_more = (flags & NGTCP2_WRITE_PKT_FLAG_STREAM_MORE) != 0; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + size_t min_pktlen = conn_min_short_pktlen(conn); + int padded = 0; + int credit_expanded = 0; + + /* Return 0 if destlen is less than minimum packet length which can + trigger Stateless Reset */ + if (destlen < min_pktlen) { + return 0; + } + + if (data_strm) { + ndatalen = conn_enforce_flow_control(conn, data_strm, datalen); + /* 0 length STREAM frame is allowed */ + if (ndatalen || datalen == 0) { + send_stream = 1; + } else { + stream_blocked = 1; + } + } + + if (!ppe_pending) { + switch (type) { + case NGTCP2_PKT_SHORT: + hd_flags = + (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE; + cc->ckm = pktns->crypto.tx.ckm; + cc->hp_key = pktns->crypto.tx.hp_key; + + /* transport parameter is only valid after handshake completion + which means we don't know how many connection ID that remote + peer can accept before handshake completion. */ + if (conn->oscid.datalen && + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + rv = conn_enqueue_new_connection_id(conn); + if (rv != 0) { + return rv; + } + } + + break; + case NGTCP2_PKT_0RTT: + assert(!conn->server); + if (!conn->early.ckm) { + return 0; + } + hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; + cc->ckm = conn->early.ckm; + cc->hp_key = conn->early.hp_key; + break; + default: + /* Unreachable */ + assert(0); + } + + cc->aead = pktns->crypto.ctx.aead; + cc->hp = pktns->crypto.ctx.hp; + cc->aead_overhead = conn->crypto.aead_overhead; + cc->encrypt = conn->callbacks.encrypt; + cc->hp_mask = conn->callbacks.hp_mask; + + /* TODO Take into account stream frames */ + if ((pktns->tx.frq || send_stream || + ngtcp2_ringbuf_len(&conn->rx.path_challenge) || + conn_should_send_max_data(conn)) && + conn->rx.unsent_max_offset > conn->rx.max_offset) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; + nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + conn->rx.max_offset = conn->rx.unsent_max_offset; + credit_expanded = 1; + } + + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, + &conn->oscid, pktns->tx.last_pkt_num + 1, + pktns_select_pkt_numlen(pktns), conn->version, 0); + + ngtcp2_ppe_init(ppe, dest, destlen, cc); + + rv = ngtcp2_ppe_encode_hd(ppe, hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(ppe)) { + return 0; + } + + if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + + lfr.type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(lfr.path_response.data, pcent->data, + sizeof(lfr.path_response.data)); + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + /* We don't retransmit PATH_RESPONSE. */ + } + } + + rv = conn_create_ack_frame( + conn, &ackfr, &pktns->acktr, type, ts, conn_compute_ack_delay(conn), + conn->local.settings.transport_params.ack_delay_exponent); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (ackfr) { + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, + ackfr->ack.largest_ack); + pkt_empty = 0; + } + } + + for (pfrc = &pktns->tx.frq; *pfrc;) { + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STOP_SENDING: + strm = + ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_STREAM: + assert(0); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.bidi.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAMS_UNI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.uni.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + strm = ngtcp2_conn_find_stream(conn, + (*pfrc)->fr.max_stream_data.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || + (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_DATA: + if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_CRYPTO: + assert(0); + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } + + if ((*pfrc)->fr.type == NGTCP2_FRAME_RETIRE_CONNECTION_ID) { + assert(conn->dcid.num_retire_queued); + --conn->dcid.num_retire_queued; + } + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + } + + if (rv != NGTCP2_ERR_NOBUF) { + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_crypto_max_datalen( + conn_cryptofrq_top(conn, pktns)->fr.crypto.offset, left, left); + + if (left == (size_t)-1) { + break; + } + + rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (nfrc == NULL) { + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + } + } + + /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream + ID space in one packet. */ + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL && + conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) { + rv = conn_call_extend_max_remote_streams_bidi( + conn, conn->remote.bidi.unsent_max_streams); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; + nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams; + *pfrc = nfrc; + + conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + } + } + + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) { + if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) { + rv = conn_call_extend_max_remote_streams_uni( + conn, conn->remote.uni.unsent_max_streams); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; + nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams; + *pfrc = nfrc; + + conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, + &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + } + } + } + + if (rv != NGTCP2_ERR_NOBUF) { + for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { + strm = ngtcp2_conn_tx_strmq_top(conn); + + if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + strm->rx.max_offset < strm->rx.unsent_max_offset) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + nfrc->fr.max_stream_data.stream_id = strm->stream_id; + nfrc->fr.max_stream_data.max_stream_data = strm->rx.unsent_max_offset; + ngtcp2_list_insert(nfrc, pfrc); + + rv = + conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } + + pkt_empty = 0; + credit_expanded = 1; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pfrc = &(*pfrc)->next; + strm->rx.max_offset = strm->rx.unsent_max_offset; + } + + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_stream_max_datalen( + strm->stream_id, ngtcp2_strm_streamfrq_top(strm)->fr.stream.offset, + left, left); + + if (left == (size_t)-1) { + break; + } + + rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (nfrc == NULL) { + /* TODO Why? */ + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + ngtcp2_conn_tx_strmq_pop(conn); + ++strm->cycle; + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + } + } + + /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to + decrease packet count. */ + if (ackfr == NULL && credit_expanded) { + rv = conn_create_ack_frame( + conn, &ackfr, &pktns->acktr, type, ts, /* ack_delay = */ 0, + conn->local.settings.transport_params.ack_delay_exponent); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (ackfr) { + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, + ackfr->ack.largest_ack); + } + } + } + } else { + pfrc = conn->pkt.pfrc; + rtb_entry_flags |= conn->pkt.rtb_entry_flags; + pkt_empty = conn->pkt.pkt_empty; + hd_logged = conn->pkt.hd_logged; + } + + left = ngtcp2_ppe_left(ppe); + + if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL && + (ndatalen = ngtcp2_pkt_stream_max_datalen(data_strm->stream_id, + data_strm->tx.offset, ndatalen, + left)) != (size_t)-1 && + (ndatalen || datalen == 0)) { + datacnt = ngtcp2_vec_copy_at_most( + data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, datav, datavcnt, ndatalen); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + nfrc->fr.stream.type = NGTCP2_FRAME_STREAM; + nfrc->fr.stream.flags = 0; + nfrc->fr.stream.stream_id = data_strm->stream_id; + nfrc->fr.stream.offset = data_strm->tx.offset; + nfrc->fr.stream.datacnt = datacnt; + ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt); + + fin = fin && ndatalen == datalen; + nfrc->fr.stream.fin = (uint8_t)fin; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + + data_strm->tx.offset += ndatalen; + conn->tx.offset += ndatalen; + + if (fin) { + ngtcp2_strm_shutdown(data_strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + + if (pdatalen) { + *pdatalen = (ngtcp2_ssize)ndatalen; + } + } else { + send_stream = 0; + } + + if (pkt_empty) { + assert(rv == 0 || NGTCP2_ERR_NOBUF == rv); + if (rv == 0 && stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + return 0; + } + + if (stream_more) { + conn->pkt.pfrc = pfrc; + conn->pkt.pkt_empty = pkt_empty; + conn->pkt.rtb_entry_flags = rtb_entry_flags; + conn->pkt.hd_logged = hd_logged; + conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; + + if (stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + if (send_stream) { + return NGTCP2_ERR_WRITE_STREAM_MORE; + } + } + + if (!(rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + if (pktns->rtb.probe_pkt_left || + pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= NGTCP2_RTB_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } + } else { + ++pktns->tx.num_non_ack_pkt; + } + } else { + pktns->tx.num_non_ack_pkt = 0; + } + + /* TODO Push STREAM frame back to ngtcp2_strm if there is an error + before ngtcp2_rtb_entry is safely created and added. */ + lfr.type = NGTCP2_FRAME_PADDING; + if ((require_padding || + (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && + ngtcp2_ppe_left(ppe)) { + lfr.padding.len = ngtcp2_ppe_padding(ppe); + } else { + lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); + } + + if (lfr.padding.len) { + padded = 1; + ngtcp2_log_tx_fr(&conn->log, hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(ppe, NULL); + if (nwrite < 0) { + assert(ngtcp2_err_is_fatal((int)nwrite)); + return nwrite; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); + + if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { + rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal((int)nwrite)); + return rv; + } + + if (*pfrc != pktns->tx.frq) { + ent->frc = pktns->tx.frq; + pktns->tx.frq = *pfrc; + *pfrc = NULL; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_rtb_entry_del(ent, conn->mem); + return rv; + } + + if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && + (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { + conn_restart_timer_on_write(conn, ts); + } + } + + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++pktns->tx.last_pkt_num; + + return nwrite; +} + +ngtcp2_ssize +ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, uint8_t type, + const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint8_t rtb_flags, ngtcp2_tstamp ts) { + int rv; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_frame lfr; + ngtcp2_ssize nwrite; + ngtcp2_crypto_cc cc; + ngtcp2_pktns *pktns; + uint8_t flags; + ngtcp2_rtb_entry *rtbent; + int padded = 0; + size_t pktlen; + + switch (type) { + case NGTCP2_PKT_INITIAL: + pktns = conn->in_pktns; + cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + flags = NGTCP2_PKT_FLAG_LONG_FORM; + break; + case NGTCP2_PKT_HANDSHAKE: + pktns = conn->hs_pktns; + cc.aead_overhead = conn->crypto.aead_overhead; + flags = NGTCP2_PKT_FLAG_LONG_FORM; + break; + case NGTCP2_PKT_SHORT: + /* 0 means Short packet. */ + pktns = &conn->pktns; + cc.aead_overhead = conn->crypto.aead_overhead; + flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE; + break; + default: + /* We don't support 0-RTT packet in this function. */ + assert(0); + } + + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_key = pktns->crypto.tx.hp_key; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + + ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), + conn->version, 0); + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return 0; + } + + ngtcp2_log_tx_pkt_hd(&conn->log, &hd); + ngtcp2_qlog_pkt_sent_start(&conn->qlog, &hd); + + rv = conn_ppe_write_frame(conn, &ppe, &hd, fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + lfr.type = NGTCP2_FRAME_PADDING; + if (type == NGTCP2_PKT_SHORT) { + lfr.padding.len = + ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + } else { + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } + if (lfr.padding.len) { + if (fr->type == NGTCP2_FRAME_ACK) { + /* If PADDING is added, the packet suddenly gets CWND + limited. */ + pktlen = ngtcp2_ppe_pktlen(&ppe); + if (pktlen > conn_cwnd_left(conn)) { + return 0; + } + } + + padded = 1; + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(&ppe, NULL); + if (nwrite < 0) { + return nwrite; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); + + /* Do this when we are sure that there is no error. */ + if (fr->type == NGTCP2_FRAME_ACK) { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); + } + + if ((rtb_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) { + rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); + if (rv != 0) { + ngtcp2_rtb_entry_del(rtbent, conn->mem); + return rv; + } + + if ((rtb_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) && + (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { + conn_restart_timer_on_write(conn, ts); + } + } + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++pktns->tx.last_pkt_num; + + return nwrite; +} + +/* + * conn_process_early_rtb makes any pending 0RTT packet Short packet. + */ +static void conn_process_early_rtb(ngtcp2_conn *conn) { + ngtcp2_rtb_entry *ent; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_ksl_it it; + + for (it = ngtcp2_rtb_head(rtb); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + if ((ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) == 0 || + ent->hd.type != NGTCP2_PKT_0RTT) { + continue; + } + + /* 0-RTT packet is retransmitted as a Short packet. */ + ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; + ent->hd.type = NGTCP2_PKT_SHORT; + } +} + +/* + * conn_write_probe_ping writes probe packet containing PING frame + * (and optionally ACK frame) to the buffer pointed by |dest| of + * length |destlen|. Probe packet is always Short packet. This + * function might return 0 if it cannot write packet (e.g., |destlen| + * is too small). + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static ngtcp2_ssize conn_write_probe_ping(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_crypto_cc cc; + ngtcp2_frame_chain *frc = NULL; + ngtcp2_rtb_entry *ent; + ngtcp2_frame *ackfr = NULL, lfr; + int rv; + ngtcp2_ssize nwrite; + + assert(pktns->crypto.tx.ckm); + + cc.aead_overhead = conn->crypto.aead_overhead; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_key = pktns->crypto.tx.hp_key; + + ngtcp2_pkt_hd_init( + &hd, + (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE, + NGTCP2_PKT_SHORT, &conn->dcid.current.cid, NULL, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), conn->version, + 0); + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return 0; + } + + ngtcp2_log_tx_pkt_hd(&conn->log, &hd); + ngtcp2_qlog_pkt_sent_start(&conn->qlog, &hd); + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame(conn, &ppe, &hd, &frc->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + rv = 0; + goto fail; + } + + rv = conn_create_ack_frame( + conn, &ackfr, &pktns->acktr, NGTCP2_PKT_SHORT, ts, + conn_compute_ack_delay(conn), + conn->local.settings.transport_params.ack_delay_exponent); + if (rv != 0) { + goto fail; + } + + if (ackfr) { + rv = conn_ppe_write_frame(conn, &ppe, &hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack); + } + } + + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + if (lfr.padding.len) { + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(&ppe, NULL); + if (nwrite < 0) { + rv = (int)nwrite; + goto fail; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); + + rv = ngtcp2_rtb_entry_new( + &ent, &hd, frc, ts, (size_t)nwrite, + NGTCP2_RTB_FLAG_PROBE | NGTCP2_RTB_FLAG_ACK_ELICITING, conn->mem); + if (rv != 0) { + goto fail; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); + if (rv != 0) { + ngtcp2_rtb_entry_del(ent, conn->mem); + return rv; + } + + if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { + conn_restart_timer_on_write(conn, ts); + } + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + ++pktns->tx.last_pkt_num; + + return nwrite; + +fail: + ngtcp2_frame_chain_del(frc, conn->mem); + + return rv; +} + +/* + * conn_write_probe_pkt writes a QUIC Short packet as probe packet. + * The packet is written to the buffer pointed by |dest| of length + * |destlen|. This function can send new stream data. In order to + * send stream data, specify the underlying stream to |strm|. If + * |fin| is set to nonzero, it signals that the given data is the + * final portion of the stream. |datav| vector of length |datavcnt| + * specify stream data to send. If no stream data to send, set |strm| + * to NULL. The number of bytes sent to the stream is assigned to + * |*pdatalen|. If 0 length STREAM data is sent, 0 is assigned to + * |*pdatalen|. The caller should initialize |*pdatalen| to -1. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest|, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_STREAM_DATA_BLOCKED + * Stream data could not be written because of flow control. + */ +static ngtcp2_ssize conn_write_probe_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, + ngtcp2_strm *strm, int fin, + const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "transmit probe pkt left=%zu", + conn->pktns.rtb.probe_pkt_left); + + /* a probe packet is not blocked by cwnd. */ + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, NGTCP2_WRITE_PKT_FLAG_NONE, ts); + if (nwrite == 0 || nwrite == NGTCP2_ERR_STREAM_DATA_BLOCKED) { + nwrite = conn_write_probe_ping(conn, dest, destlen, ts); + } + if (nwrite <= 0) { + return nwrite; + } + + --conn->pktns.rtb.probe_pkt_left; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", + nwrite); + + return nwrite; +} + +/* + * conn_handshake_remnants_left returns nonzero if there may be + * handshake packets the local endpoint has to send, including new + * packets and lost ones. + */ +static int conn_handshake_remnants_left(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + + return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + (in_pktns && (ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) || + ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || + (hs_pktns && (ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) || + ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); +} + +/* + * conn_retire_dcid_seq retires destination connection ID denoted by + * |seq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + nfrc->fr.retire_connection_id.seq = seq; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + ++conn->dcid.num_retire_queued; + + return 0; +} + +/* + * conn_retire_dcid retires |dcid|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts) { + ngtcp2_ringbuf *rb = &conn->dcid.retired; + ngtcp2_dcid *dest, *stale_dcid; + int rv; + + if (ngtcp2_ringbuf_full(rb)) { + stale_dcid = ngtcp2_ringbuf_get(rb, 0); + rv = conn_call_deactivate_dcid(conn, stale_dcid); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(rb); + } + + dest = ngtcp2_ringbuf_push_back(rb); + ngtcp2_dcid_copy(dest, dcid); + dest->ts_retired = ts; + + return conn_retire_dcid_seq(conn, dcid->seq); +} + +/* + * conn_stop_pv stops the path validation which is currently running. + * This function does nothing if no path validation is currently being + * performed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv = 0; + ngtcp2_pv *pv = conn->pv; + + if (pv == NULL) { + return 0; + } + + if (pv->dcid.seq != conn->dcid.current.seq) { + rv = conn_retire_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + goto fin; + } + } + + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq != pv->dcid.seq) { + rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); + if (rv != 0) { + goto fin; + } + } + +fin: + ngtcp2_pv_del(pv); + conn->pv = NULL; + + return rv; +} + +static void conn_reset_congestion_state(ngtcp2_conn *conn); + +/* + * conn_on_path_validation_failed is called when path validation + * fails. This function may delete |pv|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, + ngtcp2_tstamp ts) { + int rv; + + rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE); + if (rv != 0) { + return rv; + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); + conn_reset_congestion_state(conn); + } + + return conn_stop_pv(conn, ts); +} + +/* + * conn_write_path_challenge writes a packet which includes + * PATH_CHALLENGE frame into |dest| of length |destlen|. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, + ngtcp2_path *path, uint8_t *dest, + size_t destlen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_tstamp expiry; + ngtcp2_pv *pv = conn->pv; + ngtcp2_frame lfr; + + ngtcp2_pv_ensure_start(pv, ts); + + if (ngtcp2_pv_validation_timed_out(pv, ts)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path validation was timed out"); + return conn_on_path_validation_failed(conn, pv, ts); + } + + ngtcp2_pv_handle_entry_expiry(pv, ts); + + if (ngtcp2_pv_full(pv)) { + return 0; + } + + if (path) { + ngtcp2_path_copy(path, &pv->dcid.ps.path); + } + + assert(conn->callbacks.rand); + rv = conn->callbacks.rand(conn, lfr.path_challenge.data, + sizeof(lfr.path_challenge.data), + NGTCP2_RAND_CTX_PATH_CHALLENGE, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; + + /* TODO reconsider this. This might get larger pretty quickly than + validation timeout which is just around 3*PTO. */ + expiry = ts + 6ULL * NGTCP2_DEFAULT_INITIAL_RTT * (1ULL << pv->loss_count); + + ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry); + + return ngtcp2_conn_write_single_frame_pkt( + conn, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, + NGTCP2_RTB_FLAG_ACK_ELICITING, ts); +} + +ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + return ngtcp2_conn_writev_stream( + conn, path, dest, destlen, + /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE, + /* stream_id = */ -1, + /* fin = */ 0, /* datav = */ NULL, /* datavcnt = */ 0, ts); +} + +/* + * conn_on_version_negotiation is called when Version Negotiation + * packet is received. The function decodes the data in the buffer + * pointed by |payload| whose length is |payloadlen| as Version + * Negotiation packet payload. The packet header is given in |hd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_INVALID_ARGUMENT + * Packet payload is badly formatted. + */ +static int conn_on_version_negotiation(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint8_t *payload, + size_t payloadlen) { + uint32_t sv[16]; + uint32_t *p; + int rv = 0; + size_t nsv; + + if (payloadlen % sizeof(uint32_t)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (payloadlen > sizeof(sv)) { + p = ngtcp2_mem_malloc(conn->mem, payloadlen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + } else { + p = sv; + } + + /* TODO Just move to the terminal state for now in order not to send + CONNECTION_CLOSE frame. */ + conn->state = NGTCP2_CS_DRAINING; + + nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen); + + ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); + + if (conn->callbacks.recv_version_negotiation) { + rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, + conn->user_data); + } + + if (p != sv) { + ngtcp2_mem_free(conn->mem, p); + } + + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { + ngtcp2_strm *strm; + + if (ngtcp2_pq_empty(&conn->tx.strmq)) { + return 0; + } + + strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); + return strm->cycle; +} + +/* + * conn_resched_frames reschedules frames linked from |*pfrc| for + * retransmission. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_frame_chain **pfrc) { + ngtcp2_frame_chain **first = pfrc; + ngtcp2_frame_chain *frc; + ngtcp2_stream *sfr; + ngtcp2_strm *strm; + ngtcp2_ksl_key key; + int rv; + + if (*pfrc == NULL) { + return 0; + } + + for (; *pfrc;) { + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STREAM: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + sfr = &frc->fr.stream; + + strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); + if (!strm) { + ngtcp2_frame_chain_del(frc, conn->mem); + break; + } + rv = ngtcp2_strm_streamfrq_push(strm, frc); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + if (!ngtcp2_strm_is_tx_queued(strm)) { + strm->cycle = conn_tx_strmq_first_cycle(conn); + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + return rv; + } + } + break; + case NGTCP2_FRAME_CRYPTO: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &frc->fr.crypto.offset), + frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + break; + default: + pfrc = &(*pfrc)->next; + } + } + + *pfrc = pktns->tx.frq; + pktns->tx.frq = *first; + + return 0; +} + +/* + * conn_on_retry is called when Retry packet is received. The + * function decodes the data in the buffer pointed by |pkt| whose + * length is |pktlen| as Retry packet. The length of long packet + * header is given in |hdpktlen|. |pkt| includes packet header. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_INVALID_ARGUMENT + * Packet payload is badly formatted. + * NGTCP2_ERR_PROTO + * ODCID does not match; or Token is empty. + */ +static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { + int rv; + ngtcp2_pkt_retry retry; + uint8_t *p; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_rtb *in_rtb = &in_pktns->rtb; + uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; + ngtcp2_frame_chain *frc = NULL; + + if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + return 0; + } + + rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen); + if (rv != 0) { + return rv; + } + + retry.odcid = conn->dcid.current.cid; + + rv = ngtcp2_pkt_verify_retry_tag(&retry, pkt, pktlen, conn->callbacks.encrypt, + &conn->crypto.retry_aead); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "unable to verify Retry packet integrity"); + return rv; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s", + (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data, + retry.odcid.datalen)); + + if (retry.tokenlen == 0) { + return NGTCP2_ERR_PROTO; + } + + if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) { + return 0; + } + + /* DCID must be updated before invoking callback because client + generates new initial keys there. */ + conn->dcid.current.cid = hd->scid; + + conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + + assert(conn->callbacks.recv_retry); + + rv = conn->callbacks.recv_retry(conn, hd, &retry, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + conn->state = NGTCP2_CS_CLIENT_INITIAL; + + /* Just freeing memory is dangerous because we might free twice. */ + + ngtcp2_rtb_remove_all(rtb, &frc); + + rv = conn_resched_frames(conn, &conn->pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + frc = NULL; + ngtcp2_rtb_remove_all(in_rtb, &frc); + + rv = conn_resched_frames(conn, in_pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + assert(conn->token.begin == NULL); + + p = ngtcp2_mem_malloc(conn->mem, retry.tokenlen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + ngtcp2_buf_init(&conn->token, p, retry.tokenlen); + + ngtcp2_cpymem(conn->token.begin, retry.token, retry.tokenlen); + conn->token.pos = conn->token.begin; + conn->token.last = conn->token.pos + retry.tokenlen; + + conn_reset_congestion_state(conn); + + return 0; +} + +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts) { + ngtcp2_frame_chain *frc = NULL; + int rv; + ngtcp2_duration pto = conn_compute_pto(conn); + + ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, &frc, rcs, pto, ts); + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + return 0; +} + +/* + * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_PROTO + * |fr| acknowledges a packet this endpoint has not sent. + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { + int rv; + ngtcp2_frame_chain *frc = NULL; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_ssize num_acked; + + if (pktns->tx.last_pkt_num < fr->largest_ack) { + return NGTCP2_ERR_PROTO; + } + + rv = ngtcp2_pkt_validate_ack(fr); + if (rv != 0) { + return rv; + } + + ngtcp2_acktr_recv_ack(&pktns->acktr, fr); + + num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, conn, pkt_ts, ts); + if (num_acked < 0) { + /* TODO assert this */ + assert(ngtcp2_err_is_fatal((int)num_acked)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return (int)num_acked; + } + + if (num_acked == 0) { + return 0; + } + + rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->rcs, ts); + if (rv != 0) { + return rv; + } + + rcs->pto_count = 0; + pktns->rtb.probe_pkt_left = 0; + + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; +} + +/* + * conn_assign_recved_ack_delay_unscaled assigns + * fr->ack_delay_unscaled. + */ +static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr, + uint64_t ack_delay_exponent) { + fr->ack_delay_unscaled = + fr->ack_delay * (1UL << ack_delay_exponent) * NGTCP2_MICROSECONDS; +} + +/* + * conn_recv_max_stream_data processes received MAX_STREAM_DATA frame + * |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * Stream ID indicates that it is a local stream, and the local + * endpoint has not initiated it; or stream is peer initiated + * unidirectional stream. + * NGTCP2_ERR_STREAM_LIMIT + * Stream ID exceeds allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_recv_max_stream_data(ngtcp2_conn *conn, + const ngtcp2_max_stream_data *fr) { + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + int rv; + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + + idtr = &conn->remote.uni.idtr; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + /* Stream has been closed. */ + return 0; + } + + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + /* Stream has been closed. */ + return 0; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + } + + if (strm->tx.max_offset < fr->max_stream_data) { + strm->tx.max_offset = fr->max_stream_data; + + /* Don't call callback if stream is half-closed local */ + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return 0; + } + + rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id, + fr->max_stream_data); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * conn_recv_max_data processes received MAX_DATA frame |fr|. + */ +static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) { + conn->tx.max_offset = ngtcp2_max(conn->tx.max_offset, fr->max_data); +} + +/* + * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from + * |*ppc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + const ngtcp2_path *path, const uint8_t *pkt, + size_t pktlen, ngtcp2_tstamp ts) { + int rv; + ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc; + size_t i; + for (i = 0; *ppc && i < NGTCP2_MAX_NUM_BUFFED_RX_PKTS; + ppc = &(*ppc)->next, ++i) + ; + + if (i == NGTCP2_MAX_NUM_BUFFED_RX_PKTS) { + return 0; + } + + rv = ngtcp2_pkt_chain_new(&pc, path, pkt, pktlen, ts, conn->mem); + if (rv != 0) { + return rv; + } + + *ppc = pc; + + return 0; +} + +/* + * conn_ensure_decrypt_buffer ensures that conn->crypto.decrypt_buf + * has at least |n| bytes space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) { + uint8_t *nbuf; + size_t len; + ngtcp2_vec *decrypt_buf = &conn->crypto.decrypt_buf; + + if (decrypt_buf->len >= n) { + return 0; + } + + len = decrypt_buf->len == 0 ? 2048 : decrypt_buf->len * 2; + for (; len < n; len *= 2) + ; + nbuf = ngtcp2_mem_realloc(conn->mem, decrypt_buf->base, len); + if (nbuf == NULL) { + return NGTCP2_ERR_NOMEM; + } + decrypt_buf->base = nbuf; + decrypt_buf->len = len; + + return 0; +} + +/* + * decrypt_pkt decrypts the data pointed by |payload| whose length is + * |payloadlen|, and writes plaintext data to the buffer pointed by + * |dest|. The buffer pointed by |ad| is the Additional Data, and its + * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is + * the cryptographic key, and iv to use. |decrypt| is a callback + * function which actually decrypts a packet. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_TLS_DECRYPT + * TLS backend failed to decrypt data. + */ +static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *payload, size_t payloadlen, + const uint8_t *ad, size_t adlen, + int64_t pkt_num, ngtcp2_crypto_km *ckm, + ngtcp2_decrypt decrypt, size_t aead_overhead) { + /* TODO nonce is limited to 64 bytes. */ + uint8_t nonce[64]; + int rv; + + assert(sizeof(nonce) >= ckm->iv.len); + + ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); + + rv = decrypt(dest, aead, payload, payloadlen, ckm->key.base, nonce, + ckm->iv.len, ad, adlen); + + if (rv != 0) { + if (rv == NGTCP2_ERR_TLS_DECRYPT) { + return rv; + } + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + assert(payloadlen >= aead_overhead); + + return (ngtcp2_ssize)(payloadlen - aead_overhead); +} + +/* + * decrypt_hp decryptes packet header. The packet number starts at + * |pkt| + |pkt_num_offset|. The entire plaintext QUIC packet header + * will be written to the buffer pointed by |dest| whose capacity is + * |destlen|. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_PROTO + * Packet is badly formatted + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed; or it does not return + * expected result. + */ +static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, size_t destlen, + const ngtcp2_crypto_cipher *hp, + const uint8_t *pkt, size_t pktlen, + size_t pkt_num_offset, ngtcp2_crypto_km *ckm, + const ngtcp2_vec *hp_key, + ngtcp2_hp_mask hp_mask) { + size_t sample_offset; + uint8_t *p = dest; + uint8_t mask[NGTCP2_HP_MASKLEN]; + size_t i; + int rv; + + assert(hp_mask); + assert(ckm); + assert(destlen >= pkt_num_offset + 4); + + if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { + return NGTCP2_ERR_PROTO; + } + + p = ngtcp2_cpymem(p, pkt, pkt_num_offset); + + sample_offset = pkt_num_offset + 4; + + rv = hp_mask(mask, hp, hp_key->base, pkt + sample_offset); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f)); + } else { + dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f)); + if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) { + hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE; + } + } + + hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); + + for (i = 0; i < hd->pkt_numlen; ++i) { + *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; + } + + hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); + + return p - dest; +} + +/* + * conn_emit_pending_crypto_data delivers pending stream data to the + * application due to packet reordering. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed + * NGTCP2_ERR_CRYPTO + * TLS backend reported error + */ +static int conn_emit_pending_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + ngtcp2_strm *strm, + uint64_t rx_offset) { + size_t datalen; + const uint8_t *data; + int rv; + uint64_t offset; + + for (;;) { + datalen = ngtcp2_rob_data_at(&strm->rx.rob, &data, rx_offset); + if (datalen == 0) { + assert(rx_offset == ngtcp2_strm_rx_offset(strm)); + return 0; + } + + offset = rx_offset; + rx_offset += datalen; + + rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); + if (rv != 0) { + return rv; + } + + ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen); + } +} + +/* + * conn_recv_connection_close is called when CONNECTION_CLOSE or + * APPLICATION_CLOSE frame is received. + */ +static void conn_recv_connection_close(ngtcp2_conn *conn, + ngtcp2_connection_close *fr) { + conn->state = NGTCP2_CS_DRAINING; + if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { + conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; + } else { + conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + } + conn->rx.ccec.error_code = fr->error_code; +} + +static void conn_recv_path_challenge(ngtcp2_conn *conn, + ngtcp2_path_challenge *fr) { + ngtcp2_path_challenge_entry *ent; + + ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); + ngtcp2_path_challenge_entry_init(ent, fr->data); +} + +/* + * conn_reset_congestion_state resets congestion state. + */ +static void conn_reset_congestion_state(ngtcp2_conn *conn) { + rcvry_stat_reset(&conn->rcs); + cc_stat_reset(&conn->ccs); + + if (conn->hs_pktns) { + conn->hs_pktns->rtb.cc_pkt_num = conn->hs_pktns->tx.last_pkt_num + 1; + } + conn->pktns.rtb.cc_pkt_num = conn->pktns.tx.last_pkt_num + 1; +} + +static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_pv *pv = conn->pv; + + if (!pv) { + return 0; + } + + rv = ngtcp2_pv_validate(pv, fr->data); + if (rv != 0) { + if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) { + return conn_on_path_validation_failed(conn, pv, ts); + } + return 0; + } + + if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + goto fail; + } + ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); + conn_reset_congestion_state(conn); + } + + rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); + if (rv != 0) { + goto fail; + } + +fail: + return conn_stop_pv(conn, ts); +} + +/* + * pkt_num_bits returns the number of bits available when packet + * number is encoded in |pkt_numlen| bytes. + */ +static size_t pkt_num_bits(size_t pkt_numlen) { + switch (pkt_numlen) { + case 1: + return 8; + case 2: + return 16; + case 3: + return 24; + case 4: + return 32; + default: + assert(0); + } +} + +/* + * pktns_pkt_num_is_duplicate returns nonzero if |pkt_num| is + * duplicated packet number. + */ +static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) { + return ngtcp2_gaptr_is_pushed(&pktns->rx.pngap, (uint64_t)pkt_num, 1); +} + +/* + * pktns_commit_recv_pkt_num marks packet number |pkt_num| as + * received. + */ +static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num) { + int rv; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + ngtcp2_range range; + + if (pktns->rx.max_pkt_num + 1 != pkt_num) { + ngtcp2_acktr_immediate_ack(&pktns->acktr); + } + if (pktns->rx.max_pkt_num < pkt_num) { + pktns->rx.max_pkt_num = pkt_num; + } + + rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) { + it = ngtcp2_ksl_begin(&pktns->rx.pngap.gap); + range = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + ngtcp2_ksl_remove(&pktns->rx.pngap.gap, NULL, + ngtcp2_ksl_key_ptr(&key, &range)); + } + + return 0; +} + +static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + uint64_t bytes_in_flight; + + bytes_in_flight = ngtcp2_rtb_get_bytes_in_flight(&pktns->rtb); + + assert(conn->ccs.bytes_in_flight >= bytes_in_flight); + + conn->ccs.bytes_in_flight -= bytes_in_flight; + + pktns_del(pktns, conn->mem); +} + +/* + * conn_discard_initial_state discards state for Initial packet number + * space. + */ +static void conn_discard_initial_state(ngtcp2_conn *conn) { + if (!conn->in_pktns) { + return; + } + + conn_discard_pktns(conn, conn->in_pktns); + conn->in_pktns = NULL; +} + +/* + * conn_discard_handshake_state discards state for Handshake packet + * number space. + */ +static void conn_discard_handshake_state(ngtcp2_conn *conn) { + if (!conn->hs_pktns) { + return; + } + + conn_discard_pktns(conn, conn->hs_pktns); + conn->hs_pktns = NULL; +} + +/* + * verify_token verifies |hd| contains |token| in its token field. It + * returns 0 if it succeeds, or NGTCP2_ERR_PROTO. + */ +static int verify_token(const ngtcp2_vec *token, const ngtcp2_pkt_hd *hd) { + if (token->len == hd->tokenlen && + ngtcp2_cmemeq(token->base, hd->token, token->len)) { + return 0; + } + return NGTCP2_ERR_PROTO; +} + +static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *strm, const ngtcp2_crypto *fr); + +static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts); + +static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_tstamp ts); + +/* + * conn_recv_handshake_pkt processes received packet |pkt| whose + * length is |pktlen| during handshake period. The buffer pointed by + * |pkt| might contain multiple packets. This function only processes + * one packet. |pkt_ts| is the timestamp when packet is received. + * |ts| should be the current time. Usually they are the same, but + * for buffered packets, |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of bytes it reads if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_RECV_VERSION_NEGOTIATION + * Version Negotiation packet is received. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded because plain text header was malformed; + * or its payload could not be decrypted. + * NGTCP2_ERR_FRAME_FORMAT + * Frame is badly formatted + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_CRYPTO + * TLS stack reported error. + * NGTCP2_ERR_PROTO + * Generic QUIC protocol error. + * + * In addition to the above error codes, error codes returned from + * conn_recv_pkt are also returned. + */ +static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn, + const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_pkt_hd hd; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int rv; + int require_ack = 0; + size_t hdpktlen; + const uint8_t *payload; + size_t payloadlen; + ngtcp2_ssize nwrite; + uint8_t plain_hdpkt[1500]; + ngtcp2_crypto_aead *aead; + ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_km *ckm; + const ngtcp2_vec *hp_key; + ngtcp2_hp_mask hp_mask; + ngtcp2_decrypt decrypt; + size_t aead_overhead; + ngtcp2_pktns *pktns; + ngtcp2_strm *crypto; + ngtcp2_crypto_level crypto_level; + int invalid_reserved_bits = 0; + + if (pktlen == 0) { + return 0; + } + + if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { + if (conn->state == NGTCP2_CS_SERVER_INITIAL) { + /* Ignore Short packet unless server's first Handshake packet + has been transmitted. */ + return (ngtcp2_ssize)pktlen; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering Short packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, &conn->pktns, path, pkt, pktlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + return (ngtcp2_ssize)pktlen; + } + + nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); + if (nread < 0) { + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + hdpktlen = (size_t)nread; + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Receiving Version Negotiation packet after getting Handshake + packet from server is invalid. */ + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* TODO Do not change state here? */ + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { + /* Just discard invalid Version Negotiation packet */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + rv = conn_on_version_negotiation(conn, &hd, pkt + hdpktlen, + pktlen - hdpktlen); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + return NGTCP2_ERR_DISCARD_PKT; + } + return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; + case NGTCP2_PKT_RETRY: + hdpktlen = (size_t)nread; + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Receiving Retry packet after getting Initial packet from server + is invalid. */ + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + return NGTCP2_ERR_DISCARD_PKT; + } + return (ngtcp2_ssize)pktlen; + } + + if (pktlen < (size_t)nread + hd.len) { + return NGTCP2_ERR_DISCARD_PKT; + } + + pktlen = (size_t)nread + hd.len; + + if (conn->version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Quoted from spec: if subsequent packets of those types include a + different Source Connection ID, they MUST be discarded. */ + if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && + !ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_0RTT: + if (!conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + if (conn->early.ckm) { + ngtcp2_ssize nread2; + /* TODO Avoid to parse header twice. */ + nread2 = conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen, + pkt_ts, ts); + if (nread2 < 0) { + return nread2; + } + } + + /* Discard 0-RTT packet if we don't have a key to decrypt it. */ + return (ngtcp2_ssize)pktlen; + } + + /* Buffer re-ordered 0-RTT packet. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering 0-RTT packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, conn->in_pktns, path, pkt, pktlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + return (ngtcp2_ssize)pktlen; + case NGTCP2_PKT_INITIAL: + if (!conn->in_pktns) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Initial packet is discarded because keys have been discarded"); + return (ngtcp2_ssize)pktlen; + } + + assert(conn->in_pktns); + + if (conn->server) { + if (conn->local.settings.token.len) { + rv = verify_token(&conn->local.settings.token, &hd); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is invalid"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) { + rv = conn_call_recv_client_initial(conn, &hd.dcid); + if (rv != 0) { + return rv; + } + } + } else if (hd.tokenlen != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is not empty"); + return NGTCP2_ERR_DISCARD_PKT; + } + + pktns = conn->in_pktns; + aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + crypto = &pktns->crypto.strm; + crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + + break; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns->crypto.rx.ckm) { + if (conn->server) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Handshake packet at this point is unexpected and discarded"); + return (ngtcp2_ssize)pktlen; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering Handshake packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pkt, pktlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + return (ngtcp2_ssize)pktlen; + } + + pktns = conn->hs_pktns; + aead_overhead = conn->crypto.aead_overhead; + crypto = &pktns->crypto.strm; + crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + + break; + default: + /* unknown packet type */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of unknown packet type"); + return (ngtcp2_ssize)pktlen; + } + + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + aead = &pktns->crypto.ctx.aead; + hp = &pktns->crypto.ctx.hp; + ckm = pktns->crypto.rx.ckm; + hp_key = pktns->crypto.rx.hp_key; + + assert(ckm); + assert(hp_mask); + assert(decrypt); + + nwrite = decrypt_hp(&hd, plain_hdpkt, sizeof(plain_hdpkt), hp, pkt, pktlen, + (size_t)nread, ckm, hp_key, hp_mask); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + hdpktlen = (size_t)nwrite; + payload = pkt + hdpktlen; + payloadlen = hd.len - hd.pkt_numlen; + + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, + pkt_num_bits(hd.pkt_numlen)); + if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + rv = ngtcp2_pkt_verify_reserved_bits(plain_hdpkt[0]); + if (rv != 0) { + invalid_reserved_bits = 1; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet has incorrect reserved bits"); + + /* Will return error after decrypting payload */ + } + + if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was discarded because of duplicated packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_ensure_decrypt_buffer(conn, payloadlen); + if (rv != 0) { + return rv; + } + + nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, + plain_hdpkt, hdpktlen, hd.pkt_num, ckm, decrypt, + aead_overhead); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (invalid_reserved_bits) { + return NGTCP2_ERR_PROTO; + } + + payload = conn->crypto.decrypt_buf.base; + payloadlen = (size_t)nwrite; + + switch (hd.type) { + case NGTCP2_PKT_INITIAL: + if (!conn->server || ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && + !ngtcp2_cid_eq(&conn->rcid, &hd.dcid))) { + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + break; + case NGTCP2_PKT_HANDSHAKE: + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + break; + default: + assert(0); + } + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + return NGTCP2_ERR_DISCARD_PKT; + } + + if (hd.type == NGTCP2_PKT_INITIAL && + !(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) { + conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED; + if (conn->server) { + conn->rcid = hd.dcid; + } else { + conn->dcid.current.cid = hd.scid; + } + conn->odcid = hd.scid; + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog, &hd); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + if (fr->type == NGTCP2_FRAME_ACK) { + fr->ack.ack_delay = 0; + fr->ack.ack_delay_unscaled = 0; + } + + ngtcp2_log_rx_fr(&conn->log, &hd, fr); + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + break; + case NGTCP2_FRAME_PADDING: + break; + case NGTCP2_FRAME_CRYPTO: + rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); + if (rv != 0) { + return rv; + } + require_ack = 1; + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_PING: + require_ack = 1; + break; + default: + return NGTCP2_ERR_PROTO; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + if (conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { + /* Successful processing of Handshake packet from client verifies + source address. */ + conn->flags |= NGTCP2_CONN_FLAG_SADDR_VERIFIED; + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); + + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num); + if (rv != 0) { + return rv; + } + + if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { + ngtcp2_acktr_immediate_ack(&pktns->acktr); + } + + rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, + pkt_ts); + if (rv != 0) { + return rv; + } + + conn_restart_timer_on_read(conn, ts); + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING + : (ngtcp2_ssize)pktlen; +} + +/* + * conn_recv_handshake_cpkt processes compound packet during + * handshake. The buffer pointed by |pkt| might contain multiple + * packets. The Short packet must be the last one because it does not + * have payload length field. + * + * This function returns the same error code returned by + * conn_recv_handshake_pkt. + */ +static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + + conn->hs_recved += pktlen; + + while (pktlen) { + nread = conn_recv_handshake_pkt(conn, path, pkt, pktlen, ts, ts); + if (nread < 0) { + if (ngtcp2_err_is_fatal((int)nread)) { + return (int)nread; + } + + switch (nread) { + case NGTCP2_ERR_DISCARD_PKT: + return 0; + case NGTCP2_ERR_DRAINING: + return NGTCP2_ERR_DRAINING; + } + + if (nread != NGTCP2_ERR_CRYPTO && (pkt[0] & NGTCP2_HEADER_FORM_BIT) && + /* Not a Version Negotiation packet */ + pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && + ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { + break; + } + return (int)nread; + } + + assert(pktlen >= (size_t)nread); + pkt += nread; + pktlen -= (size_t)nread; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "read packet %td left %zu", nread, pktlen); + } + + return 0; +} + +int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + int64_t stream_id, void *stream_user_data) { + int rv; + uint64_t max_rx_offset; + uint64_t max_tx_offset; + int local_stream = conn_local_stream(conn, stream_id); + + if (bidi_stream(stream_id)) { + if (local_stream) { + max_rx_offset = conn->local.settings.transport_params + .initial_max_stream_data_bidi_local; + max_tx_offset = + conn->remote.transport_params.initial_max_stream_data_bidi_remote; + } else { + max_rx_offset = conn->local.settings.transport_params + .initial_max_stream_data_bidi_remote; + max_tx_offset = + conn->remote.transport_params.initial_max_stream_data_bidi_local; + } + } else if (local_stream) { + max_rx_offset = 0; + max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni; + } else { + max_rx_offset = + conn->local.settings.transport_params.initial_max_stream_data_uni; + max_tx_offset = 0; + } + + rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, + max_tx_offset, stream_user_data, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_map_insert(&conn->strms, &strm->me); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + goto fail; + } + + if (!conn_local_stream(conn, stream_id)) { + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { + goto fail; + } + } + + return 0; + +fail: + ngtcp2_strm_free(strm); + return rv; +} + +/* + * conn_emit_pending_stream_data passes buffered ordered stream data + * to the application. |rx_offset| is the first offset to deliver to + * the application. This function assumes that the data up to + * |rx_offset| has been delivered already. This function only passes + * the ordered data without any gap. If there is a gap, it stops + * providing the data to the application, and returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t rx_offset) { + size_t datalen; + const uint8_t *data; + int rv; + uint64_t offset; + + for (;;) { + /* Stop calling callback if application has called + ngtcp2_conn_shutdown_stream_read() inside the callback. + Because it doubly counts connection window. */ + if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) { + return 0; + } + + datalen = ngtcp2_rob_data_at(&strm->rx.rob, &data, rx_offset); + if (datalen == 0) { + assert(rx_offset == ngtcp2_strm_rx_offset(strm)); + return 0; + } + + offset = rx_offset; + rx_offset += datalen; + + rv = conn_call_recv_stream_data(conn, strm, + (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + rx_offset == strm->rx.last_offset, + offset, data, datalen); + if (rv != 0) { + return rv; + } + + ngtcp2_rob_pop(&strm->rx.rob, rx_offset - datalen, datalen); + } +} + +/* + * conn_recv_crypto is called when CRYPTO frame |fr| is received. + * |rx_offset_base| is the offset in the entire TLS handshake stream. + * fr->offset specifies the offset in each encryption level. + * |max_rx_offset| is, if it is nonzero, the maximum offset in the + * entire TLS handshake stream that |fr| can carry. |crypto_level| is + * the encryption level where this data is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * CRYPTO frame has invalid offset. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CRYPTO + * TLS stack reported error. + * NGTCP2_ERR_FRAME_ENCODING + * The end offset exceeds the maximum value. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, const ngtcp2_crypto *fr) { + uint64_t fr_end_offset; + uint64_t rx_offset; + int rv; + + if (fr->datacnt == 0) { + return 0; + } + + fr_end_offset = fr->offset + fr->data[0].len; + + if (NGTCP2_MAX_VARINT < fr_end_offset) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + rx_offset = ngtcp2_strm_rx_offset(crypto); + + if (fr_end_offset <= rx_offset) { + return 0; + } + + crypto->rx.last_offset = ngtcp2_max(crypto->rx.last_offset, fr_end_offset); + + /* TODO Before dispatching incoming data to TLS stack, make sure + that previous data in previous encryption level has been + completely sent to TLS stack. Usually, if data is left, it is an + error because key is generated after consuming all data in the + previous encryption level. */ + if (fr->offset <= rx_offset) { + size_t ncut = rx_offset - fr->offset; + const uint8_t *data = fr->data[0].base + ncut; + size_t datalen = fr->data[0].len - ncut; + uint64_t offset = rx_offset; + + rx_offset += datalen; + rv = ngtcp2_rob_remove_prefix(&crypto->rx.rob, rx_offset); + if (rv != 0) { + return rv; + } + + rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_crypto_data(conn, crypto_level, crypto, rx_offset); + if (rv != 0) { + return rv; + } + } else if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) { + return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED; + } else { + rv = ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len, + fr->offset); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * conn_max_data_violated returns nonzero if receiving |datalen| + * violates connection flow control on local endpoint. + */ +static int conn_max_data_violated(ngtcp2_conn *conn, size_t datalen) { + return conn->rx.max_offset - conn->rx.offset < datalen; +} + +/* + * conn_recv_stream is called when STREAM frame |fr| is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * STREAM frame is received for a local stream which is not + * initiated; or STREAM frame is received for a local + * unidirectional stream + * NGTCP2_ERR_STREAM_LIMIT + * STREAM frame has remote stream ID which is strictly greater + * than the allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated; or the end offset of stream + * data is beyond the NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + * STREAM frame has strictly larger end offset than it is + * permitted. + */ +static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { + int rv; + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + uint64_t rx_offset, fr_end_offset; + int local_stream; + int bidi; + size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + local_stream = conn_local_stream(conn, fr->stream_id); + bidi = bidi_stream(fr->stream_id); + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (local_stream) { + return NGTCP2_ERR_STREAM_STATE; + } + if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.uni.idtr; + } + + if (NGTCP2_MAX_VARINT - datalen < fr->offset) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + /* TODO The stream has been closed. This should be responded + with RESET_STREAM, or simply ignored. */ + return 0; + } + + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + /* TODO The stream has been closed. This should be responded + with RESET_STREAM, or simply ignored. */ + return 0; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + /* TODO Perhaps, call new_stream callback? */ + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + if (!bidi) { + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + } + + fr_end_offset = fr->offset + datalen; + + if (strm->rx.max_offset < fr_end_offset) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + if (strm->rx.last_offset < fr_end_offset) { + size_t len = fr_end_offset - strm->rx.last_offset; + + if (conn_max_data_violated(conn, len)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + conn->rx.offset += len; + + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + ngtcp2_conn_extend_max_offset(conn, len); + } + } + + rx_offset = ngtcp2_strm_rx_offset(strm); + + if (fr->fin) { + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) { + if (strm->rx.last_offset != fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } + + if (strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + return 0; + } + + if (rx_offset == fr_end_offset) { + return 0; + } + } else if (strm->rx.last_offset > fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } else { + strm->rx.last_offset = fr_end_offset; + + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); + + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, + strm->app_error_code); + } + + if (fr_end_offset == rx_offset) { + rv = conn_call_recv_stream_data(conn, strm, 1, rx_offset, NULL, 0); + if (rv != 0) { + return rv; + } + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, + NGTCP2_NO_ERROR); + } + } + } else { + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + strm->rx.last_offset < fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } + + strm->rx.last_offset = ngtcp2_max(strm->rx.last_offset, fr_end_offset); + + if (fr_end_offset <= rx_offset) { + return 0; + } + + if (strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + return 0; + } + } + + if (fr->offset <= rx_offset) { + size_t ncut = rx_offset - fr->offset; + uint64_t offset = rx_offset; + const uint8_t *data; + int fin; + + if (fr->datacnt) { + data = fr->data[0].base + ncut; + datalen -= ncut; + + rx_offset += datalen; + rv = ngtcp2_rob_remove_prefix(&strm->rx.rob, rx_offset); + if (rv != 0) { + return rv; + } + } else { + data = NULL; + datalen = 0; + } + + fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + rx_offset == strm->rx.last_offset; + + if (fin || datalen) { + rv = conn_call_recv_stream_data(conn, strm, fin, offset, data, datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_stream_data(conn, strm, rx_offset); + if (rv != 0) { + return rv; + } + } + } else if (fr->datacnt) { + rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, + fr->offset); + if (rv != 0) { + return rv; + } + } + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); +} + +/* + * conn_reset_stream adds RESET_STREAM frame to the transmission + * queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + ngtcp2_frame_chain *frc; + ngtcp2_pktns *pktns = &conn->pktns; + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_RESET_STREAM; + frc->fr.reset_stream.stream_id = strm->stream_id; + frc->fr.reset_stream.app_error_code = app_error_code; + frc->fr.reset_stream.final_size = strm->tx.offset; + + /* TODO This prepends RESET_STREAM to pktns->tx.frq. */ + frc->next = pktns->tx.frq; + pktns->tx.frq = frc; + + return 0; +} + +/* + * conn_stop_sending adds STOP_SENDING frame to the transmission + * queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + ngtcp2_frame_chain *frc; + ngtcp2_pktns *pktns = &conn->pktns; + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_STOP_SENDING; + frc->fr.stop_sending.stream_id = strm->stream_id; + frc->fr.stop_sending.app_error_code = app_error_code; + + /* TODO This prepends STOP_SENDING to pktns->tx.frq. */ + frc->next = pktns->tx.frq; + pktns->tx.frq = frc; + + return 0; +} + +/* + * handle_max_remote_streams_extension extends + * |*punsent_max_remote_streams| by |n| if a condition allows it. + */ +static void +handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, + size_t n) { + if (*punsent_max_remote_streams < NGTCP2_MAX_STREAMS - n) { + *punsent_max_remote_streams += n; + } +} + +/* + * conn_recv_reset_stream is called when RESET_STREAM |fr| is + * received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * RESET_STREAM frame is received to the local stream which is not + * initiated. + * NGTCP2_ERR_STREAM_LIMIT + * RESET_STREAM frame has remote stream ID which is strictly + * greater than the allowed limit. + * NGTCP2_ERR_PROTO + * RESET_STREAM frame is received to the local unidirectional + * stream + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated; or the final size is beyond the + * NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + * The final offset is strictly larger than it is permitted. + */ +static int conn_recv_reset_stream(ngtcp2_conn *conn, + const ngtcp2_reset_stream *fr) { + ngtcp2_strm *strm; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + uint64_t datalen; + ngtcp2_idtr *idtr; + int rv; + + /* TODO share this piece of code */ + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (local_stream) { + return NGTCP2_ERR_PROTO; + } + if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.uni.idtr; + } + + if (NGTCP2_MAX_VARINT < fr->final_size) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + return 0; + } + + if (conn_initial_stream_rx_offset(conn, fr->stream_id) < fr->final_size || + conn_max_data_violated(conn, fr->final_size)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + return 0; + } + + /* Stream is reset before we create ngtcp2_strm object. */ + conn->rx.offset += fr->final_size; + ngtcp2_conn_extend_max_offset(conn, fr->final_size); + + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, NULL); + if (rv != 0) { + return rv; + } + + /* There will be no activity in this stream because we got + RESET_STREAM and don't write stream data any further. This + effectively allows another new stream for peer. */ + if (bidi) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, + 1); + } else { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, + 1); + } + + return 0; + } + + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { + if (strm->rx.last_offset != fr->final_size) { + return NGTCP2_ERR_FINAL_SIZE; + } + } else if (strm->rx.last_offset > fr->final_size) { + return NGTCP2_ERR_FINAL_SIZE; + } + + datalen = fr->final_size - strm->rx.last_offset; + + if (strm->rx.max_offset < fr->final_size || + conn_max_data_violated(conn, datalen)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) { + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, strm->stream_user_data); + if (rv != 0) { + return rv; + } + + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); + } + } + + conn->rx.offset += datalen; + ngtcp2_conn_extend_max_offset(conn, datalen); + + strm->rx.last_offset = fr->final_size; + strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST; + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); +} + +/* + * conn_recv_stop_sending is called when STOP_SENDING |fr| is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * STOP_SENDING frame is received for a local stream which is not + * initiated; or STOP_SENDING frame is received for a local + * unidirectional stream. + * NGTCP2_ERR_STREAM_LIMIT + * STOP_SENDING frame has remote stream ID which is strictly + * greater than the allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_stop_sending(ngtcp2_conn *conn, + const ngtcp2_stop_sending *fr) { + int rv; + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + + idtr = &conn->remote.uni.idtr; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + return 0; + } + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + return 0; + } + + /* Frame is received reset before we create ngtcp2_strm + object. */ + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + } + + /* No RESET_STREAM is required if we have sent FIN and all data have + been acknowledged. */ + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && + ngtcp2_strm_is_all_tx_data_acked(strm)) { + return 0; + } + + rv = conn_reset_stream(conn, strm, fr->app_error_code); + if (rv != 0) { + return rv; + } + + strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + + ngtcp2_strm_streamfrq_clear(strm); + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); +} + +/* + * check_stateless_reset returns nonzero if Stateless Reset |sr| + * coming via |path| is valid against |dcid|. + */ +static int check_stateless_reset(const ngtcp2_dcid *dcid, + const ngtcp2_path *path, + const ngtcp2_pkt_stateless_reset *sr) { + return ngtcp2_path_eq(&dcid->ps.path, path) && + ngtcp2_verify_stateless_reset_token(dcid->token, + sr->stateless_reset_token) == 0; +} + +/* + * conn_on_stateless_reset decodes Stateless Reset from the buffer + * pointed by |payload| whose length is |payloadlen|. |payload| + * should start after first byte of packet. + * + * If Stateless Reset is decoded, and the Stateless Reset Token is + * validated, the connection is closed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Could not decode Stateless Reset; or Stateless Reset Token does + * not match; or No stateless reset token is available. + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *payload, size_t payloadlen) { + int rv = 1; + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *dcid; + ngtcp2_pkt_stateless_reset sr; + size_t len, i; + + rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen); + if (rv != 0) { + return rv; + } + + if (!check_stateless_reset(&conn->dcid.current, path, &sr) && + (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && + (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + if (check_stateless_reset(dcid, path, &sr)) { + break; + } + } + + if (i == len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + + conn->state = NGTCP2_CS_DRAINING; + + ngtcp2_log_rx_sr(&conn->log, &sr); + + if (!conn->callbacks.recv_stateless_reset) { + return 0; + } + + rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +/* + * conn_recv_max_streams processes the incoming MAX_STREAMS frame + * |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_FRAME_ENCODING + * The maximum streams field exceeds the maximum value. + */ +static int conn_recv_max_streams(ngtcp2_conn *conn, + const ngtcp2_max_streams *fr) { + uint64_t n; + + if (fr->max_streams > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + n = ngtcp2_min(fr->max_streams, NGTCP2_MAX_STREAMS); + + if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { + if (conn->local.bidi.max_streams < n) { + conn->local.bidi.max_streams = n; + return conn_call_extend_max_local_streams_bidi(conn, n); + } + return 0; + } + + if (conn->local.uni.max_streams < n) { + conn->local.uni.max_streams = n; + return conn_call_extend_max_local_streams_uni(conn, n); + } + return 0; +} + +static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, + uint64_t retire_prior_to) { + size_t i; + ngtcp2_dcid *dcid, *last; + int rv; + + for (i = 0; i < ngtcp2_ringbuf_len(rb);) { + dcid = ngtcp2_ringbuf_get(rb, i); + if (dcid->seq >= retire_prior_to) { + ++i; + continue; + } + + rv = conn_retire_dcid_seq(conn, dcid->seq); + if (rv != 0) { + return rv; + } + if (i == 0) { + ngtcp2_ringbuf_pop_front(rb); + } else if (i == ngtcp2_ringbuf_len(rb) - 1) { + ngtcp2_ringbuf_pop_back(rb); + break; + } else { + last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); + ngtcp2_dcid_copy(dcid, last); + ngtcp2_ringbuf_pop_back(rb); + } + } + + return 0; +} + +/* + * conn_recv_new_connection_id processes the incoming + * NEW_CONNECTION_ID frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * |fr| has the duplicated sequence number with different CID or + * token; or DCID is zero-length. + */ +static int conn_recv_new_connection_id(ngtcp2_conn *conn, + const ngtcp2_new_connection_id *fr) { + size_t i, len; + ngtcp2_dcid *dcid; + ngtcp2_pv *pv = conn->pv; + int rv; + int found = 0; + size_t extra_dcid = 0; + + if (conn->dcid.current.cid.datalen == 0) { + return NGTCP2_ERR_PROTO; + } + + if (fr->retire_prior_to > fr->seq) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid, + fr->stateless_reset_token); + if (rv != 0) { + return rv; + } + if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) { + found = 1; + } + + if (pv) { + rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid, + fr->stateless_reset_token); + if (rv != 0) { + return rv; + } + if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) { + found = 1; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.unused); + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i); + rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, + fr->stateless_reset_token); + if (rv != 0) { + return NGTCP2_ERR_PROTO; + } + if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) { + found = 1; + } + } + + if (conn->dcid.retire_prior_to < fr->retire_prior_to) { + conn->dcid.retire_prior_to = fr->retire_prior_to; + + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + conn->dcid.retire_prior_to); + if (rv != 0) { + return rv; + } + } else if (fr->seq < conn->dcid.retire_prior_to) { + /* If packets are reordered, we might have retire_prior_to which + is larger than fr->seq. + + A malicious peer might send crafted NEW_CONNECTION_ID to force + local endpoint to create lots of RETIRE_CONNECTION_ID frames. + For example, a peer might send seq = 50000 and retire_prior_to + = 50000. Then send NEW_CONNECTION_ID frames with seq < + 50000. */ + if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { + return conn_retire_dcid_seq(conn, fr->seq); + } + return 0; + } + + if (!found) { + len = ngtcp2_ringbuf_len(&conn->dcid.unused); + + if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq && + pv->dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + } + + if (conn->local.settings.transport_params.active_connection_id_limit <= + len + extra_dcid) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + if (len >= NGTCP2_MAX_DCID_POOL_SIZE) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "too many connection ID"); + return 0; + } + + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); + } + + return 0; +} + +/* + * conn_post_process_recv_new_connection_id handles retirement request + * of active DCIDs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *dcid; + int rv; + + if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + if (pv) { + if (conn->dcid.current.seq == pv->dcid.seq) { + ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + conn->dcid.current.seq == pv->fallback_dcid.seq) { + ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); + } + } + + ngtcp2_dcid_copy_no_path(&conn->dcid.current, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + } + + if (pv) { + if (pv->dcid.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + rv = conn_retire_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->dcid.seq == pv->fallback_dcid.seq) { + ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); + } + + ngtcp2_dcid_copy_no_path(&pv->dcid, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + } else { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path migration is aborted because connection ID is" + "retired and no unused connection ID is available"); + + return conn_stop_pv(conn, ts); + } + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_dcid_copy_no_path(&pv->fallback_dcid, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); + if (rv != 0) { + return rv; + } + } else { + /* Now we have no fallback dcid. */ + return conn_stop_pv(conn, ts); + } + } + } + + return 0; +} + +/* + * conn_recv_retire_connection_id processes the incoming + * RETIRE_CONNECTION_ID frame |fr|. |hd| is a packet header which + * |fr| is included. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_PROTO + * SCID is zero-length. + * NGTCP2_ERR_FRAME_ENCODING + * Attempt to retire CID which is used as DCID to send this frame. + */ +static int conn_recv_retire_connection_id(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const ngtcp2_retire_connection_id *fr, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + + if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) { + return NGTCP2_ERR_PROTO; + } + + for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + scid = ngtcp2_ksl_it_get(&it); + if (scid->seq == fr->seq) { + if (ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) { + scid->flags |= NGTCP2_SCID_FLAG_RETIRED; + ++conn->scid.num_retired; + } + + if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) { + ngtcp2_pq_remove(&conn->scid.used, &scid->pe); + scid->pe.index = NGTCP2_PQ_BAD_INDEX; + } + + scid->ts_retired = ts; + + return ngtcp2_pq_push(&conn->scid.used, &scid->pe); + } + } + + return 0; +} + +/* + * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING: + * Token is empty + */ +static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { + (void)conn; + + if (fr->tokenlen == 0) { + return NGTCP2_ERR_FRAME_ENCODING; + } + /* TODO Not implemented yet*/ + return 0; +} + +/* + * conn_select_preferred_addr asks a client application to select a + * server address from preferred addresses received from server. If a + * client chooses the address, path validation will start. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_select_preferred_addr(ngtcp2_conn *conn) { + uint8_t buf[128]; + ngtcp2_addr addr; + int rv; + ngtcp2_duration timeout; + ngtcp2_pv *pv; + ngtcp2_dcid *dcid; + + ngtcp2_addr_init(&addr, buf, 0, NULL); + + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + rv = conn_call_select_preferred_addr(conn, &addr); + if (rv != 0) { + return rv; + } + + if (addr.addrlen == 0 || + ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { + return 0; + } + + assert(conn->pv == NULL); + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + timeout = conn_compute_pto(conn); + timeout = + ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT)); + + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log, + conn->mem); + if (rv != 0) { + /* TODO Call ngtcp2_dcid_free here if it is introduced */ + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + conn->pv = pv; + + ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); + ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); + + return conn_call_activate_dcid(conn, &pv->dcid); +} + +/* + * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE + * frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Server received HANDSHAKE_DONE frame. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_handshake_done(ngtcp2_conn *conn) { + int rv; + + if (conn->server) { + return NGTCP2_ERR_PROTO; + } + + if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + return 0; + } + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + + conn_discard_handshake_state(conn); + + if (conn->callbacks.handshake_confirmed) { + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +/* + * conn_key_phase_changed returns nonzero if |hd| indicates that the + * key phase has unexpected value. + */ +static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { + ngtcp2_pktns *pktns = &conn->pktns; + + return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^ + !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE); +} + +/* + * conn_prepare_key_update installs new updated keys. + */ +static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; + ngtcp2_duration pto = conn_compute_pto(conn); + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm; + ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm; + ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm; + size_t secretlen, keylen, ivlen; + + if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + (confirmed_ts != UINT64_MAX && confirmed_ts + pto > ts)) { + return 0; + } + + if (conn->crypto.key_update.new_rx_ckm || + conn->crypto.key_update.new_tx_ckm) { + assert(conn->crypto.key_update.new_rx_ckm); + assert(conn->crypto.key_update.new_tx_ckm); + return 0; + } + + secretlen = rx_ckm->secret.len; + keylen = rx_ckm->key.len; + ivlen = rx_ckm->iv.len; + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm, + secretlen, keylen, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm, + secretlen, keylen, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + new_rx_ckm = conn->crypto.key_update.new_rx_ckm; + new_tx_ckm = conn->crypto.key_update.new_tx_ckm; + + assert(conn->callbacks.update_key); + + rv = conn->callbacks.update_key( + conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, + new_rx_ckm->key.base, new_rx_ckm->iv.base, new_tx_ckm->key.base, + new_tx_ckm->iv.base, rx_ckm->secret.base, tx_ckm->secret.base, secretlen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) { + new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; + new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; + } + + ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); + conn->crypto.key_update.old_rx_ckm = NULL; + + return 0; +} + +/* + * conn_rotate_keys rotates keys. The current key moves to old key, + * and new key moves to the current key. + */ +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { + ngtcp2_pktns *pktns = &conn->pktns; + + assert(conn->crypto.key_update.new_rx_ckm); + assert(conn->crypto.key_update.new_tx_ckm); + assert(!conn->crypto.key_update.old_rx_ckm); + assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + + conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm; + + pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm; + conn->crypto.key_update.new_rx_ckm = NULL; + pktns->crypto.rx.ckm->pkt_num = pkt_num; + + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm; + conn->crypto.key_update.new_tx_ckm = NULL; + pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; + + conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; +} + +/* + * conn_path_validation_in_progress returns nonzero if path validation + * against |path| is underway. + */ +static int conn_path_validation_in_progress(ngtcp2_conn *conn, + const ngtcp2_path *path) { + ngtcp2_pv *pv = conn->pv; + + return pv && ngtcp2_path_eq(&pv->dcid.ps.path, path); +} + +/* + * conn_recv_non_probing_pkt_on_new_path is called when non-probing + * packet is received via new path. It starts path validation against + * the new path. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + * No DCID is available + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, + const ngtcp2_path *path, + int new_cid_used, + ngtcp2_tstamp ts) { + + ngtcp2_dcid dcid; + ngtcp2_pv *pv; + int rv; + ngtcp2_duration timeout; + int require_new_cid; + + assert(conn->server); + + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) { + /* If new path equals fallback path, that means connection + migrated back to the original path. Fallback path is + considered to be validated. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path is migrated back to the original path"); + ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid); + conn_reset_congestion_state(conn); + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + return 0; + } + + /* The transport specification draft-27 says: + * + * An endpoint MUST use a new connection ID if it initiates + * connection migration as described in Section 9.2 or probes a new + * network path as described in Section 9.1. An endpoint MUST use a + * new connection ID in response to a change in the address of a + * peer if the packet with the new peer address uses an active + * connection ID that has not been previously used by the peer. + */ + require_new_cid = + (new_cid_used && + !ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &path->remote)) || + !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); + + /* If the remote endpoint uses new DCID, server has to change its + DCID as well. */ + if (require_new_cid && ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return NGTCP2_ERR_CONN_ID_BLOCKED; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "non-probing packet was received from new remote address"); + + timeout = conn_compute_pto(conn); + timeout = + ngtcp2_max(timeout, (ngtcp2_duration)(6ULL * NGTCP2_DEFAULT_INITIAL_RTT)); + + if (require_new_cid) { + dcid = *(ngtcp2_dcid *)ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &dcid); + if (rv != 0) { + return rv; + } + } else { + /* Use the current DCID if a remote endpoint does not change + DCID. */ + dcid = conn->dcid.current; + } + + ngtcp2_path_copy(&dcid.ps.path, path); + + rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, + &conn->log, conn->mem); + if (rv != 0) { + return rv; + } + + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { + ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid); + } else { + ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); + } + + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); + + conn_reset_congestion_state(conn); + + if (conn->pv) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PTV, + "path migration is aborted because new migration has started"); + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + + conn->pv = pv; + + return 0; +} + +/* + * conn_recv_delayed_handshake_pkt processes the received Handshake + * packet which is received after handshake completed. This function + * does the minimal job, and its purpose is send acknowledgement of + * this packet to the peer. We assume that hd->type == + * NGTCP2_PKT_HANDSHAKE. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Frame is badly formatted; or frame type is unknown. + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded. + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_PROTO + * Frame that is not allowed in Handshake packet is received. + */ +static int +conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + size_t pktlen, const uint8_t *payload, + size_t payloadlen, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int rv; + int require_ack = 0; + ngtcp2_pktns *pktns; + + assert(hd->type == NGTCP2_PKT_HANDSHAKE); + + pktns = conn->hs_pktns; + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog, hd); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return (int)nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + if (fr->type == NGTCP2_FRAME_ACK) { + fr->ack.ack_delay = 0; + fr->ack.ack_delay_unscaled = 0; + } + + ngtcp2_log_rx_fr(&conn->log, hd, fr); + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + break; + case NGTCP2_FRAME_PADDING: + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_CRYPTO: + case NGTCP2_FRAME_PING: + require_ack = 1; + break; + default: + return NGTCP2_ERR_PROTO; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen); + + rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num); + if (rv != 0) { + return rv; + } + + if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { + ngtcp2_acktr_immediate_ack(&pktns->acktr); + } + + rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack, ts); + if (rv != 0) { + return rv; + } + + conn_restart_timer_on_read(conn, ts); + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + return 0; +} + +/* + * conn_recv_pkt processes a packet contained in the buffer pointed by + * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. + * This function only processes the first packet. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded because plain text header was malformed; + * or its payload could not be decrypted. + * NGTCP2_ERR_PROTO + * Packet is badly formatted; or 0RTT packet contains other than + * PADDING or STREAM frames; or other QUIC protocol violation is + * found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_FRAME_ENCODING + * Frame is badly formatted; or frame type is unknown. + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_STREAM_STATE + * Frame is received to the local stream which is not initiated. + * NGTCP2_ERR_STREAM_LIMIT + * Frame has remote stream ID which is strictly greater than the + * allowed limit. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated. + * NGTCP2_ERR_FINAL_SIZE + * Frame has strictly larger end offset than it is permitted. + */ +static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { + ngtcp2_pkt_hd hd; + int rv = 0; + size_t hdpktlen; + const uint8_t *payload; + size_t payloadlen; + ngtcp2_ssize nread, nwrite; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int require_ack = 0; + ngtcp2_crypto_aead *aead; + ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_km *ckm; + const ngtcp2_vec *hp_key; + uint8_t plain_hdpkt[1500]; + ngtcp2_hp_mask hp_mask; + ngtcp2_decrypt decrypt; + size_t aead_overhead; + ngtcp2_pktns *pktns; + int non_probing_pkt = 0; + int key_phase_bit_changed = 0; + int force_decrypt_failure = 0; + int recv_ncid = 0; + int new_cid_used = 0; + + if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { + nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); + if (nread < 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decode long header"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + pktlen = (size_t)nread + hd.len; + + /* Quoted from spec: if subsequent packets of those types include + a different Source Connection ID, they MUST be discarded. */ + if (!ngtcp2_cid_eq(&conn->odcid, &hd.scid)) { + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_INITIAL: + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "delayed Initial packet was discarded"); + return (ngtcp2_ssize)pktlen; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "delayed Handshake packet was discarded"); + return (ngtcp2_ssize)pktlen; + } + + pktns = conn->hs_pktns; + ckm = pktns->crypto.rx.ckm; + hp_key = pktns->crypto.rx.hp_key; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + aead_overhead = conn->crypto.aead_overhead; + break; + case NGTCP2_PKT_0RTT: + if (!conn->server || !conn->early.ckm) { + return NGTCP2_ERR_DISCARD_PKT; + } + + pktns = &conn->pktns; + ckm = conn->early.ckm; + hp_key = conn->early.hp_key; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + aead_overhead = conn->crypto.aead_overhead; + break; + default: + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet type 0x%02x was ignored", hd.type); + return (ngtcp2_ssize)pktlen; + } + } else { + nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->oscid.datalen); + if (nread < 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decode short header"); + return NGTCP2_ERR_DISCARD_PKT; + } + + pktns = &conn->pktns; + ckm = pktns->crypto.rx.ckm; + hp_key = pktns->crypto.rx.hp_key; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + aead_overhead = conn->crypto.aead_overhead; + } + + aead = &pktns->crypto.ctx.aead; + hp = &pktns->crypto.ctx.hp; + + nwrite = decrypt_hp(&hd, plain_hdpkt, sizeof(plain_hdpkt), hp, pkt, pktlen, + (size_t)nread, ckm, hp_key, hp_mask); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + hdpktlen = (size_t)nwrite; + payload = pkt + hdpktlen; + payloadlen = pktlen - hdpktlen; + + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, + pkt_num_bits(hd.pkt_numlen)); + if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { + switch (hd.type) { + case NGTCP2_PKT_HANDSHAKE: + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + break; + case NGTCP2_PKT_0RTT: + if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) { + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + break; + default: + /* Unreachable */ + assert(0); + } + } else { + rv = conn_verify_dcid(conn, &new_cid_used, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + + if (hd.type == NGTCP2_PKT_SHORT) { + key_phase_bit_changed = conn_key_phase_changed(conn, &hd); + } + + rv = conn_ensure_decrypt_buffer(conn, payloadlen); + if (rv != 0) { + return rv; + } + + if (key_phase_bit_changed) { + assert(hd.type == NGTCP2_PKT_SHORT); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); + + if (ckm->pkt_num > hd.pkt_num) { + if (conn->crypto.key_update.old_rx_ckm) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "decrypting with old key"); + ckm = conn->crypto.key_update.old_rx_ckm; + } else { + force_decrypt_failure = 1; + } + } else if (pktns->rx.max_pkt_num < hd.pkt_num) { + assert(ckm->pkt_num < hd.pkt_num); + if (!conn->crypto.key_update.new_rx_ckm) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "new key is not available"); + force_decrypt_failure = 1; + } else { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "decrypting with new key"); + ckm = conn->crypto.key_update.new_rx_ckm; + } + } else { + force_decrypt_failure = 1; + } + } + + nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, + plain_hdpkt, hdpktlen, hd.pkt_num, ckm, decrypt, + aead_overhead); + + if (force_decrypt_failure) { + nwrite = NGTCP2_ERR_TLS_DECRYPT; + } + + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + + assert(NGTCP2_ERR_TLS_DECRYPT == nwrite); + + if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = ngtcp2_pkt_verify_reserved_bits(plain_hdpkt[0]); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet has incorrect reserved bits"); + + return NGTCP2_ERR_PROTO; + } + + if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was discarded because of duplicated packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + payload = conn->crypto.decrypt_buf.base; + payloadlen = (size_t)nwrite; + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + return NGTCP2_ERR_DISCARD_PKT; + } + + if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { + if (hd.type == NGTCP2_PKT_HANDSHAKE) { + rv = conn_recv_delayed_handshake_pkt(conn, &hd, pktlen, payload, + payloadlen, pkt_ts, ts); + if (rv < 0) { + return (ngtcp2_ssize)rv; + } + return (ngtcp2_ssize)pktlen; + } + } else { + conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog, &hd); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + if (fr->type == NGTCP2_FRAME_ACK) { + if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) && + hd.type == NGTCP2_PKT_0RTT) { + return NGTCP2_ERR_PROTO; + } + assign_recved_ack_delay_unscaled( + &fr->ack, conn->remote.transport_params.ack_delay_exponent); + } + + ngtcp2_log_rx_fr(&conn->log, &hd, fr); + + if (hd.type == NGTCP2_PKT_0RTT) { + switch (fr->type) { + case NGTCP2_FRAME_PADDING: + case NGTCP2_FRAME_PING: + case NGTCP2_FRAME_RESET_STREAM: + case NGTCP2_FRAME_STOP_SENDING: + case NGTCP2_FRAME_STREAM: + case NGTCP2_FRAME_MAX_DATA: + case NGTCP2_FRAME_MAX_STREAM_DATA: + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + case NGTCP2_FRAME_DATA_BLOCKED: + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + case NGTCP2_FRAME_NEW_CONNECTION_ID: + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: + break; + default: + return NGTCP2_ERR_PROTO; + } + } + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + case NGTCP2_FRAME_PADDING: + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + break; + default: + require_ack = 1; + } + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STREAM: + rv = conn_recv_stream(conn, &fr->stream); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_CRYPTO: + rv = conn_recv_crypto(conn, NGTCP2_CRYPTO_LEVEL_APP, &pktns->crypto.strm, + &fr->crypto); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_RESET_STREAM: + rv = conn_recv_reset_stream(conn, &fr->reset_stream); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STOP_SENDING: + rv = conn_recv_stop_sending(conn, &fr->stop_sending); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + rv = conn_recv_max_stream_data(conn, &fr->max_stream_data); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_DATA: + conn_recv_max_data(conn, &fr->max_data); + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + rv = conn_recv_max_streams(conn, &fr->max_streams); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_PING: + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + conn_recv_path_challenge(conn, &fr->path_challenge); + break; + case NGTCP2_FRAME_PATH_RESPONSE: + rv = conn_recv_path_response(conn, &fr->path_response, ts); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + rv = conn_recv_new_connection_id(conn, &fr->new_connection_id); + if (rv != 0) { + return rv; + } + recv_ncid = 1; + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id, + ts); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_NEW_TOKEN: + rv = conn_recv_new_token(conn, &fr->new_token); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + rv = conn_recv_handshake_done(conn); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_DATA_BLOCKED: + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + /* TODO Not implemented yet */ + non_probing_pkt = 1; + break; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); + + if (recv_ncid) { + rv = conn_post_process_recv_new_connection_id(conn, ts); + if (rv != 0) { + return rv; + } + } + + if (conn->server && hd.type == NGTCP2_PKT_SHORT && non_probing_pkt && + pktns->rx.max_pkt_num < hd.pkt_num && + !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && + !conn_path_validation_in_progress(conn, path)) { + rv = conn_recv_non_probing_pkt_on_new_path(conn, path, new_cid_used, ts); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + + /* DCID is not available. Just continue. */ + assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv); + } + } + + if (hd.type == NGTCP2_PKT_SHORT) { + if (ckm == conn->crypto.key_update.new_rx_ckm) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); + conn_rotate_keys(conn, hd.pkt_num); + } else if (ckm->pkt_num > hd.pkt_num) { + ckm->pkt_num = hd.pkt_num; + } + } + + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num); + if (rv != 0) { + return rv; + } + + if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) { + ngtcp2_acktr_immediate_ack(&pktns->acktr); + } + + rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, + pkt_ts); + if (rv != 0) { + return rv; + } + + conn_restart_timer_on_read(conn, ts); + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->rcs, &conn->ccs); + + return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING + : (ngtcp2_ssize)pktlen; +} + +/* + * conn_process_buffered_protected_pkt processes buffered 0RTT or + * Short packets. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_pkt. + */ +static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_pkt_chain **ppc, *next; + int rv; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "processing buffered protected packet"); + + for (ppc = &pktns->rx.buffed_pkts; *ppc;) { + next = (*ppc)->next; + nread = conn_recv_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, (*ppc)->pktlen, + (*ppc)->ts, ts); + if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) && + nread != NGTCP2_ERR_DRAINING) { + /* TODO We don't know this is the first QUIC packet in a + datagram. */ + rv = conn_on_stateless_reset(conn, &(*ppc)->path.path, (*ppc)->pkt, + (*ppc)->pktlen); + if (rv == 0) { + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + return NGTCP2_ERR_DRAINING; + } + } + + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + if (nread < 0) { + if (nread == NGTCP2_ERR_DISCARD_PKT) { + continue; + } + return (int)nread; + } + } + + return 0; +} + +/* + * conn_process_buffered_handshake_pkt processes buffered Handshake + * packets. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_handshake_pkt. + */ +static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + ngtcp2_pktns *pktns = conn->hs_pktns; + ngtcp2_ssize nread; + ngtcp2_pkt_chain **ppc, *next; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "processing buffered handshake packet"); + + for (ppc = &pktns->rx.buffed_pkts; *ppc;) { + next = (*ppc)->next; + nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, + (*ppc)->pktlen, (*ppc)->ts, ts); + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + if (nread < 0) { + if (nread == NGTCP2_ERR_DISCARD_PKT) { + continue; + } + return (int)nread; + } + } + + return 0; +} + +static void conn_sync_stream_id_limit(ngtcp2_conn *conn) { + ngtcp2_transport_params *params = &conn->remote.transport_params; + + conn->local.bidi.max_streams = params->initial_max_streams_bidi; + conn->local.uni.max_streams = params->initial_max_streams_uni; +} + +/* + * conn_handshake_completed is called once cryptographic handshake has + * completed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_handshake_completed(ngtcp2_conn *conn) { + int rv; + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED; + + rv = conn_call_handshake_completed(conn); + if (rv != 0) { + return rv; + } + + if (conn->local.bidi.max_streams > 0) { + rv = conn_call_extend_max_local_streams_bidi(conn, + conn->local.bidi.max_streams); + if (rv != 0) { + return rv; + } + } + if (conn->local.uni.max_streams > 0) { + rv = conn_call_extend_max_local_streams_uni(conn, + conn->local.uni.max_streams); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * conn_recv_cpkt processes compound packet after handshake. The + * buffer pointed by |pkt| might contain multiple packets. The Short + * packet must be the last one because it does not have payload length + * field. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT. + */ +static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + int rv; + const uint8_t *origpkt = pkt; + size_t origpktlen = pktlen; + + while (pktlen) { + nread = conn_recv_pkt(conn, path, pkt, pktlen, ts, ts); + if (nread < 0) { + if (ngtcp2_err_is_fatal((int)nread)) { + return (int)nread; + } + + if (nread == NGTCP2_ERR_DRAINING) { + return NGTCP2_ERR_DRAINING; + } + + if (origpkt == pkt) { + rv = conn_on_stateless_reset(conn, path, origpkt, origpktlen); + if (rv == 0) { + return NGTCP2_ERR_DRAINING; + } + } + if (nread == NGTCP2_ERR_DISCARD_PKT) { + return 0; + } + return (int)nread; + } + + assert(pktlen >= (size_t)nread); + pkt += nread; + pktlen -= (size_t)nread; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "read packet %td left %zu", nread, pktlen); + } + + return 0; +} + +/* + * conn_is_retired_path returns nonzero if |path| is included in + * retired path list. + */ +static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { + size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired); + ngtcp2_dcid *dcid; + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + if (ngtcp2_path_eq(&dcid->ps.path, path)) { + return 1; + } + } + + return 0; +} + +int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) { + int rv = 0; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu", + pktlen); + + if (pktlen == 0) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + /* client does not expect a packet from unknown path. */ + if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && + (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) && + !conn_is_retired_path(conn, path)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "ignore packet from unknown path"); + return 0; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: + case NGTCP2_CS_SERVER_INITIAL: + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: + return ngtcp2_conn_read_handshake(conn, path, pkt, pktlen, ts); + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + case NGTCP2_CS_POST_HANDSHAKE: + rv = conn_prepare_key_update(conn, ts); + if (rv != 0) { + return rv; + } + return conn_recv_cpkt(conn, path, pkt, pktlen, ts); + default: + assert(0); + } +} + +/* + * conn_check_pkt_num_exhausted returns nonzero if packet number is + * exhausted in at least one of packet number space. + */ +static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + + return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || + (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || + conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM; +} + +/* + * conn_server_hs_tx_left returns the maximum number of bytes that + * server is allowed to send during handshake. + */ +static size_t conn_server_hs_tx_left(ngtcp2_conn *conn) { + if (conn->flags & NGTCP2_CONN_FLAG_SADDR_VERIFIED) { + return SIZE_MAX; + } + /* From QUIC spec: Prior to validating the client address, servers + MUST NOT send more than three times as many bytes as the number + of bytes they have received. */ + return conn->hs_recved * 3 - conn->hs_sent; +} + +/* + * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for + * transmission. + */ +static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + assert(conn->server); + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + return 0; +} + +int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { + int rv; + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + /* TODO Better to log something when we ignore input */ + return 0; + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { + /* Retry packet was received */ + return 0; + } + + assert(conn->hs_pktns); + + if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) { + rv = conn_process_buffered_handshake_pkt(conn, ts); + if (rv != 0) { + return rv; + } + + conn_discard_initial_state(conn); + } + + return 0; + case NGTCP2_CS_SERVER_INITIAL: + rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + /* + * Client ServerHello might not fit into single Initial packet + * (e.g., resuming session with client authentication). If we get + * Client Initial which does not increase offset or it is 0RTT + * packet buffered, perform address validation in order to buffer + * validated data only. + */ + if (ngtcp2_rob_first_gap_offset(&conn->in_pktns->crypto.strm.rx.rob) == 0) { + if (ngtcp2_rob_data_buffered(&conn->in_pktns->crypto.strm.rx.rob)) { + /* Address has been validated with token */ + if (conn->local.settings.token.len) { + return 0; + } + return NGTCP2_ERR_RETRY; + } + if (conn->in_pktns->rx.buffed_pkts) { + /* 0RTT is buffered, force retry */ + return NGTCP2_ERR_RETRY; + } + /* If neither CRYPTO frame nor 0RTT packet is processed, just + drop connection. */ + return NGTCP2_ERR_PROTO; + } + + /* Process re-ordered 0-RTT packets which arrived before Initial + packet. */ + if (conn->early.ckm) { + assert(conn->in_pktns); + + rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts); + if (rv != 0) { + return rv; + } + } + + return 0; + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + if (conn->hs_pktns->crypto.rx.ckm) { + rv = conn_process_buffered_handshake_pkt(conn, ts); + if (rv != 0) { + return rv; + } + } + + if (conn->hs_pktns->rx.max_pkt_num != -1) { + conn_discard_initial_state(conn); + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + return 0; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + + rv = conn_handshake_completed(conn); + if (rv != 0) { + return rv; + } + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return rv; + } + + conn_discard_handshake_state(conn); + + rv = conn_enqueue_handshake_done(conn); + if (rv != 0) { + return rv; + } + + return 0; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } +} + +/* + * conn_retransmit_retry_early retransmits 0RTT packet after Retry is + * received from server. + */ +static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + return conn_write_pkt(conn, dest, destlen, NULL, NGTCP2_PKT_0RTT, NULL, 0, + NULL, 0, NGTCP2_WRITE_PKT_FLAG_NONE, ts); +} + +/* + * conn_write_handshake writes QUIC handshake packets to the buffer + * pointed by |dest| of length |destlen|. |early_datalen| specifies + * the expected length of early data to send. Specify 0 to + * |early_datalen| if there is no early data. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * NGTCP2_ERR_PKT_NUM_EXHAUSTED + * Packet number is exhausted. + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM + * Required transport parameter is missing. + * NGTCP2_CS_CLOSING + * Connection is in closing state. + * NGTCP2_CS_DRAINING + * Connection is in draining state. + * + * In addition to the above negative error codes, the same error codes + * from conn_recv_pkt may also be returned. + */ +static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, size_t early_datalen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; + uint64_t cwnd; + size_t origlen = destlen; + size_t server_hs_tx_left; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + size_t pending_early_datalen; + ngtcp2_dcid *dcid; + ngtcp2_preferred_addr *paddr; + + cwnd = conn_cwnd_left(conn); + destlen = ngtcp2_min(destlen, cwnd); + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + pending_early_datalen = conn_retry_early_payloadlen(conn); + if (pending_early_datalen) { + early_datalen = pending_early_datalen; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { + nwrite = + conn_write_client_initial(conn, dest, destlen, early_datalen, ts); + if (nwrite <= 0) { + return nwrite; + } + } else { + nwrite = conn_write_handshake_pkt(conn, dest, destlen, NGTCP2_PKT_INITIAL, + early_datalen, ts); + if (nwrite < 0) { + return nwrite; + } + } + + if (pending_early_datalen) { + early_spktlen = conn_retransmit_retry_early(conn, dest + nwrite, + destlen - (size_t)nwrite, ts); + + if (early_spktlen < 0) { + assert(ngtcp2_err_is_fatal((int)early_spktlen)); + return early_spktlen; + } + } + + conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE; + + return nwrite + early_spktlen; + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite) { + return nwrite; + } + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + pending_early_datalen = conn_retry_early_payloadlen(conn); + if (pending_early_datalen) { + early_datalen = pending_early_datalen; + } + } + + nwrite = conn_write_handshake_pkts(conn, dest, destlen, early_datalen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + nwrite = conn_retransmit_retry_early(conn, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + if (res == 0) { + nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; + } + res = nwrite; + } + return res; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + rv = conn_handshake_completed(conn); + if (rv != 0) { + return (ngtcp2_ssize)rv; + } + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + if (conn->remote.transport_params.preferred_address_present) { + assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); + + paddr = &conn->remote.transport_params.preferred_address; + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); + } + + if (conn->remote.transport_params.stateless_reset_token_present) { + assert(conn->dcid.current.seq == 0); + memcpy(conn->dcid.current.token, + conn->remote.transport_params.stateless_reset_token, + sizeof(conn->dcid.current.token)); + } + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + conn_process_early_rtb(conn); + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return (ngtcp2_ssize)rv; + } + + if (conn->remote.transport_params.preferred_address_present) { + rv = conn_select_preferred_addr(conn); + if (rv != 0) { + return rv; + } + } + + return res; + case NGTCP2_CS_SERVER_INITIAL: + nwrite = conn_write_server_handshake(conn, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + if (nwrite) { + conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE; + conn->hs_sent += (size_t)nwrite; + } + + return nwrite; + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + server_hs_tx_left = conn_server_hs_tx_left(conn); + if (server_hs_tx_left == 0) { + if (rcs->loss_detection_timer) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + rcs->loss_detection_timer = 0; + } + return 0; + } + + destlen = ngtcp2_min(destlen, server_hs_tx_left); + origlen = ngtcp2_min(origlen, server_hs_tx_left); + + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite < 0) { + return nwrite; + } + + /* Coalesce packets, because server might send Initial as + probe, and Handshake as non-probe. */ + res += nwrite; + dest += nwrite; + if (destlen <= (size_t)nwrite) { + goto server_wait_handshake_done; + } + destlen -= (size_t)nwrite; + origlen -= (size_t)nwrite; + } + + nwrite = conn_write_server_handshake(conn, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + /* TODO Write 1RTT ACK packet if we have received 0RTT packet */ + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + origlen -= (size_t)nwrite; + + nwrite = conn_write_handshake_ack_pkts(conn, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + server_wait_handshake_done: + conn->hs_sent += (size_t)res; + return res; + } + + return 0; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } +} + +ngtcp2_ssize ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + return conn_write_handshake(conn, dest, destlen, 0, ts); +} + +ngtcp2_ssize ngtcp2_conn_client_write_handshake( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, int fin, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts) { + ngtcp2_strm *strm = NULL; + int send_stream = 0; + ngtcp2_ssize spktlen, early_spktlen; + uint64_t cwnd; + int was_client_initial; + size_t datalen = ngtcp2_vec_len(datav, datavcnt); + size_t early_datalen = 0; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + + assert(!conn->server); + + /* conn->early.ckm might be created in the first call of + conn_handshake(). Check it later. */ + if (stream_id != -1 && + !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return NGTCP2_ERR_STREAM_SHUT_WR; + } + + send_stream = conn_retry_early_payloadlen(conn) == 0 && + /* 0 length STREAM frame is allowed */ + (datalen == 0 || + (datalen > 0 && (strm->tx.max_offset - strm->tx.offset) && + (conn->tx.max_offset - conn->tx.offset))); + if (send_stream) { + early_datalen = + ngtcp2_min(datalen, strm->tx.max_offset - strm->tx.offset); + early_datalen = + ngtcp2_min(early_datalen, conn->tx.max_offset - conn->tx.offset) + + NGTCP2_STREAM_OVERHEAD; + + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; + } + } else { + strm = NULL; + } + } + + if (!ppe_pending) { + was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; + spktlen = conn_write_handshake(conn, dest, destlen, early_datalen, ts); + + if (spktlen < 0) { + return spktlen; + } + + if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || !send_stream) { + return spktlen; + } + } else { + assert(!conn->pktns.crypto.tx.ckm); + assert(conn->early.ckm); + + was_client_initial = conn->pkt.was_client_initial; + spktlen = conn->pkt.hs_spktlen; + } + + /* If spktlen > 0, we are making a compound packet. If Initial + packet is written, we have to pad bytes to 0-RTT packet. */ + + if (spktlen && was_client_initial) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + + cwnd = conn_cwnd_left(conn); + + dest += spktlen; + destlen -= (size_t)spktlen; + destlen = ngtcp2_min(destlen, cwnd); + + early_spktlen = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_0RTT, + strm, fin, datav, datavcnt, wflags, ts); + + if (early_spktlen < 0) { + switch (early_spktlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + return spktlen; + case NGTCP2_ERR_WRITE_STREAM_MORE: + conn->pkt.was_client_initial = was_client_initial; + conn->pkt.hs_spktlen = spktlen; + break; + } + return early_spktlen; + } + + return spktlen + early_spktlen; +} + +void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; +} + +int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); +} + +int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, + int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) { + int rv; + (void)conn; + + rv = ngtcp2_acktr_add(acktr, pkt_num, active_ack, ts); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + return rv; + } + + return 0; +} + +int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { + ngtcp2_ssize nread; + ngtcp2_pkt_hd hd, *p; + + if (dest) { + p = dest; + } else { + p = &hd; + } + + if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) { + return -1; + } + + nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); + if (nread < 0) { + return -1; + } + + switch (p->type) { + case NGTCP2_PKT_INITIAL: + if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) { + return -1; + } + if (p->tokenlen == 0 && p->dcid.datalen < 8) { + return -1; + } + break; + case NGTCP2_PKT_0RTT: + /* 0-RTT packet may arrive before Initial packet due to + re-ordering. */ + break; + default: + return -1; + } + + switch (p->version) { + case NGTCP2_PROTO_VER: + break; + default: + return 1; + } + + return 0; +} + +void ngtcp2_conn_set_aead_overhead(ngtcp2_conn *conn, size_t aead_overhead) { + conn->crypto.aead_overhead = aead_overhead; +} + +size_t ngtcp2_conn_get_aead_overhead(ngtcp2_conn *conn) { + return conn->crypto.aead_overhead; +} + +int ngtcp2_conn_install_initial_key(ngtcp2_conn *conn, const uint8_t *rx_key, + const uint8_t *rx_iv, + const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, + size_t ivlen) { + ngtcp2_pktns *pktns = conn->in_pktns; + int rv; + + assert(pktns); + + if (pktns->crypto.rx.hp_key) { + ngtcp2_vec_del(pktns->crypto.rx.hp_key, conn->mem); + pktns->crypto.rx.hp_key = NULL; + } + if (pktns->crypto.rx.ckm) { + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + } + if (pktns->crypto.tx.hp_key) { + ngtcp2_vec_del(pktns->crypto.tx.hp_key, conn->mem); + pktns->crypto.tx.hp_key = NULL; + } + if (pktns->crypto.tx.ckm) { + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, rx_key, keylen, + rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, tx_key, keylen, + tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_handshake_key( + ngtcp2_conn *conn, const uint8_t *rx_key, const uint8_t *rx_iv, + const uint8_t *rx_hp_key, const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t keylen, size_t ivlen) { + ngtcp2_pktns *pktns = conn->hs_pktns; + int rv; + + assert(pktns); + assert(!pktns->crypto.rx.hp_key); + assert(!pktns->crypto.rx.ckm); + assert(!pktns->crypto.tx.hp_key); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, rx_key, keylen, + rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, tx_key, keylen, + tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, const uint8_t *key, + const uint8_t *iv, const uint8_t *hp_key, + size_t keylen, size_t ivlen) { + int rv; + + assert(!conn->early.hp_key); + assert(!conn->early.ckm); + + rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, key, keylen, iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + return ngtcp2_vec_new(&conn->early.hp_key, hp_key, keylen, conn->mem); +} + +int ngtcp2_conn_install_key(ngtcp2_conn *conn, const uint8_t *rx_secret, + const uint8_t *tx_secret, const uint8_t *rx_key, + const uint8_t *rx_iv, const uint8_t *rx_hp_key, + const uint8_t *tx_key, const uint8_t *tx_iv, + const uint8_t *tx_hp_key, size_t secretlen, + size_t keylen, size_t ivlen) { + ngtcp2_pktns *pktns = &conn->pktns; + int rv; + + assert(!pktns->crypto.rx.hp_key); + assert(!pktns->crypto.rx.ckm); + assert(!pktns->crypto.tx.hp_key); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, rx_secret, secretlen, rx_key, + keylen, rx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.rx.hp_key, rx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, tx_secret, secretlen, tx_key, + keylen, tx_iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_vec_new(&pktns->crypto.tx.hp_key, tx_hp_key, keylen, conn->mem); + if (rv != 0) { + return rv; + } + + conn->remote.transport_params = conn->remote.pending_transport_params; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + + return 0; +} + +int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; + ngtcp2_duration pto = conn_compute_pto(conn); + + assert(conn->state == NGTCP2_CS_POST_HANDSHAKE); + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + !conn->crypto.key_update.new_tx_ckm || + !conn->crypto.key_update.new_rx_ckm || + (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) { + return NGTCP2_ERR_INVALID_STATE; + } + + conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); + + return 0; +} + +ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) { + if (conn->rcs.loss_detection_timer) { + return conn->rcs.loss_detection_timer; + } + return UINT64_MAX; +} + +ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { + ngtcp2_tstamp res = UINT64_MAX; + ngtcp2_duration pto = conn_compute_pto(conn); + ngtcp2_scid *scid; + ngtcp2_dcid *dcid; + + if (conn->pv) { + res = ngtcp2_pv_next_expiry(conn->pv); + } + + if (!ngtcp2_pq_empty(&conn->scid.used)) { + scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); + if (scid->ts_retired != UINT64_MAX) { + res = ngtcp2_min(res, scid->ts_retired + pto); + } + } + + if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + res = ngtcp2_min(res, dcid->ts_retired + pto); + } + + return res; +} + +ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { + ngtcp2_acktr *acktr = &conn->pktns.acktr; + + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts != UINT64_MAX) { + return acktr->first_unacked_ts + conn_compute_ack_delay(conn); + } + return UINT64_MAX; +} + +ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { + ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); + ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); + ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); + ngtcp2_tstamp res = ngtcp2_min(t1, t2); + return ngtcp2_min(res, t3); +} + +int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { + rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, + ngtcp2_tstamp ts) { + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts <= ts) { + acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; + } +} + +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (conn->in_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); + } + if (conn->hs_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); + } + acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); +} + +/* + * conn_client_validate_transport_params validates |params| as client. + * |params| must be sent with Encrypted Extensions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_TRANSPORT_PARAM + * Transport parameters are invalid. + */ +static int +conn_client_validate_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params) { + if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + if (!params->original_connection_id_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_connection_id)) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + } else if (params->original_connection_id_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + return 0; +} + +int ngtcp2_conn_set_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params) { + int rv; + + assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); + + /* Assume that ngtcp2_decode_transport_params sets default value if + active_connection_id_limit is omitted. */ + if (params->active_connection_id_limit < + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + if (!conn->server) { + rv = conn_client_validate_transport_params(conn, params); + if (rv != 0) { + return rv; + } + } + + ngtcp2_log_remote_tp(&conn->log, + conn->server + ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + params); + + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, + /* local = */ 0); + + if (conn->pktns.crypto.rx.ckm) { + conn->remote.transport_params = *params; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + } else { + conn->remote.pending_transport_params = *params; + } + + conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; + + return 0; +} + +void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params) { + if (conn->pktns.crypto.rx.ckm) { + *params = conn->remote.transport_params; + } else { + *params = conn->remote.pending_transport_params; + } +} + +void ngtcp2_conn_set_early_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p = &conn->remote.transport_params; + + assert(!conn->server); + + memset(p, 0, sizeof(*p)); + + p->initial_max_streams_bidi = params->initial_max_streams_bidi; + p->initial_max_streams_uni = params->initial_max_streams_uni; + p->initial_max_stream_data_bidi_local = + params->initial_max_stream_data_bidi_local; + p->initial_max_stream_data_bidi_remote = + params->initial_max_stream_data_bidi_remote; + p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; + p->initial_max_data = params->initial_max_data; + + conn_sync_stream_id_limit(conn); + + conn->tx.max_offset = p->initial_max_data; + + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, + /* local = */ 0); +} + +void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params) { + *params = conn->local.settings.transport_params; +} + +int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, + void *stream_user_data) { + int rv; + ngtcp2_strm *strm; + + if (ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id) > + conn->local.bidi.max_streams) { + return NGTCP2_ERR_STREAM_ID_BLOCKED; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, + stream_user_data); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + + *pstream_id = conn->local.bidi.next_stream_id; + conn->local.bidi.next_stream_id += 4; + + return 0; +} + +int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, + void *stream_user_data) { + int rv; + ngtcp2_strm *strm; + + if (ngtcp2_ord_stream_id(conn->local.uni.next_stream_id) > + conn->local.uni.max_streams) { + return NGTCP2_ERR_STREAM_ID_BLOCKED; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, + stream_user_data); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); + + *pstream_id = conn->local.uni.next_stream_id; + conn->local.uni.next_stream_id += 4; + + return 0; +} + +ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { + ngtcp2_map_entry *me; + + me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); + if (me == NULL) { + return NULL; + } + + return ngtcp2_struct_of(me, ngtcp2_strm, me); +} + +ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, + uint8_t *dest, size_t destlen, + ngtcp2_ssize *pdatalen, uint32_t flags, + int64_t stream_id, int fin, + const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts) { + ngtcp2_vec datav; + + datav.len = datalen; + datav.base = (uint8_t *)data; + + return ngtcp2_conn_writev_stream(conn, path, dest, destlen, pdatalen, flags, + stream_id, fin, &datav, 1, ts); +} + +ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, + uint8_t *dest, size_t destlen, + ngtcp2_ssize *pdatalen, uint32_t flags, + int64_t stream_id, int fin, + const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { + ngtcp2_strm *strm = NULL; + ngtcp2_ssize nwrite; + uint64_t cwnd; + ngtcp2_pktns *pktns = &conn->pktns; + size_t origlen = destlen; + int rv; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + ngtcp2_ssize res = 0; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (pdatalen) { + *pdatalen = -1; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: + nwrite = + ngtcp2_conn_client_write_handshake(conn, dest, destlen, pdatalen, flags, + stream_id, fin, datav, datavcnt, ts); + if (nwrite < 0 || conn->state != NGTCP2_CS_POST_HANDSHAKE) { + return nwrite; + } + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + /* Break here so that we can coalesces Short packets. */ + break; + case NGTCP2_CS_SERVER_INITIAL: + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: + if (!ppe_pending) { + nwrite = ngtcp2_conn_write_handshake(conn, dest, destlen, ts); + if (nwrite) { + return nwrite; + } + } + if (conn->state != NGTCP2_CS_POST_HANDSHAKE && + conn->pktns.crypto.tx.ckm == NULL) { + return 0; + } + break; + case NGTCP2_CS_POST_HANDSHAKE: + break; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } + + assert(pktns->crypto.tx.ckm); + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { + rv = conn_prepare_key_update(conn, ts); + if (rv != 0) { + return rv; + } + } + + rv = conn_remove_retired_connection_id(conn, ts); + if (rv != 0) { + return rv; + } + + if (stream_id != -1) { + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return NGTCP2_ERR_STREAM_SHUT_WR; + } + + if (flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_STREAM_MORE; + } + } + + cwnd = conn_cwnd_left(conn); + destlen = ngtcp2_min(destlen, cwnd); + + if (ppe_pending) { + res = conn->pkt.hs_spktlen; + conn->pkt.hs_spktlen = 0; + /* dest and destlen have already been adjusted in ppe in the first + run. They are adjusted for probe packet later. */ + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, + strm, fin, datav, datavcnt, wflags, ts); + goto fin; + } + + if (conn->pv) { + nwrite = conn_write_path_challenge(conn, path, dest, origlen, ts); + if (nwrite) { + goto fin; + } + } + + if (res == 0) { + if (conn_handshake_remnants_left(conn)) { + if ((conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left) { + nwrite = conn_write_handshake_pkts(conn, dest, origlen, 0, ts); + if (nwrite) { + return nwrite; + } + } else { + nwrite = conn_write_handshake_pkts(conn, dest, destlen, 0, ts); + if (nwrite < 0) { + return nwrite; + } + if (nwrite > 0) { + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + } + } + } + + if (conn->pktns.rtb.probe_pkt_left) { + nwrite = conn_write_probe_pkt(conn, dest, origlen, pdatalen, strm, fin, + datav, datavcnt, ts); + goto fin; + } + + nwrite = conn_write_pkt(conn, dest, destlen, pdatalen, NGTCP2_PKT_SHORT, strm, + fin, datav, datavcnt, wflags, ts); + if (nwrite) { + assert(nwrite != NGTCP2_ERR_NOBUF); + goto fin; + } + + if (res == 0) { + return conn_write_ack_pkt(conn, dest, origlen, NGTCP2_PKT_SHORT, ts); + } + +fin: + conn->pkt.hs_spktlen = 0; + + if (nwrite >= 0) { + return res + nwrite; + } + /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above. + ppe_pending cannot be used here. */ + if (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) { + conn->pkt.hs_spktlen = res; + } + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, + ngtcp2_path *path, + uint8_t *dest, size_t destlen, + uint64_t error_code, + ngtcp2_tstamp ts) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_ssize res = 0, nwrite; + ngtcp2_frame fr; + uint8_t pkt_type; + int bundle = 0; + uint8_t bundle_pkt_type; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + switch (conn->state) { + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = error_code; + fr.connection_close.frame_type = 0; + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { + pkt_type = NGTCP2_PKT_SHORT; + } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_HANDSHAKE; + } else if (in_pktns && in_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_INITIAL; + } else { + /* This branch is taken if server has not read any Initial packet + from client. */ + return NGTCP2_ERR_INVALID_STATE; + } + + if (conn->server && pkt_type == NGTCP2_PKT_HANDSHAKE && in_pktns) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_INITIAL; + } else if (pkt_type == NGTCP2_PKT_SHORT && hs_pktns && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + bundle = 1; + bundle_pkt_type = NGTCP2_PKT_HANDSHAKE; + } + + if (bundle) { + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, dest, destlen, bundle_pkt_type, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_FLAG_NONE, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + } + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, dest + res, destlen - (size_t)res, pkt_type, + &conn->dcid.current.cid, &fr, NGTCP2_RTB_FLAG_NONE, ts); + + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + if (res == 0) { + return NGTCP2_ERR_NOBUF; + } + + conn->state = NGTCP2_CS_CLOSING; + + return res; +} + +ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, + ngtcp2_path *path, + uint8_t *dest, size_t destlen, + uint64_t app_error_code, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_frame fr; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + switch (conn->state) { + case NGTCP2_CS_POST_HANDSHAKE: + break; + default: + return NGTCP2_ERR_INVALID_STATE; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; + fr.connection_close.error_code = app_error_code; + fr.connection_close.frame_type = 0; + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_FLAG_NONE, ts); + + if (nwrite < 0) { + return nwrite; + } + + if (nwrite == 0) { + return NGTCP2_ERR_NOBUF; + } + + conn->state = NGTCP2_CS_CLOSING; + + return nwrite; +} + +int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { + return conn->state == NGTCP2_CS_CLOSING; +} + +int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) { + return conn->state == NGTCP2_CS_DRAINING; +} + +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + + if (!strm->app_error_code) { + app_error_code = strm->app_error_code; + } + + rv = ngtcp2_map_remove(&conn->strms, strm->me.key); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + return rv; + } + + rv = conn_call_stream_close(conn, strm, app_error_code); + if (rv != 0) { + goto fin; + } + + if (ngtcp2_strm_is_tx_queued(strm)) { + ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); + } + +fin: + ngtcp2_strm_free(strm); + ngtcp2_mem_free(conn->mem, strm); + + return rv; +} + +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == + NGTCP2_STRM_FLAG_SHUT_RDWR && + ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) || + ngtcp2_rob_first_gap_offset(&strm->rx.rob) == strm->rx.last_offset) && + (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && + (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) || + (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && + ngtcp2_strm_is_all_tx_data_acked(strm)))) { + return ngtcp2_conn_close_stream(conn, strm, app_error_code); + } + return 0; +} + +/* + * conn_shutdown_stream_write closes send stream with error code + * |app_error_code|. RESET_STREAM frame is scheduled. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) { + return 0; + } + + /* Set this flag so that we don't accidentally send DATA to this + stream. */ + strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + strm->app_error_code = app_error_code; + + ngtcp2_strm_streamfrq_clear(strm); + + return conn_reset_stream(conn, strm, app_error_code); +} + +/* + * conn_shutdown_stream_read closes read stream with error code + * |app_error_code|. STOP_SENDING frame is scheduled. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return 0; + } + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) { + return 0; + } + + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST))) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); + } + + strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; + strm->app_error_code = app_error_code; + + return conn_stop_sending(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + int rv; + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + rv = conn_shutdown_stream_read(conn, strm, app_error_code); + if (rv != 0) { + return rv; + } + + rv = conn_shutdown_stream_write(conn, strm, app_error_code); + if (rv != 0) { + return rv; + } + + return 0; +} + +int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_shutdown_stream_write(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_shutdown_stream_read(conn, strm, app_error_code); +} + +/* + * conn_extend_max_stream_offset extends stream level flow control + * window by |datalen| of the stream denoted by |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, + size_t datalen) { + ngtcp2_strm *top; + + if (strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { + strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT; + } else { + strm->rx.unsent_max_offset += datalen; + } + + if (!(strm->flags & + (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) && + !ngtcp2_strm_is_tx_queued(strm) && + conn_should_send_max_stream_data(conn, strm)) { + if (!ngtcp2_pq_empty(&conn->tx.strmq)) { + top = ngtcp2_conn_tx_strmq_top(conn); + strm->cycle = top->cycle; + } + strm->cycle = conn_tx_strmq_first_cycle(conn); + return ngtcp2_conn_tx_strmq_push(conn, strm); + } + + return 0; +} + +int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, + size_t datalen) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_extend_max_stream_offset(conn, strm, datalen); +} + +void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, size_t datalen) { + if (NGTCP2_MAX_VARINT < (uint64_t)datalen || + conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { + conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT; + return; + } + + conn->rx.unsent_max_offset += datalen; +} + +void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); +} + +void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); +} + +size_t ngtcp2_conn_get_bytes_in_flight(ngtcp2_conn *conn) { + return conn->ccs.bytes_in_flight; +} + +const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { + return &conn->dcid.current.cid; +} + +uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { + return conn->version; +} + +int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_frame_chain *frc = NULL; + int rv; + + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + + ngtcp2_rtb_remove_all(rtb, &frc); + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + return rv; +} + +void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + + rcs->latest_rtt = rtt; + + if (rcs->smoothed_rtt == 0) { + rcs->min_rtt = rtt; + rcs->smoothed_rtt = rtt; + rcs->rttvar = rtt / 2; + return; + } + + rcs->min_rtt = ngtcp2_min(rcs->min_rtt, rtt); + if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { + ack_delay = + ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); + } else { + ack_delay = ngtcp2_min(ack_delay, NGTCP2_DEFAULT_MAX_ACK_DELAY); + } + if (rtt > rcs->min_rtt + ack_delay) { + rtt -= ack_delay; + } + + rcs->rttvar = + (rcs->rttvar * 3 + (rcs->smoothed_rtt < rtt ? rtt - rcs->smoothed_rtt + : rcs->smoothed_rtt - rtt)) / + 4; + rcs->smoothed_rtt = (rcs->smoothed_rtt * 7 + rtt) / 8; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 + " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64 + " ack_delay=%" PRIu64, + (uint64_t)(rcs->latest_rtt / NGTCP2_MILLISECONDS), + (uint64_t)(rcs->min_rtt / NGTCP2_MILLISECONDS), + rcs->smoothed_rtt / NGTCP2_MILLISECONDS, + rcs->rttvar / NGTCP2_MILLISECONDS, + (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); +} + +const ngtcp2_rcvry_stat *ngtcp2_conn_get_rcvry_stat(ngtcp2_conn *conn) { + return &conn->rcs; +} + +const ngtcp2_cc_stat *ngtcp2_conn_get_cc_stat(ngtcp2_conn *conn) { + return &conn->ccs; +} + +static ngtcp2_pktns *conn_get_earliest_loss_time_pktns(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_pktns *res = in_pktns; + + if (hs_pktns && hs_pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || hs_pktns->rtb.loss_time < res->rtb.loss_time)) { + res = hs_pktns; + } + if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + pktns->rtb.loss_time != UINT64_MAX && + (res == NULL || pktns->rtb.loss_time < res->rtb.loss_time)) { + res = pktns; + } + + return res; +} + +static ngtcp2_tstamp conn_get_earliest_last_tx_pkt_ts(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_tstamp earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + ngtcp2_pktns *pktns = ns[0]; + size_t i; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (!pktns || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + pktns = ns[i]; + } + } + + return earliest_ts; +} + +static ngtcp2_pktns * +conn_get_earliest_non_null_last_tx_pktns(ngtcp2_conn *conn) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_pktns *res = ns[0]; + ngtcp2_rcvry_stat *rcs = &conn->rcs; + size_t i, earliest_ts = rcs->last_tx_pkt_ts[NGTCP2_CRYPTO_LEVEL_INITIAL]; + + for (i = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; i <= NGTCP2_CRYPTO_LEVEL_APP; ++i) { + if (res == NULL || + (rcs->last_tx_pkt_ts[i] != UINT64_MAX && + (earliest_ts == UINT64_MAX || rcs->last_tx_pkt_ts[i] < earliest_ts) && + (i != NGTCP2_CRYPTO_LEVEL_APP || + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)))) { + earliest_ts = rcs->last_tx_pkt_ts[i]; + res = ns[i]; + } + } + + return res; +} + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + ngtcp2_duration timeout; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_pktns *earliest_pktns = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_tstamp last_tx_pkt_ts; + + if (earliest_pktns && earliest_pktns->rtb.loss_time != UINT64_MAX) { + rcs->loss_detection_timer = earliest_pktns->rtb.loss_time; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", + rcs->loss_detection_timer); + return; + } + + if ((!in_pktns || ngtcp2_rtb_num_ack_eliciting(&in_pktns->rtb) == 0) && + (!hs_pktns || ngtcp2_rtb_num_ack_eliciting(&hs_pktns->rtb) == 0) && + ngtcp2_rtb_num_ack_eliciting(&pktns->rtb) == 0 && + (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { + if (rcs->loss_detection_timer) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + rcs->loss_detection_timer = 0; + } + return; + } + + if (rcs->smoothed_rtt == 0) { + timeout = 2 * NGTCP2_DEFAULT_INITIAL_RTT; + } else { + timeout = conn_compute_pto(conn); + } + timeout *= 1ULL << rcs->pto_count; + + last_tx_pkt_ts = conn_get_earliest_last_tx_pkt_ts(conn); + + if (last_tx_pkt_ts == UINT64_MAX) { + last_tx_pkt_ts = ts; + } + + rcs->loss_detection_timer = last_tx_pkt_ts + timeout; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 + " timeout=%" PRIu64, + rcs->loss_detection_timer, last_tx_pkt_ts, + (uint64_t)(timeout / NGTCP2_MILLISECONDS)); +} + +/* + * conn_handshake_pkt_lost is called when handshake packets which + * belong to |pktns| are lost. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_on_crypto_timeout(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + ngtcp2_frame_chain *frc = NULL; + int rv; + + rv = ngtcp2_rtb_on_crypto_timeout(&pktns->rtb, &frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + rv = conn_resched_frames(conn, pktns, &frc); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frc, conn->mem); + return rv; + } + + return 0; +} + +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_rcvry_stat *rcs = &conn->rcs; + int rv; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *loss_pktns = conn_get_earliest_loss_time_pktns(conn); + ngtcp2_pktns *earliest_pktns; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + switch (conn->state) { + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + rcs->loss_detection_timer = 0; + return 0; + default: + break; + } + + if (!rcs->loss_detection_timer) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer fired"); + + if (loss_pktns && loss_pktns->rtb.loss_time != UINT64_MAX) { + rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, rcs, ts); + if (rv != 0) { + return rv; + } + ngtcp2_conn_set_loss_detection_timer(conn, ts); + return 0; + } + + if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (hs_pktns->crypto.tx.ckm) { + hs_pktns->rtb.probe_pkt_left = 1; + } else { + conn_on_crypto_timeout(conn, in_pktns); + in_pktns->rtb.probe_pkt_left = 1; + } + } else { + earliest_pktns = conn_get_earliest_non_null_last_tx_pktns(conn); + + assert(earliest_pktns); + + switch (earliest_pktns->rtb.crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + assert(in_pktns); + rv = conn_on_crypto_timeout(conn, in_pktns); + if (rv != 0) { + return rv; + } + in_pktns->rtb.probe_pkt_left = 1; + if (!conn->server) { + break; + } + /* fall through for server so that it can coalesce packets. */ + /* fall through */ + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + assert(hs_pktns); + rv = conn_on_crypto_timeout(conn, hs_pktns); + if (rv != 0) { + return rv; + } + hs_pktns->rtb.probe_pkt_left = 1; + break; + case NGTCP2_CRYPTO_LEVEL_APP: + conn->pktns.rtb.probe_pkt_left = 2; + break; + default: + assert(0); + } + } + + ++rcs->pto_count; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu", + rcs->pto_count); + + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; +} + +int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, const size_t datalen) { + ngtcp2_pktns *pktns; + ngtcp2_frame_chain *frc; + ngtcp2_crypto *fr; + ngtcp2_ksl_key key; + int rv; + + if (datalen == 0) { + return 0; + } + + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + assert(conn->in_pktns); + pktns = conn->in_pktns; + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + assert(conn->hs_pktns); + pktns = conn->hs_pktns; + break; + case NGTCP2_CRYPTO_LEVEL_APP: + pktns = &conn->pktns; + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + fr = &frc->fr.crypto; + + fr->type = NGTCP2_FRAME_CRYPTO; + fr->offset = pktns->crypto.tx.offset; + fr->datacnt = 1; + fr->data[0].len = datalen; + fr->data[0].base = (uint8_t *)data; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + ngtcp2_ksl_key_ptr(&key, &fr->offset), frc); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + pktns->crypto.strm.tx.offset += datalen; + pktns->crypto.tx.offset += datalen; + + return 0; +} + +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) { + assert(!ngtcp2_pq_empty(&conn->tx.strmq)); + return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); +} + +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) { + ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn); + assert(strm); + ngtcp2_pq_pop(&conn->tx.strmq); + strm->pe.index = NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { + return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); +} + +size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) { + return ngtcp2_ksl_len(&conn->scid.set); +} + +size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + + for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + scid = ngtcp2_ksl_it_get(&it); + *dest++ = scid->cid; + } + + return ngtcp2_ksl_len(&conn->scid.set); +} + +size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { + size_t n = 1; /* for conn->dcid.current */ + ngtcp2_pv *pv = conn->pv; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq) { + ++n; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq != pv->dcid.seq) { + ++n; + } + } + + n += ngtcp2_ringbuf_len(&conn->dcid.retired); + + return n; +} + +static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, + const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + ngtcp2_path_storage_init2(&dest->ps, &src->ps.path); + dest->token_present = + (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token); + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_cid_token *orig = dest; + ngtcp2_dcid *dcid; + size_t len, i; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + copy_dcid_to_cid_token(dest, &conn->dcid.current); + ++dest; + + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq) { + copy_dcid_to_cid_token(dest, &pv->dcid); + ++dest; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq != pv->dcid.seq) { + copy_dcid_to_cid_token(dest, &pv->fallback_dcid); + ++dest; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + copy_dcid_to_cid_token(dest, dcid); + ++dest; + } + + return (size_t)(dest - orig); +} + +void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { + ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; + + assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf)); + ngtcp2_addr_copy(dest, addr); +} + +void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { + ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote; + + assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf)); + ngtcp2_addr_copy(dest, addr); +} + +const ngtcp2_addr *ngtcp2_conn_get_remote_addr(ngtcp2_conn *conn) { + return &conn->dcid.current.ps.path.remote; +} + +int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn->remote.transport_params.disable_active_migration || + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + return NGTCP2_ERR_INVALID_STATE; + } + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return NGTCP2_ERR_CONN_ID_BLOCKED; + } + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } + + ngtcp2_dcid_copy(&conn->dcid.current, dcid); + ngtcp2_path_copy(&conn->dcid.current.ps.path, path); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + conn_reset_congestion_state(conn); + + return 0; +} + +uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) { + return conn->local.uni.max_streams; +} + +uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { + return conn->tx.max_offset - conn->tx.offset; +} + +ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { + ngtcp2_duration trpto; + ngtcp2_duration idle_timeout; + + /* TODO Remote max_idle_timeout becomes effective after handshake + completion. */ + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + (conn->local.settings.transport_params.max_idle_timeout && + conn->local.settings.transport_params.max_idle_timeout < + conn->remote.transport_params.max_idle_timeout)) { + idle_timeout = conn->local.settings.transport_params.max_idle_timeout; + } else { + idle_timeout = conn->remote.transport_params.max_idle_timeout; + } + + if (idle_timeout == 0) { + return UINT64_MAX; + } + + trpto = 3 * conn_compute_pto(conn); + + return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); +} + +ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { + return conn_compute_pto(conn); +} + +void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx) { + assert(conn->in_pktns); + conn->in_pktns->crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) { + assert(conn->in_pktns); + return &conn->in_pktns->crypto.ctx; +} + +void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead) { + conn->crypto.retry_aead = *aead; +} + +void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx) { + assert(conn->hs_pktns); + conn->hs_pktns->crypto.ctx = *ctx; + conn->pktns.crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) { + return &conn->pktns.crypto.ctx; +} + +void ngtcp2_conn_get_connection_close_error_code( + ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) { + *ccec = conn->rx.ccec; +} + +void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) { + conn->crypto.tls_error = liberr; +} + +int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) { + return conn->crypto.tls_error; +} + +int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { + return conn_local_stream(conn, stream_id); +} + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, + const uint8_t *data) { + memcpy(pcent->data, data, sizeof(pcent->data)); +} + +void ngtcp2_settings_default(ngtcp2_settings *settings) { + memset(settings, 0, sizeof(*settings)); + settings->transport_params.max_packet_size = NGTCP2_MAX_PKT_SIZE; + settings->transport_params.ack_delay_exponent = + NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + settings->transport_params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + settings->transport_params.active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; +} + +/* The functions prefixed with ngtcp2_pkt_ are usually put inside + ngtcp2_pkt.c. This function uses encryption construct and uses + test data defined only in ngtcp2_conn_test.c, so it is written + here. */ +ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const uint8_t *key, const uint8_t *iv, + ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const uint8_t *hp_key) { + ngtcp2_pkt_hd hd; + ngtcp2_crypto_km ckm; + ngtcp2_crypto_cc cc; + ngtcp2_vec hp_key_vec; + ngtcp2_ppe ppe; + ngtcp2_frame fr = {0}; + int rv; + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, + NGTCP2_PROTO_VER, /* len = */ 0); + + ngtcp2_vec_init(&ckm.secret, NULL, 0); + ngtcp2_vec_init(&ckm.key, key, 16); + ngtcp2_vec_init(&ckm.iv, iv, 12); + ngtcp2_vec_init(&hp_key_vec, hp_key, 16); + ckm.pkt_num = 0; + ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + + cc.aead_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + cc.aead = *aead; + cc.hp = *hp; + cc.ckm = &ckm; + cc.hp_key = &hp_key_vec; + cc.encrypt = encrypt; + cc.hp_mask = hp_mask; + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return NGTCP2_ERR_NOBUF; + } + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = error_code; + + rv = ngtcp2_ppe_encode_frame(&ppe, &fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + return ngtcp2_ppe_final(&ppe, NULL); +} + +int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } diff --git a/deps/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/lib/ngtcp2_conn.h new file mode 100644 index 00000000000000..3b17a3d230ada7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conn.h @@ -0,0 +1,708 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONN_H +#define NGTCP2_CONN_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_crypto.h" +#include "ngtcp2_acktr.h" +#include "ngtcp2_rtb.h" +#include "ngtcp2_strm.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_idtr.h" +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_log.h" +#include "ngtcp2_pq.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_pv.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_ppe.h" +#include "ngtcp2_qlog.h" +#include "ngtcp2_rst.h" + +typedef enum { + /* Client specific handshake states */ + NGTCP2_CS_CLIENT_INITIAL, + NGTCP2_CS_CLIENT_WAIT_HANDSHAKE, + NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED, + /* Server specific handshake states */ + NGTCP2_CS_SERVER_INITIAL, + NGTCP2_CS_SERVER_WAIT_HANDSHAKE, + NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED, + /* Shared by both client and server */ + NGTCP2_CS_POST_HANDSHAKE, + NGTCP2_CS_CLOSING, + NGTCP2_CS_DRAINING, +} ngtcp2_conn_state; + +/* NGTCP2_MAX_STREAMS is the maximum number of streams. */ +#define NGTCP2_MAX_STREAMS (1LL << 60) + +/* NGTCP2_MAX_NUM_BUFFED_RX_PKTS is the maximum number of buffered + reordered packets. */ +#define NGTCP2_MAX_NUM_BUFFED_RX_PKTS 4 + +/* NGTCP2_MAX_REORDERED_CRYPTO_DATA is the maximum offset of crypto + data which is not continuous. In other words, there is a gap of + unreceived data. */ +#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536 + +/* NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA is the maximum offset of received + crypto stream in Initial packet. We set this hard limit here + because crypto stream is unbounded. */ +#define NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA 65536 +/* NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA is the maximum offset of + received crypto stream in Handshake packet. We set this hard limit + here because crypto stream is unbounded. */ +#define NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA 65536 + +/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can + accept. */ +#define NGTCP2_MAX_RETRIES 3 + +/* NGTCP2_MAX_DCID_POOL_SIZE is the maximum number of destination + connection ID the remote endpoint provides to store. It must be + the power of 2. */ +#define NGTCP2_MAX_DCID_POOL_SIZE 8 +/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID + kept to catch in-flight packet on retired path. */ +#define NGTCP2_MAX_DCID_RETIRED_SIZE 2 +/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source + connection ID the local endpoint provides to the remote endpoint. + The chosen value was described in old draft. Now a remote endpoint + tells the maximum value. The value can be quite large, and we have + to put the sane limit.*/ +#define NGTCP2_MAX_SCID_POOL_SIZE 8 + +/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non + ACK-eliciting packets. */ +#define NGTCP2_MAX_NON_ACK_TX_PKT 10 + +/* + * ngtcp2_max_frame is defined so that it covers the largest ACK + * frame. + */ +typedef union { + ngtcp2_frame fr; + struct { + ngtcp2_ack ack; + /* ack includes 1 ngtcp2_ack_blk. */ + ngtcp2_ack_blk blks[NGTCP2_MAX_ACK_BLKS - 1]; + } ackfr; +} ngtcp2_max_frame; + +typedef struct { + uint8_t data[8]; +} ngtcp2_path_challenge_entry; + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, + const uint8_t *data); + +typedef enum { + NGTCP2_CONN_FLAG_NONE = 0x00, + /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake + completed. */ + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED = 0x01, + /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is + negotiated. This is only used for client. */ + NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED = 0x02, + /* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport + parameters are received. */ + NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED = 0x04, + /* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected + packet is received, and decrypted successfully. This flag is + used to stop retransmitting handshake packets. It might be + replaced with an another mechanism when we implement key + update. */ + NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT = 0x08, + /* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry + packet. */ + NGTCP2_CONN_FLAG_RECV_RETRY = 0x10, + /* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is + rejected by a peer. */ + NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED = 0x20, + /* NGTCP2_CONN_FLAG_SADDR_VERIFIED is set when source address is + verified. */ + NGTCP2_CONN_FLAG_SADDR_VERIFIED = 0x40, + /* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint + confirmed completion of handshake. */ + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED = 0x80, + /* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the + library transitions its state to "post handshake". */ + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED = 0x0100, + /* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update + is not confirmed by the local endpoint. That is, it has not + received ACK frame which acknowledges packet which is encrypted + with new key. */ + NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED = 0x0800, + /* NGTCP2_CONN_FLAG_PPE_PENDING is set when + NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state + of ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ + NGTCP2_CONN_FLAG_PPE_PENDING = 0x1000, + /* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle + timer should be restarted on next write. */ + NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE = 0x2000, + /* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as + peer verified client address. This flag is only used by + client. */ + NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED = 0x4000, +} ngtcp2_conn_flag; + +typedef struct { + ngtcp2_buf buf; + /* pkt_type is the type of packet to send data in buf. If it is 0, + it must be sent in Short packet. Otherwise, it is sent the long + packet type denoted by pkt_type. */ + uint8_t pkt_type; +} ngtcp2_crypto_data; + +typedef struct { + struct { + /* last_pkt_num is the packet number which the local endpoint sent + last time.*/ + int64_t last_pkt_num; + ngtcp2_frame_chain *frq; + /* num_non_ack_pkt is the number of continuous non ACK-eliciting + packets. */ + size_t num_non_ack_pkt; + } tx; + + struct { + /* pngap tracks received packet number in order to suppress + duplicated packet number. */ + ngtcp2_gaptr pngap; + /* max_pkt_num is the largest packet number received so far. */ + int64_t max_pkt_num; + /* + * buffed_pkts is buffered packets which cannot be decrypted with + * the current encryption level. + * + * In server Initial encryption level, 0-RTT packet may be buffered. + * In server Handshake encryption level, Short packet may be buffered. + * + * In client Initial encryption level, Handshake or Short packet may + * be buffered. In client Handshake encryption level, Short packet + * may be buffered. + * + * - 0-RTT packet is only buffered in server Initial encryption + * level ngtcp2_pktns. + * + * - Handshake packet is only buffered in client Handshake + * encryption level ngtcp2_pktns. + * + * - Short packet is only buffered in Short encryption level + * ngtcp2_pktns. + */ + ngtcp2_pkt_chain *buffed_pkts; + } rx; + + struct { + struct { + /* frq contains crypto data sorted by their offset. */ + ngtcp2_ksl frq; + /* offset is the offset of crypto stream in this packet number + space. */ + uint64_t offset; + /* ckm is a cryptographic key, and iv to encrypt outgoing + packets. */ + ngtcp2_crypto_km *ckm; + /* hp_key is header protection key. */ + ngtcp2_vec *hp_key; + } tx; + + struct { + /* ckm is a cryptographic key, and iv to decrypt incoming + packets. */ + ngtcp2_crypto_km *ckm; + /* hp_key is header protection key. */ + ngtcp2_vec *hp_key; + } rx; + + ngtcp2_strm strm; + ngtcp2_crypto_ctx ctx; + } crypto; + + ngtcp2_acktr acktr; + ngtcp2_rtb rtb; +} ngtcp2_pktns; + +struct ngtcp2_conn { + ngtcp2_conn_state state; + ngtcp2_conn_callbacks callbacks; + /* rcid is a connection ID present in Initial or 0-RTT packet from + client as destination connection ID. Server uses this field to + check that duplicated Initial or 0-RTT packet are indeed sent to + this connection. Client uses this field to validate + original_connection_id transport parameter. */ + ngtcp2_cid rcid; + /* oscid is the source connection ID initially used by the local + endpoint. */ + ngtcp2_cid oscid; + /* odcid is the destination connection ID initially negotiated + during handshake. It is used to receive late handshake packets + after handshake completion. */ + ngtcp2_cid odcid; + ngtcp2_pktns *in_pktns; + ngtcp2_pktns *hs_pktns; + ngtcp2_pktns pktns; + + struct { + /* current is the current destination connection ID. */ + ngtcp2_dcid current; + /* unused is a set of unused CID received from peer. */ + ngtcp2_ringbuf unused; + /* retired is a set of CID retired by local endpoint. Keep them + in 3*PTO to catch packets in flight along the old path. */ + ngtcp2_ringbuf retired; + /* retire_prior_to is the larget retire_prior_to received so + far. */ + uint64_t retire_prior_to; + /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames + queued for transmission. */ + size_t num_retire_queued; + } dcid; + + struct { + /* set is a set of CID sent to peer. The peer can use any CIDs in + this set. This includes used CID as well as unused ones. */ + ngtcp2_ksl set; + /* used is a set of CID used by peer. The sort function of this + priority queue takes timestamp when CID is retired and sorts + them in ascending order. */ + ngtcp2_pq used; + /* last_seq is the last sequence number of connection ID. */ + uint64_t last_seq; + /* num_retired is the number of retired Connection ID still + included in set. */ + size_t num_retired; + } scid; + + struct { + /* strmq contains ngtcp2_strm which has frames to send. */ + ngtcp2_pq strmq; + /* ack is ACK frame. The underlying buffer is resused. */ + ngtcp2_frame *ack; + /* max_ack_blks is the number of additional ngtcp2_ack_blk which + ack can contain. */ + size_t max_ack_blks; + /* offset is the offset the local endpoint has sent to the remote + endpoint. */ + uint64_t offset; + /* max_offset is the maximum offset that local endpoint can + send. */ + uint64_t max_offset; + } tx; + + struct { + /* unsent_max_offset is the maximum offset that remote endpoint + can send without extending MAX_DATA. This limit is not yet + notified to the remote endpoint. */ + uint64_t unsent_max_offset; + /* offset is the cumulative sum of stream data received for this + connection. */ + uint64_t offset; + /* max_offset is the maximum offset that remote endpoint can + send. */ + uint64_t max_offset; + /* path_challenge stores received PATH_CHALLENGE data. */ + ngtcp2_ringbuf path_challenge; + /* ccec is the received connection close error code. */ + ngtcp2_connection_close_error_code ccec; + } rx; + + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_vec *hp_key; + } early; + + struct { + ngtcp2_settings settings; + struct { + /* max_streams is the maximum number of bidirectional streams which + the local endpoint can open. */ + uint64_t max_streams; + /* next_stream_id is the bidirectional stream ID which the local + endpoint opens next. */ + int64_t next_stream_id; + } bidi; + + struct { + /* max_streams is the maximum number of unidirectional streams + which the local endpoint can open. */ + uint64_t max_streams; + /* next_stream_id is the unidirectional stream ID which the + local endpoint opens next. */ + int64_t next_stream_id; + } uni; + } local; + + struct { + /* transport_params is the received transport parameters during + handshake. It is used for Short packet only. */ + ngtcp2_transport_params transport_params; + /* pending_transport_params is received transport parameters + during handshake. It is copied to transport_params when 1RTT + key is available. */ + ngtcp2_transport_params pending_transport_params; + struct { + ngtcp2_idtr idtr; + /* unsent_max_streams is the maximum number of streams of peer + initiated bidirectional stream which the local endpoint can + accept. This limit is not yet notified to the remote + endpoint. */ + uint64_t unsent_max_streams; + /* max_streams is the maximum number of streams of peer + initiated bidirectional stream which the local endpoint can + accept. */ + uint64_t max_streams; + } bidi; + + struct { + ngtcp2_idtr idtr; + /* unsent_max_streams is the maximum number of streams of peer + initiated unidirectional stream which the local endpoint can + accept. This limit is not yet notified to the remote + endpoint. */ + uint64_t unsent_max_streams; + /* max_streams is the maximum number of streams of peer + initiated unidirectional stream which the local endpoint can + accept. */ + uint64_t max_streams; + } uni; + } remote; + + struct { + struct { + /* new_tx_ckm is a new sender 1RTT key which has not been + used. */ + ngtcp2_crypto_km *new_tx_ckm; + /* new_rx_ckm is a new receiver 1RTT key which has not + successfully decrypted incoming packet yet. */ + ngtcp2_crypto_km *new_rx_ckm; + /* old_rx_ckm is an old receiver 1RTT key. */ + ngtcp2_crypto_km *old_rx_ckm; + /* confirmed_ts is the time instant when the key update is + confirmed by the local endpoint last time. UINT64_MAX means + undefined value. */ + ngtcp2_tstamp confirmed_ts; + } key_update; + + size_t aead_overhead; + /* decrypt_buf is a buffer which is used to write decrypted data. */ + ngtcp2_vec decrypt_buf; + /* retry_aead is AEAD to verify Retry packet integrity. */ + ngtcp2_crypto_aead retry_aead; + /* tls_error is TLS related error. */ + int tls_error; + } crypto; + + /* pkt contains the packet intermediate construction data to support + NGTCP2_WRITE_STREAM_FLAG_MORE */ + struct { + ngtcp2_crypto_cc cc; + ngtcp2_pkt_hd hd; + ngtcp2_ppe ppe; + ngtcp2_frame_chain **pfrc; + int pkt_empty; + int hd_logged; + uint8_t rtb_entry_flags; + int was_client_initial; + ngtcp2_ssize hs_spktlen; + } pkt; + + ngtcp2_map strms; + ngtcp2_rcvry_stat rcs; + ngtcp2_cc_stat ccs; + ngtcp2_pv *pv; + ngtcp2_log log; + ngtcp2_qlog qlog; + ngtcp2_rst rst; + ngtcp2_default_cc cc; + /* token is an address validation token received from server. */ + ngtcp2_buf token; + /* hs_recved is the number of bytes received from client before its + address is validated. This field is only used by server to + ensure "3 times received data" rule. */ + size_t hs_recved; + /* hs_sent is the number of bytes sent from server during handshake. + This field is only used by server to ensure "3 times received + data" rule. */ + size_t hs_sent; + const ngtcp2_mem *mem; + /* idle_ts is the time instant when idle timer started. */ + ngtcp2_tstamp idle_ts; + void *user_data; + uint32_t version; + /* flags is bitwise OR of zero or more of ngtcp2_conn_flag. */ + uint16_t flags; + int server; +}; + +/** + * @function + * + * `ngtcp2_conn_read_handshake` performs QUIC cryptographic handshake + * by reading given data. |pkt| points to the buffer to read and + * |pktlen| is the length of the buffer. |path| is the network path. + * + * The application should call `ngtcp2_conn_write_handshake` (or + * `ngtcp2_conn_client_write_handshake` for client session) to make + * handshake go forward after calling this function. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: (TBD). + */ +int ngtcp2_conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_handshake` performs QUIC cryptographic handshake + * by writing handshake packets. It may write a packet in the given + * buffer pointed by |dest| whose capacity is given as |destlen|. + * Application must ensure that the buffer pointed by |dest| is not + * empty. + * + * Application should keep calling this function repeatedly until it + * returns zero, or negative error code. + * + * Application should call this function until + * `ngtcp2_conn_get_handshake_completed` returns nonzero. After the + * completion of handshake, `ngtcp2_conn_read_pkt` and + * `ngtcp2_conn_write_pkt` should be called instead. + * + * During handshake, application can send 0-RTT data (or its response) + * using `ngtcp2_conn_write_stream`. + * `ngtcp2_conn_client_write_handshake` is generally efficient because + * it can coalesce Handshake packet and 0-RTT packet into one UDP + * packet. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +ngtcp2_ssize ngtcp2_conn_write_handshake(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_client_write_handshake` is just like + * `ngtcp2_conn_write_handshake`, but it is for client only, and can + * write 0-RTT data. This function can coalesce handshake packet and + * 0-RTT packet into single UDP packet, thus it is generally more + * efficient than the combination of `ngtcp2_conn_write_handshake` and + * `ngtcp2_conn_write_stream`. + * + * |stream_id|, |fin|, |datav|, and |datavcnt| are stream identifier + * to which 0-RTT data is sent, whether it is a last data chunk in + * this stream, a vector of 0-RTT data, and its number of elements + * respectively. If there is no 0RTT data to send, pass negative + * integer to |stream_id|. The amount of 0RTT data sent is assigned + * to |*pdatalen|. If no data is sent, -1 is assigned. Note that 0 + * length STREAM frame is allowed in QUIC, so 0 might be assigned to + * |*pdatalen|. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +ngtcp2_ssize ngtcp2_conn_client_write_handshake( + ngtcp2_conn *conn, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, int fin, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its + * reception timestamp |ts| in order to send its ACK. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_PROTO + * Same packet number has already been added. + */ +int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, + int64_t pkt_num, int active_ack, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_find_stream returns a stream whose stream ID is + * |stream_id|. If no such stream is found, it returns NULL. + */ +ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id); + +/* + * conn_init_stream initializes |strm|. Its stream ID is |stream_id|. + * This function adds |strm| to conn->strms. |strm| must be allocated + * by the caller. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-callback function failed. + */ +int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + int64_t stream_id, void *stream_user_data); + +/* + * ngtcp2_conn_close_stream closes stream |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Stream is not found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code); + +/* + * ngtcp2_conn_close_stream closes stream |strm| if no further + * transmission and reception are allowed, and all reordered incoming + * data are emitted to the application, and the transmitted data are + * acked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Stream is not found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code); + +/* + * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest + * RTT which is not adjusted by ack delay. |ack_delay| is unscaled + * ack_delay included in ACK frame. |ack_delay| is actually tainted + * (sent by peer), so don't assume that |ack_delay| is always smaller + * than, or equals to |rtt|. + */ +void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay); + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_detect_lost_pkt detects lost packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the + * top of queue. tx_strmq must not be empty. + */ +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue. + * tx_strmq must not be empty. + */ +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); + +/* + * ngtcp2_conn_internal_expiry returns the minimum expiry time among + * all timers in |conn|. + */ +ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| + * frame only in the buffer pointed by |dest| whose length if + * |destlen|. |type| is a long packet type to send. If |type| is 0, + * Short packet is used. |dcid| is used as a destination connection + * ID. + * + * The packet written by this function will not be retransmitted. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +ngtcp2_ssize +ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest, + size_t destlen, uint8_t type, + const ngtcp2_cid *dcid, ngtcp2_frame *fr, + uint8_t rtb_flags, ngtcp2_tstamp ts); + +#endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/lib/ngtcp2_conv.c new file mode 100644 index 00000000000000..7811c84d0bc19a --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conv.c @@ -0,0 +1,251 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conv.h" + +#include +#include + +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" + +uint64_t ngtcp2_get_uint64(const uint8_t *p) { + uint64_t n; + memcpy(&n, p, 8); + return ngtcp2_ntohl64(n); +} + +uint64_t ngtcp2_get_uint48(const uint8_t *p) { + uint64_t n = 0; + memcpy(((uint8_t *)&n) + 2, p, 6); + return ngtcp2_ntohl64(n); +} + +uint32_t ngtcp2_get_uint32(const uint8_t *p) { + uint32_t n; + memcpy(&n, p, 4); + return ntohl(n); +} + +uint32_t ngtcp2_get_uint24(const uint8_t *p) { + uint32_t n = 0; + memcpy(((uint8_t *)&n) + 1, p, 3); + return ntohl(n); +} + +uint16_t ngtcp2_get_uint16(const uint8_t *p) { + uint16_t n; + memcpy(&n, p, 2); + return ntohs(n); +} + +uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p) { + union { + char b[8]; + uint16_t n16; + uint32_t n32; + uint64_t n64; + } n; + + *plen = 1u << (*p >> 6); + + switch (*plen) { + case 1: + return *p; + case 2: + memcpy(&n, p, 2); + n.b[0] &= 0x3f; + return ntohs(n.n16); + case 4: + memcpy(&n, p, 4); + n.b[0] &= 0x3f; + return ntohl(n.n32); + case 8: + memcpy(&n, p, 8); + n.b[0] &= 0x3f; + return ngtcp2_ntohl64(n.n64); + } + + assert(0); +} + +int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) { + switch (pkt_numlen) { + case 1: + return *p; + case 2: + return (int64_t)ngtcp2_get_uint16(p); + case 3: + return (int64_t)ngtcp2_get_uint24(p); + case 4: + return (int64_t)ngtcp2_get_uint32(p); + default: + assert(0); + } +} + +uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) { + n = ngtcp2_htonl64(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n) { + n = ngtcp2_htonl64(n); + return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 2, 6); +} + +uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n) { + n = htonl(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) { + n = htonl(n); + return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) { + n = htons(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { + uint8_t *rv; + if (n < 64) { + *p++ = (uint8_t)n; + return p; + } + if (n < 16384) { + rv = ngtcp2_put_uint16be(p, (uint16_t)n); + *p |= 0x40; + return rv; + } + if (n < 1073741824) { + rv = ngtcp2_put_uint32be(p, (uint32_t)n); + *p |= 0x80; + return rv; + } + assert(n < 4611686018427387904ULL); + rv = ngtcp2_put_uint64be(p, n); + *p |= 0xc0; + return rv; +} + +uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { + uint8_t *rv; + + assert(n < 16384); + + rv = ngtcp2_put_uint16be(p, n); + *p |= 0x40; + + return rv; +} + +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) { + switch (len) { + case 1: + *p++ = (uint8_t)pkt_num; + return p; + case 2: + ngtcp2_put_uint16be(p, (uint16_t)pkt_num); + return p + 2; + case 3: + ngtcp2_put_uint24be(p, (uint32_t)pkt_num); + return p + 3; + case 4: + ngtcp2_put_uint32be(p, (uint32_t)pkt_num); + return p + 4; + default: + assert(0); + } +} + +size_t ngtcp2_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); } + +size_t ngtcp2_put_varint_len(uint64_t n) { + if (n < 64) { + return 1; + } + if (n < 16384) { + return 2; + } + if (n < 1073741824) { + return 4; + } + assert(n < 4611686018427387904ULL); + return 8; +} + +int64_t ngtcp2_nth_server_bidi_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_SERVER_STREAM_ID_BIDI; + } + + return (int64_t)(((n - 1) << 2) | 0x01); +} + +int64_t ngtcp2_nth_client_bidi_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_CLIENT_STREAM_ID_BIDI; + } + + return (int64_t)((n - 1) << 2); +} + +int64_t ngtcp2_nth_server_uni_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_SERVER_STREAM_ID_UNI; + } + + return (int64_t)(((n - 1) << 2) | 0x03); +} + +int64_t ngtcp2_nth_client_uni_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_CLIENT_STREAM_ID_UNI; + } + + return (int64_t)(((n - 1) << 2) | 0x02); +} + +uint64_t ngtcp2_ord_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2) + 1; +} diff --git a/deps/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/lib/ngtcp2_conv.h new file mode 100644 index 00000000000000..f3336f44a92dad --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_conv.h @@ -0,0 +1,261 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONV_H +#define NGTCP2_CONV_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ENDIAN_H +# include +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include +#endif /* HAVE_SYS_ENDIAN_H */ + +#include + +#if defined HAVE_BE64TOH || HAVE_DECL_BE64TOH +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +/* + * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint64_t ngtcp2_get_uint64(const uint8_t *p); + +/* + * ngtcp2_get_uint48 reads 6 bytes from |p| as 48 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint64_t ngtcp2_get_uint48(const uint8_t *p); + +/* + * ngtcp2_get_uint32 reads 4 bytes from |p| as 32 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint32_t ngtcp2_get_uint32(const uint8_t *p); + +/* + * ngtcp2_get_uint24 reads 3 bytes from |p| as 24 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint32_t ngtcp2_get_uint24(const uint8_t *p); + +/* + * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint16_t ngtcp2_get_uint16(const uint8_t *p); + +/* + * ngtcp2_get_varint reads variable-length integer from |p|, and + * returns it in host byte order. The number of bytes read is stored + * in |*plen|. + */ +uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p); + +/* + * ngtcp2_get_pkt_num reads encoded packet number from |p|. The + * packet number is encoed in |pkt_numlen| bytes. + */ +int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen); + +/* + * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_uint48be writes |n| in host byte order in |p| in network + * byte order. It writes only least significant 48 bits. It returns + * the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_uint32be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n); + +/* + * ngtcp2_put_uint24be writes |n| in host byte order in |p| in network + * byte order. It writes only least significant 24 bits. It returns + * the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n); + +/* + * ngtcp2_put_uint16be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); + +/* + * ngtcp2_put_varint writes |n| in |p| using variable-length integer + * encoding. It returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer + * encoding. |n| must be strictly less than 16384. The function + * always encodes |n| in 2 bytes. It returns the one beyond of the + * last written position. + */ +uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); + +/* + * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It + * returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len); + +/* + * ngtcp2_get_varint_len returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t ngtcp2_get_varint_len(const uint8_t *p); + +/* + * ngtcp2_put_varint_len returns the required number of bytes to + * encode |n|. + */ +size_t ngtcp2_put_varint_len(uint64_t n); + +/* + * ngtcp2_nth_server_bidi_id returns |n|-th server bidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_SERVER_STREAM_ID_BIDI, this function returns + * NGTCP2_MAX_SERVER_STREAM_ID_BIDI. + */ +int64_t ngtcp2_nth_server_bidi_id(uint64_t n); + +/* + * ngtcp2_nth_client_bidi_id returns |n|-th client bidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_CLIENT_STREAM_ID_BIDI, this function returns + * NGTCP2_MAX_CLIENT_STREAM_ID_BIDI. + */ +int64_t ngtcp2_nth_client_bidi_id(uint64_t n); + +/* + * ngtcp2_nth_server_uni_id returns |n|-th server unidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_SERVER_STREAM_ID_UNI, this function returns + * NGTCP2_MAX_SERVER_STREAM_ID_UNI. + */ +int64_t ngtcp2_nth_server_uni_id(uint64_t n); + +/* + * ngtcp2_nth_client_uni_id returns |n|-th client unidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_CLIENT_STREAM_ID_UNI, this function returns + * NGTCP2_MAX_CLIENT_STREAM_ID_UNI. + */ +int64_t ngtcp2_nth_client_uni_id(uint64_t n); + +/* + * ngtcp2_ord_stream_id returns the ordinal number of |stream_id|. + */ +uint64_t ngtcp2_ord_stream_id(int64_t stream_id); + +#endif /* NGTCP2_CONV_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/lib/ngtcp2_crypto.c new file mode 100644 index 00000000000000..dd65ff7e3039fb --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_crypto.c @@ -0,0 +1,649 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_crypto.h" + +#include +#include + +#include "ngtcp2_str.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_conn.h" + +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, + size_t secretlen, const uint8_t *key, size_t keylen, + const uint8_t *iv, size_t ivlen, + const ngtcp2_mem *mem) { + int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, keylen, ivlen, mem); + if (rv != 0) { + return rv; + } + + if (secretlen) { + memcpy((*pckm)->secret.base, secret, secretlen); + } + memcpy((*pckm)->key.base, key, keylen); + memcpy((*pckm)->iv.base, iv, ivlen); + + return 0; +} + +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, + size_t keylen, size_t ivlen, + const ngtcp2_mem *mem) { + size_t len; + uint8_t *p; + + len = sizeof(ngtcp2_crypto_km) + secretlen + keylen + ivlen; + + *pckm = ngtcp2_mem_malloc(mem, len); + if (*pckm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km); + (*pckm)->secret.base = p; + (*pckm)->secret.len = secretlen; + p += secretlen; + (*pckm)->key.base = p; + (*pckm)->key.len = keylen; + p += keylen; + (*pckm)->iv.base = p; + (*pckm)->iv.len = ivlen; + (*pckm)->pkt_num = -1; + (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + + return 0; +} + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) { + if (ckm == NULL) { + return; + } + + ngtcp2_mem_free(mem, ckm); +} + +void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, + int64_t pkt_num) { + size_t i; + uint64_t n; + + memcpy(dest, iv, ivlen); + n = ngtcp2_htonl64((uint64_t)pkt_num); + + for (i = 0; i < 8; ++i) { + dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i]; + } +} + +/* + * varint_paramlen returns the length of a single transport parameter + * which has variable integer in its parameter. + */ +static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) { + size_t valuelen = ngtcp2_put_varint_len(param); + return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(valuelen) + valuelen; +} + +/* + * write_varint_param writes parameter |id| of the given |value| in + * varint encoding. It returns p + the number of bytes written. + */ +static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id, + uint64_t value) { + p = ngtcp2_put_varint(p, id); + p = ngtcp2_put_varint(p, ngtcp2_put_varint_len(value)); + return ngtcp2_put_varint(p, value); +} + +ngtcp2_ssize +ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, + ngtcp2_transport_params_type exttype, + const ngtcp2_transport_params *params) { + uint8_t *p; + size_t len = 0; + /* For some reason, gcc 7.3.0 requires this initialization. */ + size_t preferred_addrlen = 0; + + switch (exttype) { + case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO: + break; + case NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS: + if (params->stateless_reset_token_present) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + + ngtcp2_put_varint_len(NGTCP2_STATELESS_RESET_TOKENLEN) + + NGTCP2_STATELESS_RESET_TOKENLEN; + } + if (params->preferred_address_present) { + assert(params->preferred_address.cid.datalen == 0 || + params->preferred_address.cid.datalen >= NGTCP2_MIN_CIDLEN); + assert(params->preferred_address.cid.datalen <= NGTCP2_MAX_CIDLEN); + preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ + + 16 /* ipv6Address */ + 2 /* ipv6Port */ + + 1 + + params->preferred_address.cid.datalen /* CID */ + + NGTCP2_STATELESS_RESET_TOKENLEN; + len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + + ngtcp2_put_varint_len(preferred_addrlen) + preferred_addrlen; + } + if (params->original_connection_id_present) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID) + + ngtcp2_put_varint_len(params->original_connection_id.datalen) + + params->original_connection_id.datalen; + } + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (params->initial_max_stream_data_bidi_local) { + len += varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params->initial_max_stream_data_bidi_local); + } + if (params->initial_max_stream_data_bidi_remote) { + len += varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params->initial_max_stream_data_bidi_remote); + } + if (params->initial_max_stream_data_uni) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params->initial_max_stream_data_uni); + } + if (params->initial_max_data) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params->initial_max_data); + } + if (params->initial_max_streams_bidi) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params->initial_max_streams_bidi); + } + if (params->initial_max_streams_uni) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params->initial_max_streams_uni); + } + if (params->max_packet_size != NGTCP2_MAX_PKT_SIZE) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE, + params->max_packet_size); + } + if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params->ack_delay_exponent); + } + if (params->disable_active_migration) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + + ngtcp2_put_varint_len(0); + } + if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params->max_ack_delay / NGTCP2_MILLISECONDS); + } + if (params->max_idle_timeout) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + } + if (params->active_connection_id_limit && + params->active_connection_id_limit != + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + } + + if (destlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = dest; + + if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + if (params->stateless_reset_token_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN); + p = ngtcp2_put_varint(p, sizeof(params->stateless_reset_token)); + p = ngtcp2_cpymem(p, params->stateless_reset_token, + sizeof(params->stateless_reset_token)); + } + if (params->preferred_address_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); + p = ngtcp2_put_varint(p, preferred_addrlen); + + p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + + p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + + *p++ = (uint8_t)params->preferred_address.cid.datalen; + if (params->preferred_address.cid.datalen) { + p = ngtcp2_cpymem(p, params->preferred_address.cid.data, + params->preferred_address.cid.datalen); + } + p = ngtcp2_cpymem( + p, params->preferred_address.stateless_reset_token, + sizeof(params->preferred_address.stateless_reset_token)); + } + if (params->original_connection_id_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID); + p = ngtcp2_put_varint(p, params->original_connection_id.datalen); + p = ngtcp2_cpymem(p, params->original_connection_id.data, + params->original_connection_id.datalen); + } + } + + if (params->initial_max_stream_data_bidi_local) { + p = write_varint_param( + p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params->initial_max_stream_data_bidi_local); + } + + if (params->initial_max_stream_data_bidi_remote) { + p = write_varint_param( + p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params->initial_max_stream_data_bidi_remote); + } + + if (params->initial_max_stream_data_uni) { + p = write_varint_param(p, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params->initial_max_stream_data_uni); + } + + if (params->initial_max_data) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params->initial_max_data); + } + + if (params->initial_max_streams_bidi) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params->initial_max_streams_bidi); + } + + if (params->initial_max_streams_uni) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params->initial_max_streams_uni); + } + + if (params->max_packet_size != NGTCP2_MAX_PKT_SIZE) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE, + params->max_packet_size); + } + + if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params->ack_delay_exponent); + } + + if (params->disable_active_migration) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); + p = ngtcp2_put_varint(p, 0); + } + + if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params->max_ack_delay / NGTCP2_MILLISECONDS); + } + + if (params->max_idle_timeout) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + } + + if (params->active_connection_id_limit && + params->active_connection_id_limit != + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + } + + assert((size_t)(p - dest) == len); + + return (ngtcp2_ssize)len; +} + +/* + * decode_varint decodes a single varint from the buffer pointed by + * |p| of length |end - p|. If it decodes an integer successfully, it + * stores the integer in |*pdest| and returns 0. Otherwise it returns + * -1. + */ +static ngtcp2_ssize decode_varint(uint64_t *pdest, const uint8_t *p, + const uint8_t *end) { + size_t len; + + if (p == end) { + return -1; + } + + len = ngtcp2_get_varint_len(p); + if ((uint64_t)(end - p) < len) { + return -1; + } + + *pdest = ngtcp2_get_varint(&len, p); + + return (ngtcp2_ssize)len; +} + +/* + * decode_varint_param decodes length prefixed value from the buffer + * pointed by |p| of length |end - p|. The length and value are + * encoded in varint form. If it decodes a value successfully, it + * stores the value in |*pdest| and returns 0. Otherwise it returns + * -1. + */ +static ngtcp2_ssize decode_varint_param(uint64_t *pdest, const uint8_t *p, + const uint8_t *end) { + const uint8_t *begin = p; + ngtcp2_ssize nread; + uint64_t valuelen; + size_t n; + + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return -1; + } + + p += nread; + + if ((uint64_t)(end - p) < valuelen) { + return -1; + } + + if (ngtcp2_get_varint_len(p) != valuelen) { + return -1; + } + + *pdest = ngtcp2_get_varint(&n, p); + + p += valuelen; + + return (ngtcp2_ssize)(p - begin); +} + +int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen) { + const uint8_t *p, *end; + size_t len; + uint64_t param_type; + uint64_t valuelen; + ngtcp2_ssize nread; + uint8_t scb[8192]; + size_t scb_idx; + size_t scb_shift; + + p = data; + end = data + datalen; + + /* Set default values */ + params->initial_max_streams_bidi = 0; + params->initial_max_streams_uni = 0; + params->initial_max_stream_data_bidi_local = 0; + params->initial_max_stream_data_bidi_remote = 0; + params->initial_max_stream_data_uni = 0; + params->max_packet_size = NGTCP2_MAX_PKT_SIZE; + params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params->stateless_reset_token_present = 0; + params->preferred_address_present = 0; + memset(¶ms->preferred_address, 0, sizeof(params->preferred_address)); + params->disable_active_migration = 0; + params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params->max_idle_timeout = 0; + params->active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params->original_connection_id_present = 0; + + if (datalen == 0) { + return 0; + } + + memset(scb, 0, sizeof(scb)); + + for (; (size_t)(end - p) >= 2;) { + nread = decode_varint(¶m_type, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + + scb_idx = param_type / 8; + scb_shift = param_type % 8; + + if (scb[scb_idx] & (1 << scb_shift)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + scb[scb_idx] |= (uint8_t)(1 << scb_shift); + switch (param_type) { + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: + nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_local, + p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: + nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_remote, + p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI: + nread = decode_varint_param(¶ms->initial_max_stream_data_uni, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA: + nread = decode_varint_param(¶ms->initial_max_data, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI: + nread = decode_varint_param(¶ms->initial_max_streams_bidi, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_STREAM_LIMIT; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI: + nread = decode_varint_param(¶ms->initial_max_streams_uni, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_STREAM_LIMIT; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: + nread = decode_varint_param(¶ms->max_idle_timeout, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->max_idle_timeout *= NGTCP2_MILLISECONDS; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_PACKET_SIZE: + nread = decode_varint_param(¶ms->max_packet_size, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)valuelen != sizeof(params->stateless_reset_token)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + memcpy(params->stateless_reset_token, p, + sizeof(params->stateless_reset_token)); + params->stateless_reset_token_present = 1; + + p += sizeof(params->stateless_reset_token); + break; + case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT: + nread = decode_varint_param(¶ms->ack_delay_exponent, p, end); + if (nread < 0 || params->ack_delay_exponent > 20) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ + + 2 /* ipv6Port */ + + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN; + if (valuelen < len) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + memcpy(params->preferred_address.ipv4_addr, p, + sizeof(params->preferred_address.ipv4_addr)); + p += sizeof(params->preferred_address.ipv4_addr); + params->preferred_address.ipv4_port = ngtcp2_get_uint16(p); + p += sizeof(uint16_t); + + memcpy(params->preferred_address.ipv6_addr, p, + sizeof(params->preferred_address.ipv6_addr)); + p += sizeof(params->preferred_address.ipv6_addr); + params->preferred_address.ipv6_port = ngtcp2_get_uint16(p); + p += sizeof(uint16_t); + + /* cid */ + params->preferred_address.cid.datalen = *p++; + len += params->preferred_address.cid.datalen; + if (valuelen != len || + params->preferred_address.cid.datalen > NGTCP2_MAX_CIDLEN || + (params->preferred_address.cid.datalen != 0 && + params->preferred_address.cid.datalen < NGTCP2_MIN_CIDLEN)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->preferred_address.cid.datalen) { + memcpy(params->preferred_address.cid.data, p, + params->preferred_address.cid.datalen); + p += params->preferred_address.cid.datalen; + } + + /* stateless reset token */ + memcpy(params->preferred_address.stateless_reset_token, p, + sizeof(params->preferred_address.stateless_reset_token)); + p += sizeof(params->preferred_address.stateless_reset_token); + params->preferred_address_present = 1; + break; + case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION: + nread = decode_varint(&valuelen, p, end); + if (nread < 0 || valuelen != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + params->disable_active_migration = 1; + break; + case NGTCP2_TRANSPORT_PARAM_ORIGINAL_CONNECTION_ID: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if (valuelen < NGTCP2_MIN_CIDLEN || valuelen > NGTCP2_MAX_CIDLEN || + (size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + ngtcp2_cid_init(¶ms->original_connection_id, p, valuelen); + params->original_connection_id_present = 1; + p += valuelen; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY: + nread = decode_varint_param(¶ms->max_ack_delay, p, end); + if (nread < 0 || params->max_ack_delay >= 16384) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->max_ack_delay *= NGTCP2_MILLISECONDS; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT: + nread = decode_varint_param(¶ms->active_connection_id_limit, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + default: + /* Ignore unknown parameter */ + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += valuelen; + break; + } + } + + if (end - p != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + return 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/lib/ngtcp2_crypto.h new file mode 100644 index 00000000000000..8dc1ef9f39d344 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_crypto.h @@ -0,0 +1,102 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_H +#define NGTCP2_CRYPTO_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" + +/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial + packets. Because QUIC uses AEAD_AES_128_GCM, the overhead is 16 + bytes. */ +#define NGTCP2_INITIAL_AEAD_OVERHEAD 16 + +/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ +#define NGTCP2_MAX_AEAD_OVERHEAD 16 + +typedef enum { + NGTCP2_CRYPTO_KM_FLAG_NONE, + /* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is + set. */ + NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE = 0x01, +} ngtcp2_crypto_km_flag; + +typedef struct { + ngtcp2_vec secret; + ngtcp2_vec key; + ngtcp2_vec iv; + /* pkt_num is a packet number of a packet which uses this keying + material. For encryption key, it is the lowest packet number of + a packet. For decryption key, it is the lowest packet number of + a packet which can be decrypted with this keying material. */ + int64_t pkt_num; + /* flags is the bitwise OR of zero or more of + ngtcp2_crypto_km_flag. */ + uint8_t flags; +} ngtcp2_crypto_km; + +/* + * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object and + * assigns its pointer to |*pckm|. The |secret| of length + * |secretlen|, the |key| of length |keylen| and the |iv| of length + * |ivlen| are copied to |*pckm|. If |secretlen| == 0, the function + * assumes no secret is given which is acceptable. The sole reason to + * store secret is update keys. Only 1RTT key can be updated. + */ +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, + size_t secretlen, const uint8_t *key, size_t keylen, + const uint8_t *iv, size_t ivlen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but + * it does not copy secret, key and IV. + */ +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, + size_t keylen, size_t ivlen, + const ngtcp2_mem *mem); + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem); + +typedef struct { + ngtcp2_crypto_aead aead; + ngtcp2_crypto_cipher hp; + const ngtcp2_crypto_km *ckm; + const ngtcp2_vec *hp_key; + size_t aead_overhead; + ngtcp2_encrypt encrypt; + ngtcp2_decrypt decrypt; + ngtcp2_hp_mask hp_mask; +} ngtcp2_crypto_cc; + +void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, + int64_t pkt_num); + +#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/lib/ngtcp2_err.c new file mode 100644 index 00000000000000..ae2edb35ee428f --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_err.c @@ -0,0 +1,136 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_err.h" + +const char *ngtcp2_strerror(int liberr) { + switch (liberr) { + case 0: + return "NO_ERROR"; + case NGTCP2_ERR_INVALID_ARGUMENT: + return "ERR_INVALID_ARGUMENT"; + case NGTCP2_ERR_UNKNOWN_PKT_TYPE: + return "ERR_UNKNOWN_PKT_TYPE"; + case NGTCP2_ERR_NOBUF: + return "ERR_NOBUF"; + case NGTCP2_ERR_PROTO: + return "ERR_PROTO"; + case NGTCP2_ERR_INVALID_STATE: + return "ERR_INVALID_STATE"; + case NGTCP2_ERR_ACK_FRAME: + return "ERR_ACK_FRAME"; + case NGTCP2_ERR_STREAM_ID_BLOCKED: + return "ERR_STREAM_ID_BLOCKED"; + case NGTCP2_ERR_STREAM_IN_USE: + return "ERR_STREAM_IN_USE"; + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + return "ERR_STREAM_DATA_BLOCKED"; + case NGTCP2_ERR_FLOW_CONTROL: + return "ERR_FLOW_CONTROL"; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return "ERR_CONNECTION_ID_LIMIT"; + case NGTCP2_ERR_STREAM_LIMIT: + return "ERR_STREAM_LIMIT"; + case NGTCP2_ERR_FINAL_SIZE: + return "ERR_FINAL_SIZE"; + case NGTCP2_ERR_CRYPTO: + return "ERR_CRYPTO"; + case NGTCP2_ERR_PKT_NUM_EXHAUSTED: + return "ERR_PKT_NUM_EXHAUSTED"; + case NGTCP2_ERR_NOMEM: + return "ERR_NOMEM"; + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + return "ERR_REQUIRED_TRANSPORT_PARAM"; + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + return "ERR_MALFORMED_TRANSPORT_PARAM"; + case NGTCP2_ERR_FRAME_ENCODING: + return "ERR_FRAME_ENCODING"; + case NGTCP2_ERR_TLS_DECRYPT: + return "ERR_TLS_DECRYPT"; + case NGTCP2_ERR_STREAM_SHUT_WR: + return "ERR_STREAM_SHUT_WR"; + case NGTCP2_ERR_STREAM_NOT_FOUND: + return "ERR_STREAM_NOT_FOUND"; + case NGTCP2_ERR_STREAM_STATE: + return "ERR_STREAM_STATE"; + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + return "ERR_RECV_VERSION_NEGOTIATION"; + case NGTCP2_ERR_CLOSING: + return "ERR_CLOSING"; + case NGTCP2_ERR_DRAINING: + return "ERR_DRAINING"; + case NGTCP2_ERR_TRANSPORT_PARAM: + return "ERR_TRANSPORT_PARAM"; + case NGTCP2_ERR_DISCARD_PKT: + return "ERR_DISCARD_PKT"; + case NGTCP2_ERR_PATH_VALIDATION_FAILED: + return "ERR_PATH_VALIDATION_FAILED"; + case NGTCP2_ERR_CONN_ID_BLOCKED: + return "ERR_CONN_ID_BLOCKED"; + case NGTCP2_ERR_CALLBACK_FAILURE: + return "ERR_CALLBACK_FAILURE"; + case NGTCP2_ERR_INTERNAL: + return "ERR_INTERNAL"; + case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: + return "ERR_CRYPTO_BUFFER_EXCEEDED"; + case NGTCP2_ERR_WRITE_STREAM_MORE: + return "ERR_WRITE_STREAM_MORE"; + case NGTCP2_ERR_RETRY: + return "ERR_RETRY"; + default: + return "(unknown)"; + } +} + +int ngtcp2_err_is_fatal(int liberr) { return liberr < NGTCP2_ERR_FATAL; } + +uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { + switch (liberr) { + case 0: + return NGTCP2_NO_ERROR; + case NGTCP2_ERR_ACK_FRAME: + case NGTCP2_ERR_FRAME_ENCODING: + return NGTCP2_FRAME_ENCODING_ERROR; + case NGTCP2_ERR_FLOW_CONTROL: + return NGTCP2_FLOW_CONTROL_ERROR; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return NGTCP2_CONNECTION_ID_LIMIT_ERROR; + case NGTCP2_ERR_STREAM_LIMIT: + return NGTCP2_STREAM_LIMIT_ERROR; + case NGTCP2_ERR_FINAL_SIZE: + return NGTCP2_FINAL_SIZE_ERROR; + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + return NGTCP2_TRANSPORT_PARAMETER_ERROR; + case NGTCP2_ERR_INVALID_ARGUMENT: + return NGTCP2_INTERNAL_ERROR; + case NGTCP2_ERR_STREAM_STATE: + return NGTCP2_STREAM_STATE_ERROR; + case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: + return NGTCP2_CRYPTO_BUFFER_EXCEEDED; + default: + return NGTCP2_PROTOCOL_VIOLATION; + } +} diff --git a/deps/ngtcp2/lib/ngtcp2_err.h b/deps/ngtcp2/lib/ngtcp2_err.h new file mode 100644 index 00000000000000..9229f5425a63cf --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_err.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ERR_H +#define NGTCP2_ERR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#endif /* NGTCP2_ERR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/lib/ngtcp2_gaptr.c new file mode 100644 index 00000000000000..4cbd898a292dc1 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_gaptr.c @@ -0,0 +1,127 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_gaptr.h" +#include "ngtcp2_range.h" + +#include + +int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + int rv; + ngtcp2_range range = {0, UINT64_MAX}; + ngtcp2_ksl_key key; + + rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, ngtcp2_ksl_key_ptr(&key, &range), + NULL); + if (rv != 0) { + ngtcp2_ksl_free(&gaptr->gap); + return rv; + } + + gaptr->mem = mem; + + return 0; +} + +void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { + if (gaptr == NULL) { + return; + } + + ngtcp2_ksl_free(&gaptr->gap); +} + +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { + int rv; + ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key, old_key; + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); + + for (; !ngtcp2_ksl_it_end(&it);) { + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + m = ngtcp2_range_intersect(&q, &k); + if (!ngtcp2_range_len(&m)) { + break; + } + + if (ngtcp2_range_eq(&k, &m)) { + ngtcp2_ksl_remove(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &k)); + continue; + } + ngtcp2_range_cut(&l, &r, &k, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k), + ngtcp2_ksl_key_ptr(&key, &l)); + + if (ngtcp2_range_len(&r)) { + rv = ngtcp2_ksl_insert(&gaptr->gap, &it, ngtcp2_ksl_key_ptr(&key, &r), + NULL); + if (rv != 0) { + return rv; + } + } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_ksl_update_key(&gaptr->gap, ngtcp2_ksl_key_ptr(&old_key, &k), + ngtcp2_ksl_key_ptr(&key, &r)); + } + ngtcp2_ksl_it_next(&it); + } + return 0; +} + +uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + return r.begin; +} + +ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { + ngtcp2_range q = {offset, offset + 1}; + ngtcp2_ksl_key key; + return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, + ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); +} + +int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, + size_t datalen) { + ngtcp2_ksl_key key; + ngtcp2_range q = {offset, offset + datalen}; + ngtcp2_ksl_it it = + ngtcp2_ksl_lower_bound_compar(&gaptr->gap, ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); + ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it).ptr; + ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/lib/ngtcp2_gaptr.h new file mode 100644 index 00000000000000..03181449deaf02 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_gaptr.h @@ -0,0 +1,96 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_GAPTR_H +#define NGTCP2_GAPTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_ksl.h" + +/* + * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct { + /* gap maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + ngtcp2_ksl gap; + /* mem is custom memory allocator */ + const ngtcp2_mem *mem; +} ngtcp2_gaptr; + +/* + * ngtcp2_gaptr_init initializes |gaptr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); + +/* + * ngtcp2_gaptr_free frees resources allocated for |gaptr|. + */ +void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr); + +/* + * ngtcp2_gaptr_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); + +/* + * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); + +/* + * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to + * the first gap which overlaps or comes after |offset|. + */ +ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset); + +/* + * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + + * datalen) is completely pushed into this object. + */ +int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, + size_t datalen); + +#endif /* NGTCP2_GAPTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/lib/ngtcp2_idtr.c new file mode 100644 index 00000000000000..f04806b4a8b22f --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_idtr.c @@ -0,0 +1,86 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_idtr.h" + +#include + +int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { + int rv; + + rv = ngtcp2_gaptr_init(&idtr->gap, mem); + if (rv != 0) { + return rv; + } + + idtr->server = server; + + return 0; +} + +void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { + if (idtr == NULL) { + return; + } + + ngtcp2_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to id space used by + * ngtcp2_idtr. + */ +static uint64_t id_from_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2); +} + +int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + if (ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1)) { + return NGTCP2_ERR_STREAM_IN_USE; + } + + return ngtcp2_gaptr_push(&idtr->gap, q, 1); +} + +int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1); +} + +uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr) { + return ngtcp2_gaptr_first_gap_offset(&idtr->gap); +} diff --git a/deps/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/lib/ngtcp2_idtr.h new file mode 100644 index 00000000000000..63f332e64f9cd0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_idtr.h @@ -0,0 +1,95 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_IDTR_H +#define NGTCP2_IDTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_gaptr.h" + +/* + * ngtcp2_idtr tracks the usage of stream ID. + */ +typedef struct { + /* gap maintains the range of ID which is not used yet. Initially, + its range is [0, UINT64_MAX). */ + ngtcp2_gaptr gap; + /* server is nonzero if this object records server initiated stream + ID. */ + int server; +} ngtcp2_idtr; + +/* + * ngtcp2_idtr_init initializes |idtr|. + * + * If this object records server initiated ID (even number), set + * |server| to nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); + +/* + * ngtcp2_idtr_free frees resources allocated for |idtr|. + */ +void ngtcp2_idtr_free(ngtcp2_idtr *idtr); + +/* + * ngtcp2_idtr_open claims that |stream_id| is in used. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_STREAM_IN_USE + * ID has already been used. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id); + +/* + * ngtcp2_idtr_open tells whether ID |stream_id| is in used or not. + * + * It returns nonzero if |stream_id| is used. + */ +int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id); + +/* + * ngtcp2_idtr_first_gap returns the first id of first gap. If there + * is no gap, it returns UINT64_MAX. The returned id is an id space + * used in this object internally, and not stream ID. + */ +uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr); + +#endif /* NGTCP2_IDTR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/lib/ngtcp2_ksl.c new file mode 100644 index 00000000000000..7936a269127c21 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ksl.c @@ -0,0 +1,749 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ksl.h" + +#include +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" + +static size_t ksl_nodelen(size_t keylen) { + return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & + (size_t)~0xf; +} + +static size_t ksl_blklen(size_t nodelen) { + return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK - + sizeof(uint64_t); +} + +/* + * ksl_node_set_key sets |key| to |node|. + */ +static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, + const void *key) { + memcpy(&node->key, key, ksl->keylen); +} + +/* + * ksl_node_key assigns the pointer to key of |node| to key->ptr and + * returns |key|. + */ +static ngtcp2_ksl_key *ksl_node_key(ngtcp2_ksl_key *key, + ngtcp2_ksl_node *node) { + key->ptr = &node->key; + return key; +} + +/* + * ksl_nth_node returns |n|th node under |blk|. + */ +static ngtcp2_ksl_node *ksl_nth_node(const ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t n) { + return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} + +ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t n) { + return ksl_nth_node(ksl, blk, n); +} + +int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem) { + size_t nodelen = ksl_nodelen(keylen); + size_t blklen = ksl_blklen(nodelen); + ngtcp2_ksl_blk *head; + + ksl->head = ngtcp2_mem_malloc(mem, blklen); + if (!ksl->head) { + return NGTCP2_ERR_NOMEM; + } + ksl->front = ksl->back = ksl->head; + ksl->compar = compar; + ksl->keylen = keylen; + ksl->nodelen = nodelen; + ksl->n = 0; + ksl->mem = mem; + + head = ksl->head; + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; + + return 0; +} + +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + ksl_free_blk(ksl, ksl_nth_node(ksl, blk, i)->blk); + } + } + + ngtcp2_mem_free(ksl->mem, blk); +} + +void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { + if (!ksl) { + return; + } + + ksl_free_blk(ksl, ksl->head); +} + +/* + * ksl_split_blk splits |blk| into 2 ngtcp2_ksl_blk objects. The new + * ngtcp2_ksl_blk is always the "right" block. + * + * It returns the pointer to the ngtcp2_ksl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + ngtcp2_ksl_blk *rblk; + + rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + if (rblk->next) { + rblk->next->prev = rblk; + } else if (ksl->back == blk) { + ksl->back = rblk; + } + rblk->prev = blk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), + ksl->nodelen * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGTCP2_KSL_MIN_NBLK); + assert(rblk->n >= NGTCP2_KSL_MIN_NBLK); + + return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *node; + ngtcp2_ksl_blk *lblk = ksl_nth_node(ksl, blk, i)->blk, *rblk; + + rblk = ksl_split_blk(ksl, lblk); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + memmove(blk->nodes + (i + 2) * ksl->nodelen, + blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + node = ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + + return 0; +} + +/* + * ksl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_head(ngtcp2_ksl *ksl) { + ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; + ngtcp2_ksl_node *node; + + rblk = ksl_split_blk(ksl, ksl->head); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + lblk = ksl->head; + + nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (nhead == NULL) { + ngtcp2_mem_free(ksl->mem, rblk); + return NGTCP2_ERR_NOMEM; + } + nhead->next = nhead->prev = NULL; + nhead->n = 2; + nhead->leaf = 0; + + node = ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, &ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + node->blk = rblk; + + ksl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose key is |key| with the associated + * |data| at the index of |i|. This function assumes that the number + * of nodes contained by |blk| is strictly less than + * NGTCP2_KSL_MAX_NBLK. + */ +static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, + const ngtcp2_ksl_key *key, void *data) { + ngtcp2_ksl_node *node; + + assert(blk->n < NGTCP2_KSL_MAX_NBLK); + + memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, + ksl->nodelen * (blk->n - i)); + + node = ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key->ptr); + node->data = data; + + ++blk->n; +} + +static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { + ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid; + ngtcp2_ksl_node *node; + ngtcp2_ksl_key node_key; + + while (right - left > 1) { + mid = (left + right) / 2; + node = ksl_nth_node(ksl, blk, (size_t)mid); + if (compar(ksl_node_key(&node_key, node), key)) { + left = mid; + } else { + right = mid; + } + } + + return (size_t)right; +} + +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key, void *data) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + ngtcp2_ksl_key node_key; + size_t i; + int rv; + + if (blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_head(ksl); + if (rv != 0) { + return rv; + } + blk = ksl->head; + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + ksl_insert_node(ksl, blk, i, key, data); + ++ksl->n; + if (it) { + ngtcp2_ksl_it_init(it, ksl, blk, i); + } + return 0; + } + + if (i == blk->n) { + /* This insertion extends the largest key in this subtree. */ + for (; !blk->leaf;) { + node = ksl_nth_node(ksl, blk, blk->n - 1); + if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, blk->n - 1); + if (rv != 0) { + return rv; + } + node = ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key->ptr); + blk = node->blk; + } + ksl_insert_node(ksl, blk, blk->n, key, data); + ++ksl->n; + if (it) { + ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1); + } + return 0; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, i); + if (rv != 0) { + return rv; + } + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + node = ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar(ksl_node_key(&node_key, node), key)) { + ksl_node_set_key(ksl, node, key->ptr); + } + } + } + + blk = node->blk; + } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |ksl| by 1. + * + * This function returns the pointer to the merged block. + */ +static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t i) { + ngtcp2_ksl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = ksl_nth_node(ksl, blk, i)->blk; + rblk = ksl_nth_node(ksl, blk, i + 1)->blk; + + assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK); + + memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, + ksl->nodelen * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + if (lblk->next) { + lblk->next->prev = lblk; + } else if (ksl->back == rblk) { + ksl->back = lblk; + } + + ngtcp2_mem_free(ksl->mem, rblk); + + if (ksl->head == blk && blk->n == 2) { + ngtcp2_mem_free(ksl->mem, ksl->head); + ksl->head = lblk; + } else { + ksl_remove_node(ksl, blk, i + 1); + ksl_node_set_key(ksl, ksl_nth_node(ksl, blk, i), + &ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + } + + return lblk; +} + +/* + * ksl_shift_left moves the first node in blk->nodes[i]->blk->nodes to + * blk->nodes[i - 1]->blk->nodes. + */ +static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *lnode, *rnode, *dest, *src; + + assert(i > 0); + + lnode = ksl_nth_node(ksl, blk, i - 1); + rnode = ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK); + assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK); + + dest = ksl_nth_node(ksl, lnode->blk, lnode->blk->n); + src = ksl_nth_node(ksl, rnode->blk, 0); + + memcpy(dest, src, ksl->nodelen); + ksl_node_set_key(ksl, lnode, &dest->key); + ++lnode->blk->n; + + --rnode->blk->n; + memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen, + ksl->nodelen * rnode->blk->n); +} + +/* + * ksl_shift_right moves the last node in blk->nodes[i]->blk->nodes to + * blk->nodes[i + 1]->blk->nodes. + */ +static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *lnode, *rnode, *dest, *src; + + assert(i < blk->n - 1); + + lnode = ksl_nth_node(ksl, blk, i); + rnode = ksl_nth_node(ksl, blk, i + 1); + + assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK); + assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK); + + memmove(rnode->blk->nodes + ksl->nodelen, rnode->blk->nodes, + ksl->nodelen * rnode->blk->n); + ++rnode->blk->n; + + dest = ksl_nth_node(ksl, rnode->blk, 0); + src = ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1); + + memcpy(dest, src, ksl->nodelen); + + --lnode->blk->n; + ksl_node_set_key(ksl, lnode, + &ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +void ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + size_t i; + + if (!blk->leaf && blk->n == 2 && + ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && + ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { + blk = ksl_merge_node(ksl, ksl->head, 0); + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + assert(i < blk->n); + + if (blk->leaf) { + assert(i < blk->n); + ksl_remove_node(ksl, blk, i); + --ksl->n; + if (it) { + if (blk->n == i && blk->next) { + ngtcp2_ksl_it_init(it, ksl, blk->next, 0); + } else { + ngtcp2_ksl_it_init(it, ksl, blk, i); + } + } + return; + } + + node = ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGTCP2_KSL_MIN_NBLK) { + if (i > 0 && + ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + } else if (i + 1 < blk->n && + ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + } else if (i > 0) { + blk = ksl_merge_node(ksl, blk, i - 1); + } else { + assert(i + 1 < blk->n); + blk = ksl_merge_node(ksl, blk, i); + } + } else { + blk = node->blk; + } + } +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ksl_nth_node(ksl, blk, i)->blk; + } +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key, + ngtcp2_ksl_compar compar) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ksl_nth_node(ksl, blk, i)->blk; + } +} + +void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, + const ngtcp2_ksl_key *new_key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + ngtcp2_ksl_key node_key; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, ksl_node_key(&node_key, node), old_key)); + ksl_node_set_key(ksl, node, new_key->ptr); + return; + } + + ksl_node_key(&node_key, node); + if (key_equal(ksl->compar, &node_key, old_key) || + ksl->compar(&node_key, new_key)) { + ksl_node_set_key(ksl, node, new_key->ptr); + } + + blk = node->blk; + } +} + +static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { + size_t i; + ngtcp2_ksl_node *node; + ngtcp2_ksl_key node_key; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *ksl_node_key(&node_key, node)->i); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, ksl_nth_node(ksl, blk, i)->blk, level + 1); + } +} + +size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } + +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { + size_t i; + ngtcp2_ksl_blk *head; + + if (!ksl->head->leaf) { + for (i = 0; i < ksl->head->n; ++i) { + ksl_free_blk(ksl, ksl_nth_node(ksl, ksl->head, i)->blk); + } + } + + ksl->front = ksl->back = ksl->head; + ksl->n = 0; + + head = ksl->head; + + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; +} + +void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } + +ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { + ngtcp2_ksl_it it; + ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + return it; +} + +ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { + ngtcp2_ksl_it it; + ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + return it; +} + +void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, + ngtcp2_ksl_blk *blk, size_t i) { + it->ksl = ksl; + it->blk = blk; + it->i = i; +} + +void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { + assert(it->i < it->blk->n); + return ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) { + assert(!ngtcp2_ksl_it_end(it)); + + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} + +void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) { + assert(!ngtcp2_ksl_it_begin(it)); + + if (it->i == 0) { + it->blk = it->blk->prev; + it->i = it->blk->n - 1; + } else { + --it->i; + } +} + +int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} + +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) { + ngtcp2_ksl_key node_key; + + assert(it->i < it->blk->n); + + return *ksl_node_key(&node_key, ksl_nth_node(it->ksl, it->blk, it->i)); +} + +ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr) { + key->ptr = ptr; + return key; +} + +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + const ngtcp2_range *a = lhs->ptr, *b = rhs->ptr; + return a->begin < b->begin; +} + +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + const ngtcp2_range *a = lhs->ptr, *b = rhs->ptr; + return a->begin < b->begin && + !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end)); +} diff --git a/deps/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/lib/ngtcp2_ksl.h new file mode 100644 index 00000000000000..c9ce128a2209b2 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ksl.h @@ -0,0 +1,334 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_KSL_H +#define NGTCP2_KSL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +/* + * Skip List using single key instead of range. + */ + +#define NGTCP2_KSL_DEGR 8 +/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1) +/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1) + +/* + * ngtcp2_ksl_key represents key in ngtcp2_ksl. + */ +typedef union { + /* i is defined to retrieve int64_t key for convenience. */ + const int64_t *i; + /* ptr points to the key. */ + const void *ptr; +} ngtcp2_ksl_key; + +struct ngtcp2_ksl_node; +typedef struct ngtcp2_ksl_node ngtcp2_ksl_node; + +struct ngtcp2_ksl_blk; +typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk; + +/* + * ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or + * opaque data. If a node is an internal node, it contains + * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the + * location starting at key. + */ +struct ngtcp2_ksl_node { + union { + ngtcp2_ksl_blk *blk; + void *data; + }; + union { + uint64_t align; + /* key is a buffer to include key associated to this node. + Because the length of key is unknown until ngtcp2_ksl_init is + called, the actual buffer will be allocated after this + field. */ + uint8_t key[1]; + }; +}; + +/* + * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. + */ +struct ngtcp2_ksl_blk { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + ngtcp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + size_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + int leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK + ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until ngtcp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; +}; + +/* + * ngtcp2_ksl_compar is a function type which returns nonzero if key + * |lhs| should be placed before |rhs|. It returns 0 otherwise. + */ +typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +struct ngtcp2_ksl; +typedef struct ngtcp2_ksl ngtcp2_ksl; + +struct ngtcp2_ksl_it; +typedef struct ngtcp2_ksl_it ngtcp2_ksl_it; + +/* + * ngtcp2_ksl_it is a forward iterator to iterate nodes. + */ +struct ngtcp2_ksl_it { + const ngtcp2_ksl *ksl; + ngtcp2_ksl_blk *blk; + size_t i; +}; + +/* + * ngtcp2_ksl is a deterministic paged skip list. + */ +struct ngtcp2_ksl { + /* head points to the root block. */ + ngtcp2_ksl_blk *head; + /* front points to the first leaf block. */ + ngtcp2_ksl_blk *front; + /* back points to the last leaf block. */ + ngtcp2_ksl_blk *back; + ngtcp2_ksl_compar compar; + size_t n; + /* keylen is the size of key */ + size_t keylen; + /* nodelen is the actual size of ngtcp2_ksl_node including key + storage. */ + size_t nodelen; + const ngtcp2_mem *mem; +}; + +/* + * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare + * function. |keylen| is the length of key. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |ksl| itself. + */ +void ngtcp2_ksl_free(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_insert inserts |key| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function assumes that |key| does not exist in |ksl|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key, void *data); + +/* + * ngtcp2_ksl_remove removes the |key| from |ksl|. It assumes such + * the key is included in |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. + */ +void ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key); + +/* + * ngtcp2_ksl_lower_bound returns the iterator which points to the + * first node which has the key which is equal to |key| or the last + * node which satisfies !compar(&node->key, key). If there is no such + * node, it returns the iterator which satisfies ngtcp2_ksl_it_end(it) + * != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key); + +/* + * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key, + ngtcp2_ksl_compar compar); + +/* + * ngtcp2_ksl_update_key replaces the key of nodes which has |old_key| + * with |new_key|. |new_key| must be strictly greater than the + * previous node and strictly smaller than the next node. + */ +void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, + const ngtcp2_ksl_key *new_key); + +/* + * ngtcp2_ksl_begin returns the iterator which points to the first + * node. If there is no node in |ksl|, it returns the iterator which + * satisfies ngtcp2_ksl_it_end(it) != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_end returns the iterator which points to the node + * following the last node. The returned object satisfies + * ngtcp2_ksl_it_end(). If there is no node in |ksl|, it returns the + * iterator which satisfies ngtcp2_ksl_it_begin(it) != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_len returns the number of elements stored in |ksl|. + */ +size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_clear removes all elements stored in |ksl|. + */ +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. This + * function is provided for unit testing. + */ +ngtcp2_ksl_node *ngtcp2_ksl_nth_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t n); + +/* + * ngtcp2_ksl_print prints its internal state in stderr. It assumes + * that the key is of type int64_t. This function should be used for + * the debugging purpose only. + */ +void ngtcp2_ksl_print(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_it_init initializes |it|. + */ +void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, + ngtcp2_ksl_blk *blk, size_t i); + +/* + * ngtcp2_ksl_it_get returns the data associated to the node which + * |it| points to. It is undefined to call this function when + * ngtcp2_ksl_it_end(it) returns nonzero. + */ +void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_next advances the iterator by one. It is undefined + * if this function is called when ngtcp2_ksl_it_end(it) returns + * nonzero. + */ +void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_prev moves backward the iterator by one. It is + * undefined if this function is called when ngtcp2_ksl_it_begin(it) + * returns nonzero. + */ +void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first + * node. |it| might satisfy both ngtcp2_ksl_it_begin(&it) and + * ngtcp2_ksl_it_end(&it) if the skip list has no node. + */ +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when ngtcp2_ksl_it_end(it) + * returns nonzero. + */ +ngtcp2_ksl_key ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_key_ptr is a convenient function which initializes |key| + * with |ptr| and returns |key|. + */ +ngtcp2_ksl_key *ngtcp2_ksl_key_ptr(ngtcp2_ksl_key *key, const void *ptr); + +/* + * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. + * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the + * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin + * < (const ngtcp2_range *)(rhs->ptr)->begin. + */ +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +/* + * ngtcp2_ksl_range_exclusive_compar is an implementation of + * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to + * ngtcp2_range object and the function returns nonzero if (const + * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +#endif /* NGTCP2_KSL_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/lib/ngtcp2_log.c new file mode 100644 index 00000000000000..11f335d974c3eb --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_log.c @@ -0,0 +1,730 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_log.h" + +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_macro.h" + +void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, + ngtcp2_printf log_printf, ngtcp2_tstamp ts, + void *user_data) { + if (scid) { + ngtcp2_encode_hex(log->scid, scid->data, scid->datalen); + } else { + log->scid[0] = '\0'; + } + log->log_printf = log_printf; + log->ts = log->last_ts = ts; + log->user_data = user_data; +} + +/* + * # Log header + * + * + * + * : + * Log level. I=Info, W=Warning, E=Error + * + * : + * Timestamp relative to ngtcp2_log.ts field in milliseconds + * resolution. + * + * : + * Source Connection ID in hex string. + * + * : + * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto, + * con=connection(catch all) + * + * # Frame event + * + * () () + * + * : + * Flow direction. tx=transmission, rx=reception + * + * : + * Packet number. + * + * : + * Packet name. (e.g., Initial, Handshake, S01) + * + * : + * Packet type in hex string. + * + * : + * Frame name. (e.g., STREAM, ACK, PING) + * + * : + * Frame type in hex string. + */ + +#define NGTCP2_LOG_BUFLEN 4096 + +/* TODO Split second and remaining fraction with comma */ +#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" +#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" + +#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ + (DIR), hd->pkt_num, strpkttype(hd), hd->type + +#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ + (DIR), hd->pkt_num, strpkttype(hd), hd->type + +#define NGTCP2_LOG_TP_HD_FIELDS \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" + +static const char *strerrorcode(uint64_t error_code) { + switch (error_code) { + case NGTCP2_NO_ERROR: + return "NO_ERROR"; + case NGTCP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGTCP2_SERVER_BUSY: + return "SERVER_BUSY"; + case NGTCP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGTCP2_STREAM_LIMIT_ERROR: + return "STREAM_LIMIT_ERROR"; + case NGTCP2_STREAM_STATE_ERROR: + return "STREAM_STATE_ERROR"; + case NGTCP2_FINAL_SIZE_ERROR: + return "FINAL_SIZE_ERROR"; + case NGTCP2_FRAME_ENCODING_ERROR: + return "FRAME_ENCODING_ERROR"; + case NGTCP2_TRANSPORT_PARAMETER_ERROR: + return "TRANSPORT_PARAMETER_ERROR"; + case NGTCP2_CONNECTION_ID_LIMIT_ERROR: + return "CONNECTION_ID_LIMIT_ERROR"; + case NGTCP2_PROTOCOL_VIOLATION: + return "PROTOCOL_VIOLATION"; + case NGTCP2_INVALID_TOKEN: + return "INVALID_TOKEN"; + case NGTCP2_CRYPTO_BUFFER_EXCEEDED: + return "CRYPTO_BUFFER_EXCEEDED"; + case NGTCP2_KEY_UPDATE_ERROR: + return "KEY_UPDATE_ERROR"; + default: + if (0x100u <= error_code && error_code <= 0x1ffu) { + return "CRYPTO_ERROR"; + } + return "(unknown)"; + } +} + +static const char *strapperrorcode(uint64_t app_error_code) { + (void)app_error_code; + return "(unknown)"; +} + +static const char *strpkttype_long(uint8_t type) { + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return "VN"; + case NGTCP2_PKT_INITIAL: + return "Initial"; + case NGTCP2_PKT_RETRY: + return "Retry"; + case NGTCP2_PKT_HANDSHAKE: + return "Handshake"; + case NGTCP2_PKT_0RTT: + return "0RTT"; + default: + return "(unknown)"; + } +} + +static const char *strpkttype(const ngtcp2_pkt_hd *hd) { + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + return strpkttype_long(hd->type); + } + return "Short"; +} + +static const char *strevent(ngtcp2_log_event ev) { + switch (ev) { + case NGTCP2_LOG_EVENT_CON: + return "con"; + case NGTCP2_LOG_EVENT_PKT: + return "pkt"; + case NGTCP2_LOG_EVENT_FRM: + return "frm"; + case NGTCP2_LOG_EVENT_RCV: + return "rcv"; + case NGTCP2_LOG_EVENT_CRY: + return "cry"; + case NGTCP2_LOG_EVENT_PTV: + return "ptv"; + case NGTCP2_LOG_EVENT_NONE: + default: + return "non"; + } +} + +static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; } + +static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stream *fr, const char *dir) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64 + " len=%" PRIu64 " uni=%d"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id, + fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt), + (fr->stream_id & 0x2) != 0); +} + +static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_ack *fr, const char *dir) { + int64_t largest_ack, min_ack; + size_t i; + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) largest_ack=%" PRId64 + " ack_delay=%" PRIu64 "(%" PRIu64 + ") ack_block_count=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack, + fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay, + fr->num_blks); + + largest_ack = fr->largest_ack; + min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 + "] block_count=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack, + fr->first_ack_blklen); + + for (i = 0; i < fr->num_blks; ++i) { + const ngtcp2_ack_blk *blk = &fr->blks[i]; + largest_ack = min_ack - (int64_t)blk->gap - 2; + min_ack = largest_ack - (int64_t)blk->blklen; + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 + "] gap=%" PRIu64 " block_count=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, + min_ack, blk->gap, blk->blklen); + } +} + +static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_padding *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " PADDING(0x%02x) len=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len); +} + +static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_reset_stream *fr, + const char *dir) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " RESET_STREAM(0x%02x) id=0x%" PRIx64 + " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size); +} + +static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_connection_close *fr, + const char *dir) { + char reason[256]; + size_t reasonlen = ngtcp2_min(sizeof(reason) - 1, fr->reasonlen); + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT + " CONNECTION_CLOSE(0x%02x) error_code=%s(0x%" PRIx64 ") " + "frame_type=%" PRIx64 " reason_len=%" PRIu64 " reason=[%s]"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + fr->type == NGTCP2_FRAME_CONNECTION_CLOSE + ? strerrorcode(fr->error_code) + : strapperrorcode(fr->error_code), + fr->error_code, fr->frame_type, fr->reasonlen, + ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen)); +} + +static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_data *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_DATA(0x%02x) max_data=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data); +} + +static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_stream_data *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02x) id=0x%" PRIx64 + " max_stream_data=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + fr->max_stream_data); +} + +static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_streams *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02x) max_streams=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams); +} + +static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_ping *fr, const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02x)"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + +static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_data_blocked *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02x) offset=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset); +} + +static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stream_data_blocked *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02x) id=0x%" PRIx64 + " offset=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->stream_id, fr->offset); +} + +static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_streams_blocked *fr, + const char *dir) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02x) stream_limit=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_limit); +} + +static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_new_connection_id *fr, + const char *dir) { + uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1]; + uint8_t cid[sizeof(fr->cid.data) * 2 + 1]; + + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02x) seq=%" PRIu64 + " cid=0x%s retire_prior_to=%" PRIu64 + " stateless_reset_token=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq, + (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen), + fr->retire_prior_to, + (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token, + sizeof(fr->stateless_reset_token))); +} + +static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stop_sending *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " STOP_SENDING(0x%02x) id=0x%" PRIx64 + " app_error_code=%s(0x%" PRIx64 ")"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + strapperrorcode(fr->app_error_code), fr->app_error_code); +} + +static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_path_challenge *fr, + const char *dir) { + uint8_t buf[sizeof(fr->data) * 2 + 1]; + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02x) data=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); +} + +static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_path_response *fr, + const char *dir) { + uint8_t buf[sizeof(fr->data) * 2 + 1]; + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02x) data=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); +} + +static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_crypto *fr, const char *dir) { + size_t datalen = 0; + size_t i; + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); +} + +static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_new_token *fr, const char *dir) { + /* Show at most first 64 bytes of token. If token is longer than 64 + bytes, log first 64 bytes and then append "*" */ + uint8_t buf[128 + 1 + 1]; + uint8_t *p; + + if (fr->tokenlen > 64) { + p = ngtcp2_encode_hex(buf, fr->token, 64); + p[128] = '*'; + p[129] = '\0'; + } else { + p = ngtcp2_encode_hex(buf, fr->token, fr->tokenlen); + } + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s token_len=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->tokenlen); +} + +static void log_fr_retire_connection_id(ngtcp2_log *log, + const ngtcp2_pkt_hd *hd, + const ngtcp2_retire_connection_id *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02x) seq=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq); +} + +static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_handshake_done *fr, + const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + +static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr, const char *dir) { + switch (fr->type) { + case NGTCP2_FRAME_STREAM: + log_fr_stream(log, hd, &fr->stream, dir); + break; + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + log_fr_ack(log, hd, &fr->ack, dir); + break; + case NGTCP2_FRAME_PADDING: + log_fr_padding(log, hd, &fr->padding, dir); + break; + case NGTCP2_FRAME_RESET_STREAM: + log_fr_reset_stream(log, hd, &fr->reset_stream, dir); + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + log_fr_connection_close(log, hd, &fr->connection_close, dir); + break; + case NGTCP2_FRAME_MAX_DATA: + log_fr_max_data(log, hd, &fr->max_data, dir); + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + log_fr_max_stream_data(log, hd, &fr->max_stream_data, dir); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + log_fr_max_streams(log, hd, &fr->max_streams, dir); + break; + case NGTCP2_FRAME_PING: + log_fr_ping(log, hd, &fr->ping, dir); + break; + case NGTCP2_FRAME_DATA_BLOCKED: + log_fr_data_blocked(log, hd, &fr->data_blocked, dir); + break; + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + log_fr_stream_data_blocked(log, hd, &fr->stream_data_blocked, dir); + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + log_fr_streams_blocked(log, hd, &fr->streams_blocked, dir); + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + log_fr_new_connection_id(log, hd, &fr->new_connection_id, dir); + break; + case NGTCP2_FRAME_STOP_SENDING: + log_fr_stop_sending(log, hd, &fr->stop_sending, dir); + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + log_fr_path_challenge(log, hd, &fr->path_challenge, dir); + break; + case NGTCP2_FRAME_PATH_RESPONSE: + log_fr_path_response(log, hd, &fr->path_response, dir); + break; + case NGTCP2_FRAME_CRYPTO: + log_fr_crypto(log, hd, &fr->crypto, dir); + break; + case NGTCP2_FRAME_NEW_TOKEN: + log_fr_new_token(log, hd, &fr->new_token, dir); + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir); + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + log_fr_handshake_done(log, hd, &fr->handshake_done, dir); + break; + default: + assert(0); + } +} + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr) { + if (!log->log_printf) { + return; + } + + log_fr(log, hd, fr, "rx"); +} + +void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr) { + if (!log->log_printf) { + return; + } + + log_fr(log, hd, fr, "tx"); +} + +void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv) { + size_t i; + + if (!log->log_printf) { + return; + } + + for (i = 0; i < nsv; ++i) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"), + NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]); + } +} + +void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { + uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1]; + ngtcp2_pkt_hd shd; + ngtcp2_pkt_hd *hd = &shd; + + if (!log->log_printf) { + return; + } + + memset(&shd, 0, sizeof(shd)); + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), + NGTCP2_LOG_PKT_HD_FIELDS("rx"), + (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token, + sizeof(sr->stateless_reset_token)), + sr->randlen); +} + +void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, + const ngtcp2_transport_params *params) { + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; + uint8_t addr[16 * 2 + 1]; + uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; + + if (!log->log_printf) { + return; + } + + if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + if (params->stateless_reset_token_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " stateless_reset_token=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + token, params->stateless_reset_token, + sizeof(params->stateless_reset_token))); + } + + if (params->preferred_address_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.ipv4_addr=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + addr, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr))); + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv4_port); + + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.ipv6_addr=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + addr, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr))); + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv6_port); + + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.cid=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + cid, params->preferred_address.cid.data, + params->preferred_address.cid.datalen)); + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + token, params->preferred_address.stateless_reset_token, + sizeof(params->preferred_address.stateless_reset_token))); + } + + if (params->original_connection_id_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " original_connection_id=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + cid, params->original_connection_id.data, + params->original_connection_id.datalen)); + } + } + + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local); + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_stream_data_uni=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_bidi_streams=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_uni_streams=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%u"), + NGTCP2_LOG_TP_HD_FIELDS, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_packet_size=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->max_packet_size); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " ack_delay_exponent=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%u"), + NGTCP2_LOG_TP_HD_FIELDS, + params->max_ack_delay / NGTCP2_MILLISECONDS); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " disable_active_migration=%d"), + NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration); +} + +void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, + uint8_t flags, ngtcp2_tstamp sent_ts) { + if (!log->log_printf) { + return; + } + + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, + (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", + type, sent_ts); +} + +static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const char *dir) { + uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1]; + uint8_t scid[sizeof(hd->scid.data) * 2 + 1]; + + if (!log->log_printf) { + return; + } + + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, + "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), + (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) + : "Short", + hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); +} + +void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + log_pkt_hd(log, hd, "rx"); +} + +void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + log_pkt_hd(log, hd, "tx"); +} + +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...) { + va_list ap; + int n; + char buf[NGTCP2_LOG_BUFLEN]; + + if (!log->log_printf) { + return; + } + + va_start(ap, fmt); + n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (n < 0 || (size_t)n >= sizeof(buf)) { + return; + } + + log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"), + timestamp_cast(log->last_ts - log->ts), log->scid, + strevent(ev), buf); +} + +void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, + "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, + strpkttype(hd), hd->type); +} diff --git a/deps/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/lib/ngtcp2_log.h new file mode 100644 index 00000000000000..bb9737e354b019 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_log.h @@ -0,0 +1,99 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_LOG_H +#define NGTCP2_LOG_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" + +typedef enum { + NGTCP2_LOG_EVENT_NONE, + /* connection (catch-all) event */ + NGTCP2_LOG_EVENT_CON, + /* packet event */ + NGTCP2_LOG_EVENT_PKT, + /* frame event */ + NGTCP2_LOG_EVENT_FRM, + /* recovery event */ + NGTCP2_LOG_EVENT_RCV, + /* crypto event */ + NGTCP2_LOG_EVENT_CRY, + /* path validation event */ + NGTCP2_LOG_EVENT_PTV, +} ngtcp2_log_event; + +struct ngtcp2_log { + /* log_printf is a sink to write log. NULL means no logging + output. */ + ngtcp2_printf log_printf; + /* ts is the time point used to write time delta in the log. */ + ngtcp2_tstamp ts; + /* last_ts is the most recent time point that this object is + told. */ + ngtcp2_tstamp last_ts; + /* user_data is user-defined opaque data which is passed to + log_pritnf. */ + void *user_data; + /* scid is SCID encoded as NULL-terminated hex string. */ + uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1]; +}; + +typedef struct ngtcp2_log ngtcp2_log; + +void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, + ngtcp2_printf log_printf, ngtcp2_tstamp ts, + void *user_data); + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr); +void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr); + +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...); + +void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv); + +void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr); + +void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, + const ngtcp2_transport_params *params); + +void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, + uint8_t flags, ngtcp2_tstamp sent_ts); + +void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +#endif /* NGTCP2_LOG_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_macro.h b/deps/ngtcp2/lib/ngtcp2_macro.h new file mode 100644 index 00000000000000..e2603aae15dd04 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_macro.h @@ -0,0 +1,53 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MACRO_H +#define NGTCP2_MACRO_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#define ngtcp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define ngtcp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define ngtcp2_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +/* ngtcp2_list_insert inserts |T| before |*PD|. The contract is that + this is singly linked list, and the next element is pointed by next + field of the previous element. |PD| must be a pointer to the + pointer to the next field of the previous element of |*PD|: if C is + the previous element of |PD|, PD = &C->next. */ +#define ngtcp2_list_insert(T, PD) \ + do { \ + (T)->next = *(PD); \ + *(PD) = (T); \ + } while (0) + +#endif /* NGTCP2_MACRO_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/lib/ngtcp2_map.c new file mode 100644 index 00000000000000..bd214ff87b5046 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_map.c @@ -0,0 +1,210 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_map.h" + +#include + +#include "ngtcp2_conv.h" + +#define INITIAL_TABLE_LENGTH 256 + +int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_entry *)); + if (map->table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void ngtcp2_map_free(ngtcp2_map *map) { ngtcp2_mem_free(map->mem, map->table); } + +void ngtcp2_map_each_free(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + ngtcp2_map_entry *next = entry->next; + func(entry, ptr); + entry = next; + } + map->table[i] = NULL; + } +} + +int ngtcp2_map_each(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry, *next; + for (entry = map->table[i]; entry;) { + next = entry->next; + rv = func(entry, ptr); + if (rv != 0) { + return rv; + } + entry = next; + } + } + return 0; +} + +void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) { + entry->key = key; + entry->next = NULL; +} + +/* FNV1a hash */ +static uint32_t hash(key_type key, uint32_t mod) { + uint8_t *p, *end; + uint32_t h = 0x811C9DC5u; + + key = ngtcp2_htonl64(key); + p = (uint8_t *)&key; + end = p + sizeof(key_type); + + for (; p != end; ++p) { + h ^= *p; + h *= 0x01000193u; + } + + return h & (mod - 1); +} + +static int insert(ngtcp2_map_entry **table, uint32_t tablelen, + ngtcp2_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + if (table[h] == NULL) { + table[h] = entry; + } else { + ngtcp2_map_entry *p; + /* We won't allow duplicated key, so check it out. */ + for (p = table[h]; p; p = p->next) { + if (p->key == entry->key) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + entry->next = table[h]; + table[h] = entry; + } + return 0; +} + +/* new_tablelen must be power of 2 */ +static int resize(ngtcp2_map *map, uint32_t new_tablelen) { + uint32_t i; + ngtcp2_map_entry **new_table; + + new_table = + ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_entry *)); + if (new_table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + ngtcp2_map_entry *entry; + for (entry = map->table[i]; entry;) { + ngtcp2_map_entry *next = entry->next; + entry->next = NULL; + /* This function must succeed */ + insert(new_table, new_tablelen, entry); + entry = next; + } + } + ngtcp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; +} + +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { + int rv; + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = insert(map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) { + uint32_t h; + ngtcp2_map_entry *entry; + h = hash(key, map->tablelen); + for (entry = map->table[h]; entry; entry = entry->next) { + if (entry->key == key) { + return entry; + } + } + return NULL; +} + +int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { + uint32_t h; + ngtcp2_map_entry **dst; + + h = hash(key, map->tablelen); + + for (dst = &map->table[h]; *dst; dst = &(*dst)->next) { + if ((*dst)->key != key) { + continue; + } + + *dst = (*dst)->next; + --map->size; + return 0; + } + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +void ngtcp2_map_clear(ngtcp2_map *map) { + uint32_t i; + + for (i = 0; i < map->tablelen; ++i) { + map->table[i] = NULL; + } + + map->size = 0; +} + +size_t ngtcp2_map_size(ngtcp2_map *map) { return map->size; } diff --git a/deps/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/lib/ngtcp2_map.h new file mode 100644 index 00000000000000..86f23a16a7e5dd --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_map.h @@ -0,0 +1,145 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MAP_H +#define NGTCP2_MAP_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" + +/* Implementation of unordered map */ + +typedef uint64_t key_type; + +typedef struct ngtcp2_map_entry { + struct ngtcp2_map_entry *next; + key_type key; +} ngtcp2_map_entry; + +typedef struct { + ngtcp2_map_entry **table; + const ngtcp2_mem *mem; + size_t size; + uint32_t tablelen; +} ngtcp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use ngtcp2_map_each_free() to free + * each entries. + */ +void ngtcp2_map_free(ngtcp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void ngtcp2_map_each_free(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int ngtcp2_map_remove(ngtcp2_map *map, key_type key); + +/* + * Removes all entries from |map|. + */ +void ngtcp2_map_clear(ngtcp2_map *map); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t ngtcp2_map_size(ngtcp2_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * ngtcp2_map_each_free() instead. + */ +int ngtcp2_map_each(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr); + +#endif /* NGTCP2_MAP_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/lib/ngtcp2_mem.c new file mode 100644 index 00000000000000..2c036ad1634b05 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_mem.c @@ -0,0 +1,75 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } + +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/deps/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/lib/ngtcp2_mem.h new file mode 100644 index 00000000000000..cdecf8763a5f36 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_mem.h @@ -0,0 +1,43 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MEM_H +#define NGTCP2_MEM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); +void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); +void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); +void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); + +#endif /* NGTCP2_MEM_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_net.h b/deps/ngtcp2/lib/ngtcp2_net.h new file mode 100644 index 00000000000000..9c121ea7f21547 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_net.h @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_NET_H +#define NGTCP2_NET_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include +#endif /* HAVE_NETINET_IN_H */ + +#include + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +#endif /* NGTCP2_NET_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/lib/ngtcp2_path.c new file mode 100644 index 00000000000000..ebda947c6d6379 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_path.c @@ -0,0 +1,68 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_path.h" + +#include + +#include "ngtcp2_addr.h" + +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, + const ngtcp2_addr *remote) { + path->local = *local; + path->remote = *remote; +} + +void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { + ngtcp2_addr_copy(&dest->local, &src->local); + ngtcp2_addr_copy(&dest->remote, &src->remote); +} + +int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { + return ngtcp2_addr_eq(&a->local, &b->local) && + ngtcp2_addr_eq(&a->remote, &b->remote); +} + +void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, const void *local_addr, + size_t local_addrlen, void *local_user_data, + const void *remote_addr, size_t remote_addrlen, + void *remote_user_data) { + ngtcp2_addr_init(&ps->path.local, ps->local_addrbuf, 0, local_user_data); + ngtcp2_addr_init(&ps->path.remote, ps->remote_addrbuf, 0, remote_user_data); + + ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); + ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); +} + +void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, + const ngtcp2_path *path) { + ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen, + path->local.user_data, path->remote.addr, + path->remote.addrlen, path->remote.user_data); +} + +void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { + ngtcp2_addr_init(&ps->path.local, ps->local_addrbuf, 0, NULL); + ngtcp2_addr_init(&ps->path.remote, ps->remote_addrbuf, 0, NULL); +} diff --git a/deps/ngtcp2/lib/ngtcp2_path.h b/deps/ngtcp2/lib/ngtcp2_path.h new file mode 100644 index 00000000000000..1b2e2f87c8c780 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_path.h @@ -0,0 +1,63 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PATH_H +#define NGTCP2_PATH_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * ngtcp2_path_init initializes |path| with the given addresses. Note + * that the buffer pointed by local->addr and remote->addr are not + * copied. Their pointer values are assigned instead. + */ +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, + const ngtcp2_addr *remote); + +/* + * ngtcp2_path_copy copies |src| into |dest|. This function assumes + * that |dest| has enough buffer to store the deep copy of src->local + * and src->remote. + */ +void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); + +/* + * ngtcp2_path_eq returns nonzero if |a| equals |b| such that + * ngtcp2_addr_eq(&a->local, &b->local) && ngtcp2_addr_eq(&a->remote, + * &b->remote) is true. + */ +int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); + +/* + * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial + * data. + */ +void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, + const ngtcp2_path *path); + +#endif /* NGTCP2_PATH_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pipeack.c b/deps/ngtcp2/lib/ngtcp2_pipeack.c new file mode 100644 index 00000000000000..f44bdf07bbb485 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pipeack.c @@ -0,0 +1,155 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pipeack.h" + +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_rcvry.h" + +void ngtcp2_pipeack_init(ngtcp2_pipeack *pipeack, ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + + memset(pipeack, 0, sizeof(*pipeack)); + + /* Initialize value to UINT64_MAX so that it does not restrict CWND + growth. It serves as "undefined" value. */ + pipeack->value = UINT64_MAX; + pipeack->ts = ts; + pipeack->len = 1; + + sample = &pipeack->samples[pipeack->pos]; + sample->value = 0; + sample->ts = ts; +} + +static ngtcp2_duration get_rtt(const ngtcp2_rcvry_stat *rcs) { + if (rcs->smoothed_rtt == 0) { + return NGTCP2_DEFAULT_INITIAL_RTT; + } + return rcs->smoothed_rtt; +} + +/* + * compute_sampling_period returns pipeACK sampling period. + */ +static ngtcp2_duration compute_sampling_period(ngtcp2_duration rtt) { + return ngtcp2_max(rtt * 3, NGTCP2_SECONDS); +} + +/* + * compute_measurement_period returns the measurement period using + * pipeACK sampling period |speriod|. + */ +static ngtcp2_duration compute_measurement_period(ngtcp2_duration speriod) { + return speriod / NGTCP2_PIPEACK_NUM_SAMPLES; +} + +static void pipeack_update_sample(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + ngtcp2_duration rtt = get_rtt(rcs); + ngtcp2_duration speriod = compute_sampling_period(rtt); + ngtcp2_duration mperiod = compute_measurement_period(speriod); + + if (pipeack->len == 0) { + pipeack->len = 1; + + sample = &pipeack->samples[pipeack->pos]; + sample->ts = ts; + sample->value = acked_bytes; + + return; + } + + sample = &pipeack->samples[pipeack->pos]; + if (sample->ts + mperiod <= ts) { + ts = sample->ts + (ts - sample->ts) / mperiod * mperiod; + pipeack->pos = (pipeack->pos - 1) & (NGTCP2_PIPEACK_NUM_SAMPLES - 1); + + if (pipeack->len < NGTCP2_PIPEACK_NUM_SAMPLES) { + ++pipeack->len; + } + + sample = &pipeack->samples[pipeack->pos]; + sample->ts = ts; + sample->value = acked_bytes; + + return; + } + + sample->value = ngtcp2_max(sample->value, acked_bytes); +} + +void ngtcp2_pipeack_update(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts) { + uint64_t value; + ngtcp2_duration d = ts - pipeack->ts; + ngtcp2_duration rtt = get_rtt(rcs); + + pipeack->acked_bytes += acked_bytes; + + if (d < rtt) { + return; + } + + value = pipeack->acked_bytes * rtt / d; + pipeack->acked_bytes = 0; + pipeack->ts = ts; + + pipeack_update_sample(pipeack, value, rcs, ts); +} + +void ngtcp2_pipeack_update_value(ngtcp2_pipeack *pipeack, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts) { + ngtcp2_pipeack_sample *sample; + ngtcp2_duration rtt = get_rtt(rcs); + ngtcp2_duration speriod = compute_sampling_period(rtt); + ngtcp2_duration mperiod = compute_measurement_period(speriod); + size_t i; + uint64_t res = 0; + + for (i = pipeack->len; i > 0; --i) { + sample = &pipeack->samples[(pipeack->pos + i - 1) & + (NGTCP2_PIPEACK_NUM_SAMPLES - 1)]; + if (sample->ts + speriod <= ts) { + --pipeack->len; + continue; + } + if (sample->ts + mperiod > ts) { + break; + } + + res = ngtcp2_max(res, sample->value); + } + + if (res == 0) { + pipeack->value = UINT64_MAX; + } else { + pipeack->value = res; + } +} diff --git a/deps/ngtcp2/lib/ngtcp2_pipeack.h b/deps/ngtcp2/lib/ngtcp2_pipeack.h new file mode 100644 index 00000000000000..d7a34a936bbb57 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pipeack.h @@ -0,0 +1,89 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PIPEACK_H +#define NGTCP2_PIPEACK_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_pipeack_sample { + /* + * value is the maximum cumulative acknowledged bytes per RTT in + * this sample. + */ + uint64_t value; + /* ts is the time when this sample started. */ + ngtcp2_tstamp ts; +} ngtcp2_pipeack_sample; + +#define NGTCP2_PIPEACK_NUM_SAMPLES 4 + +/* + * ngtcp2_pipeack implements pipeACK measurement described in RFC + * 7661. + */ +typedef struct ngtcp2_pipeack { + /* value is the pre-computed maximum pipeACK value in the effective + sample period. */ + uint64_t value; + /* acked_bytes is the accumulated acked_bytes per RTT. */ + uint64_t acked_bytes; + /* ts is the timestamp when the accumulation to acked_bytes + started. */ + ngtcp2_tstamp ts; + /* pos points to the latest sample in samples. */ + size_t pos; + /* len is the number of effective from pos. samples is treated as + ring buffer and its index will wrap when pos + len goes beyond + NGTCP2_PIPEACK_NUM_SAMPLES - 1. */ + size_t len; + ngtcp2_pipeack_sample samples[NGTCP2_PIPEACK_NUM_SAMPLES]; +} ngtcp2_pipeack; + +/* + * ngtcp2_pipeack_init initializes the object pointed by |pipeack|. + */ +void ngtcp2_pipeack_init(ngtcp2_pipeack *pipeack, ngtcp2_tstamp ts); + +/* + * ngtcp2_pipeack_update notifies the acknowledged bytes |acked_bytes| + * to |pipeack|. + */ +void ngtcp2_pipeack_update(ngtcp2_pipeack *pipeack, uint64_t acked_bytes, + const ngtcp2_rcvry_stat *rcs, ngtcp2_tstamp ts); + +/* + * ngtcp2_pipeack_update_value computes maximum pipeACK value. + * Samples which have timestamp <= |ts| - pipeACK sampling period are + * discarded. + */ +void ngtcp2_pipeack_update_value(ngtcp2_pipeack *pipeack, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_tstamp ts); + +#endif /* NGTCP2_PIPEACK_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/lib/ngtcp2_pkt.c new file mode 100644 index 00000000000000..059022bf2ca8d0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pkt.c @@ -0,0 +1,2252 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pkt.h" + +#include +#include +#include + +#include "ngtcp2_conv.h" +#include "ngtcp2_str.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_mem.h" + +int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts, + const ngtcp2_mem *mem) { + *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen); + if (*ppc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_path_storage_init2(&(*ppc)->path, path); + (*ppc)->next = NULL; + (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain); + (*ppc)->pktlen = pktlen; + (*ppc)->ts = ts; + + memcpy((*ppc)->pkt, pkt, pktlen); + + return 0; +} + +void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, pc); +} + +int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, + size_t *pdcidlen, const uint8_t **pscid, + size_t *pscidlen, const uint8_t *data, + size_t datalen, size_t short_dcidlen) { + size_t len; + uint32_t version; + size_t dcidlen, scidlen; + + assert(datalen); + + if (data[0] & NGTCP2_HEADER_FORM_BIT) { + /* 1 byte (Header Form, Fixed Bit, Long Packet Type, Type-Specific bits) + * 4 bytes Version + * 1 byte DCID Length + * 1 byte SCID Length + */ + len = 1 + 4 + 1 + 1; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dcidlen = data[5]; + len += dcidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + scidlen = data[5 + 1 + dcidlen]; + len += scidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + version = ngtcp2_get_uint32(&data[1]); + + if ((version == 0 || version == NGTCP2_PROTO_VER) && + (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + *pversion = version; + *pdcid = &data[6]; + *pdcidlen = dcidlen; + *pscid = &data[6 + dcidlen + 1]; + *pscidlen = scidlen; + + if (version && version != NGTCP2_PROTO_VER) { + return 1; + } + return 0; + } + + assert(short_dcidlen <= NGTCP2_MAX_CIDLEN); + + len = 1 + short_dcidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + *pversion = NGTCP2_PROTO_VER; + *pdcid = &data[1]; + *pdcidlen = short_dcidlen; + *pscid = NULL; + *pscidlen = 0; + + return 0; +} + +void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, + const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + int64_t pkt_num, size_t pkt_numlen, uint32_t version, + size_t len) { + hd->flags = flags; + hd->type = type; + if (dcid) { + hd->dcid = *dcid; + } else { + ngtcp2_cid_zero(&hd->dcid); + } + if (scid) { + hd->scid = *scid; + } else { + ngtcp2_cid_zero(&hd->scid); + } + hd->pkt_num = pkt_num; + hd->token = NULL; + hd->tokenlen = 0; + hd->pkt_numlen = pkt_numlen; + hd->version = version; + hd->len = len; +} + +static int has_mask(uint8_t b, uint8_t mask) { return (b & mask) == mask; } + +ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen) { + uint8_t type; + uint32_t version; + size_t dcil, scil; + const uint8_t *p; + size_t len = 0; + size_t n; + size_t ntokenlen = 0; + const uint8_t *token = NULL; + size_t tokenlen = 0; + + if (pktlen < 5) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + version = ngtcp2_get_uint32(&pkt[1]); + + if (version == 0) { + type = NGTCP2_PKT_VERSION_NEGOTIATION; + /* This must be Version Negotiation packet which lacks packet + number and payload length fields. */ + len = 5 + 2; + } else { + if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + type = ngtcp2_pkt_get_type_long(pkt[0]); + switch (type) { + case NGTCP2_PKT_INITIAL: + len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - + 1; /* Cut packet number field */ + break; + case NGTCP2_PKT_RETRY: + /* Retry packet does not have packet number and length fields */ + len = 5 + 2; + break; + case NGTCP2_PKT_HANDSHAKE: + case NGTCP2_PKT_0RTT: + len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ + break; + default: + /* Unreachable */ + assert(0); + } + } + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = &pkt[5]; + dcil = *p; + if (dcil > NGTCP2_MAX_CIDLEN) { + /* QUIC v1 implementation never expect to receive CID length more + than NGTCP2_MAX_CIDLEN. */ + return NGTCP2_ERR_INVALID_ARGUMENT; + } + len += dcil; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p += 1 + dcil; + scil = *p; + if (scil > NGTCP2_MAX_CIDLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + len += scil; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p += 1 + scil; + + if (type == NGTCP2_PKT_INITIAL) { + /* Token Length */ + ntokenlen = ngtcp2_get_varint_len(p); + len += ntokenlen - 1; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + tokenlen = ngtcp2_get_varint(&ntokenlen, p); + len += tokenlen; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p += ntokenlen; + + if (tokenlen) { + token = p; + } + + p += tokenlen; + } + + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + case NGTCP2_PKT_RETRY: + break; + default: + /* Length */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + + dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; + dest->type = type; + dest->version = version; + dest->pkt_num = 0; + dest->pkt_numlen = 0; + + p = &pkt[6]; + ngtcp2_cid_init(&dest->dcid, p, dcil); + p += dcil + 1; + ngtcp2_cid_init(&dest->scid, p, scil); + p += scil; + + dest->token = (uint8_t *)token; + dest->tokenlen = tokenlen; + p += ntokenlen + tokenlen; + + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + case NGTCP2_PKT_RETRY: + dest->len = 0; + break; + default: + dest->len = ngtcp2_get_varint(&n, p); + p += n; + } + + assert((size_t)(p - pkt) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen) { + size_t len = 1 + dcidlen; + const uint8_t *p = pkt; + + assert(dcidlen <= NGTCP2_MAX_CIDLEN); + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) || + (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = &pkt[1]; + + dest->type = NGTCP2_PKT_SHORT; + + ngtcp2_cid_init(&dest->dcid, p, dcidlen); + p += dcidlen; + + /* Set 0 to SCID so that we don't accidentally reference it and gets + garbage. */ + ngtcp2_cid_zero(&dest->scid); + + dest->flags = NGTCP2_PKT_FLAG_NONE; + dest->version = 0; + dest->len = 0; + dest->pkt_num = 0; + dest->pkt_numlen = 0; + + assert((size_t)(p - pkt) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd) { + uint8_t *p; + size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen - + 2; /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for + len and 1 byte for packet number. */ + + if (hd->type != NGTCP2_PKT_RETRY) { + len += 2 /* Length */ + hd->pkt_numlen; + } + + if (hd->type == NGTCP2_PKT_INITIAL) { + len += ngtcp2_put_varint_len(hd->tokenlen) + hd->tokenlen; + } + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK | + (uint8_t)(hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1); + p = ngtcp2_put_uint32be(p, hd->version); + *p++ = (uint8_t)hd->dcid.datalen; + if (hd->dcid.datalen) { + p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); + } + *p++ = (uint8_t)hd->scid.datalen; + if (hd->scid.datalen) { + p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen); + } + + if (hd->type == NGTCP2_PKT_INITIAL) { + p = ngtcp2_put_varint(p, hd->tokenlen); + if (hd->tokenlen) { + p = ngtcp2_cpymem(p, hd->token, hd->tokenlen); + } + } + + if (hd->type != NGTCP2_PKT_RETRY) { + p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd) { + uint8_t *p; + size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1); + if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { + *p |= NGTCP2_SHORT_KEY_PHASE_BIT; + } + + ++p; + + if (hd->dcid.datalen) { + p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); + } + + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, + size_t payloadlen) { + uint8_t type; + + if (payloadlen == 0) { + return 0; + } + + type = payload[0]; + + switch (type) { + case NGTCP2_FRAME_PADDING: + return (ngtcp2_ssize)ngtcp2_pkt_decode_padding_frame(&dest->padding, + payload, payloadlen); + case NGTCP2_FRAME_RESET_STREAM: + return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload, + payloadlen); + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close, + payload, payloadlen); + case NGTCP2_FRAME_MAX_DATA: + return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload, + payloadlen); + case NGTCP2_FRAME_MAX_STREAM_DATA: + return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data, + payload, payloadlen); + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload, + payloadlen); + case NGTCP2_FRAME_PING: + return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen); + case NGTCP2_FRAME_DATA_BLOCKED: + return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload, + payloadlen); + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + return ngtcp2_pkt_decode_stream_data_blocked_frame( + &dest->stream_data_blocked, payload, payloadlen); + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked, + payload, payloadlen); + case NGTCP2_FRAME_NEW_CONNECTION_ID: + return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id, + payload, payloadlen); + case NGTCP2_FRAME_STOP_SENDING: + return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload, + payloadlen); + case NGTCP2_FRAME_ACK: + return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen); + case NGTCP2_FRAME_PATH_CHALLENGE: + return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge, + payload, payloadlen); + case NGTCP2_FRAME_PATH_RESPONSE: + return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload, + payloadlen); + case NGTCP2_FRAME_CRYPTO: + return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen); + case NGTCP2_FRAME_NEW_TOKEN: + return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload, + payloadlen); + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + return ngtcp2_pkt_decode_retire_connection_id_frame( + &dest->retire_connection_id, payload, payloadlen); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done, + payload, payloadlen); + default: + if (has_mask(type, NGTCP2_FRAME_STREAM)) { + return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen); + } + return NGTCP2_ERR_FRAME_ENCODING; + } +} + +ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, + const uint8_t *payload, + size_t payloadlen) { + uint8_t type; + size_t len = 1 + 1; + const uint8_t *p; + size_t datalen; + size_t ndatalen = 0; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + if (type & NGTCP2_STREAM_OFF_BIT) { + ++len; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + + if (type & NGTCP2_STREAM_LEN_BIT) { + ++len; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + ndatalen = ngtcp2_get_varint_len(p); + len += ndatalen - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + datalen = ngtcp2_get_varint(&ndatalen, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + } else { + len = payloadlen; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STREAM; + dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM); + dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + + if (type & NGTCP2_STREAM_OFF_BIT) { + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + } else { + dest->offset = 0; + } + + if (type & NGTCP2_STREAM_LEN_BIT) { + p += ndatalen; + } else { + datalen = payloadlen - (size_t)(p - payload); + } + + if (datalen) { + dest->data[0].len = datalen; + dest->data[0].base = (uint8_t *)p; + dest->datacnt = 1; + p += datalen; + } else { + dest->datacnt = 0; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t num_blks, max_num_blks; + size_t nnum_blks; + size_t len = 1 + 1 + 1 + 1 + 1; + const uint8_t *p; + size_t i, j; + ngtcp2_ack_blk *blk; + size_t n; + uint8_t type; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + /* Largest Acknowledged */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + /* ACK Delay */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + /* ACK Block Count */ + nnum_blks = ngtcp2_get_varint_len(p); + len += nnum_blks - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + num_blks = ngtcp2_get_varint(&nnum_blks, p); + len += num_blks * (1 + 1); + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += nnum_blks; + + /* First ACK Block */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + for (i = 0; i < num_blks; ++i) { + /* Gap, and Additional ACK Block */ + for (j = 0; j < 2; ++j) { + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + } + + if (type == NGTCP2_FRAME_ACK_ECN) { + len += 3; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + for (i = 0; i < 3; ++i) { + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + } + + /* TODO We might not decode all blocks. It could be very large. */ + max_num_blks = ngtcp2_min(NGTCP2_MAX_ACK_BLKS, num_blks); + + p = payload + 1; + + dest->type = type; + dest->largest_ack = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->ack_delay = ngtcp2_get_varint(&n, p); + /* This value will be assigned in the upper layer. */ + dest->ack_delay_unscaled = 0; + p += n; + dest->num_blks = max_num_blks; + p += nnum_blks; + dest->first_ack_blklen = ngtcp2_get_varint(&n, p); + p += n; + + for (i = 0; i < max_num_blks; ++i) { + blk = &dest->blks[i]; + blk->gap = ngtcp2_get_varint(&n, p); + p += n; + blk->blklen = ngtcp2_get_varint(&n, p); + p += n; + } + for (i = max_num_blks; i < num_blks; ++i) { + p += ngtcp2_get_varint_len(p); + p += ngtcp2_get_varint_len(p); + } + + if (type == NGTCP2_FRAME_ACK_ECN) { + /* Just parse ECN section for now */ + for (i = 0; i < 3; ++i) { + ngtcp2_get_varint(&n, p); + p += n; + } + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *p, *ep; + + assert(payloadlen > 0); + + p = payload + 1; + ep = payload + payloadlen; + + for (; p != ep && *p == NGTCP2_FRAME_PADDING; ++p) + ; + + dest->type = NGTCP2_FRAME_PADDING; + dest->len = (size_t)(p - payload); + + return dest->len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_RESET_STREAM; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->app_error_code = ngtcp2_get_varint(&n, p); + p += n; + dest->final_size = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( + ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t reasonlen; + size_t nreasonlen; + size_t n; + uint8_t type; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { + ++len; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + + nreasonlen = ngtcp2_get_varint_len(p); + len += nreasonlen - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + reasonlen = ngtcp2_get_varint(&nreasonlen, p); + len += reasonlen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = type; + dest->error_code = ngtcp2_get_varint(&n, p); + p += n; + if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { + dest->frame_type = ngtcp2_get_varint(&n, p); + p += n; + } else { + dest->frame_type = 0; + } + dest->reasonlen = reasonlen; + p += nreasonlen; + if (reasonlen == 0) { + dest->reason = NULL; + } else { + dest->reason = (uint8_t *)p; + p += reasonlen; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_MAX_DATA; + dest->max_data = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( + ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_MAX_STREAM_DATA; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->max_stream_data = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = payload[0]; + dest->max_streams = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, + const uint8_t *payload, + size_t payloadlen) { + (void)payload; + (void)payloadlen; + + dest->type = NGTCP2_FRAME_PING; + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_DATA_BLOCKED; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( + ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = payload[0]; + dest->stream_limit = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( + ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1 + 1 + 16; + const uint8_t *p; + size_t n; + size_t cil; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + cil = *p; + if (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + len += cil; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_NEW_CONNECTION_ID; + dest->seq = ngtcp2_get_varint(&n, p); + p += n; + dest->retire_prior_to = ngtcp2_get_varint(&n, p); + p += n + 1; + ngtcp2_cid_init(&dest->cid, p, cil); + p += cil; + memcpy(dest->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); + p += NGTCP2_STATELESS_RESET_TOKENLEN; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STOP_SENDING; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->app_error_code = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 8; + const uint8_t *p; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_PATH_CHALLENGE; + ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); + p += sizeof(dest->data); + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 8; + const uint8_t *p; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_PATH_RESPONSE; + ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); + p += sizeof(dest->data); + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t datalen; + size_t ndatalen; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + ndatalen = ngtcp2_get_varint_len(p); + len += ndatalen - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + datalen = ngtcp2_get_varint(&ndatalen, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_CRYPTO; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + dest->data[0].len = datalen; + p += ndatalen; + if (dest->data[0].len) { + dest->data[0].base = (uint8_t *)p; + p += dest->data[0].len; + dest->datacnt = 1; + } else { + dest->data[0].base = NULL; + dest->datacnt = 0; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + size_t datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + datalen = ngtcp2_get_varint(&n, p); + len += datalen; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_NEW_TOKEN; + dest->tokenlen = datalen; + p += n; + dest->token = p; + p += dest->tokenlen; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + dest->seq = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen) { + (void)payload; + (void)payloadlen; + + dest->type = NGTCP2_FRAME_HANDSHAKE_DONE; + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, + ngtcp2_frame *fr) { + switch (fr->type) { + case NGTCP2_FRAME_STREAM: + return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream); + case NGTCP2_FRAME_ACK: + return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack); + case NGTCP2_FRAME_PADDING: + return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding); + case NGTCP2_FRAME_RESET_STREAM: + return ngtcp2_pkt_encode_reset_stream_frame(out, outlen, &fr->reset_stream); + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + return ngtcp2_pkt_encode_connection_close_frame(out, outlen, + &fr->connection_close); + case NGTCP2_FRAME_MAX_DATA: + return ngtcp2_pkt_encode_max_data_frame(out, outlen, &fr->max_data); + case NGTCP2_FRAME_MAX_STREAM_DATA: + return ngtcp2_pkt_encode_max_stream_data_frame(out, outlen, + &fr->max_stream_data); + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + return ngtcp2_pkt_encode_max_streams_frame(out, outlen, &fr->max_streams); + case NGTCP2_FRAME_PING: + return ngtcp2_pkt_encode_ping_frame(out, outlen, &fr->ping); + case NGTCP2_FRAME_DATA_BLOCKED: + return ngtcp2_pkt_encode_data_blocked_frame(out, outlen, &fr->data_blocked); + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + return ngtcp2_pkt_encode_stream_data_blocked_frame( + out, outlen, &fr->stream_data_blocked); + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + return ngtcp2_pkt_encode_streams_blocked_frame(out, outlen, + &fr->streams_blocked); + case NGTCP2_FRAME_NEW_CONNECTION_ID: + return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen, + &fr->new_connection_id); + case NGTCP2_FRAME_STOP_SENDING: + return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending); + case NGTCP2_FRAME_PATH_CHALLENGE: + return ngtcp2_pkt_encode_path_challenge_frame(out, outlen, + &fr->path_challenge); + case NGTCP2_FRAME_PATH_RESPONSE: + return ngtcp2_pkt_encode_path_response_frame(out, outlen, + &fr->path_response); + case NGTCP2_FRAME_CRYPTO: + return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto); + case NGTCP2_FRAME_NEW_TOKEN: + return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token); + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + return ngtcp2_pkt_encode_retire_connection_id_frame( + out, outlen, &fr->retire_connection_id); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_encode_handshake_done_frame(out, outlen, + &fr->handshake_done); + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } +} + +ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, + ngtcp2_stream *fr) { + size_t len = 1; + uint8_t flags = NGTCP2_STREAM_LEN_BIT; + uint8_t *p; + size_t i; + size_t datalen = 0; + + if (fr->fin) { + flags |= NGTCP2_STREAM_FIN_BIT; + } + + if (fr->offset) { + flags |= NGTCP2_STREAM_OFF_BIT; + len += ngtcp2_put_varint_len(fr->offset); + } + + len += ngtcp2_put_varint_len((uint64_t)fr->stream_id); + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + len += ngtcp2_put_varint_len(datalen); + len += datalen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = flags | NGTCP2_FRAME_STREAM; + + fr->flags = flags; + + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + + if (fr->offset) { + p = ngtcp2_put_varint(p, fr->offset); + } + + p = ngtcp2_put_varint(p, datalen); + + for (i = 0; i < fr->datacnt; ++i) { + assert(fr->data[i].len); + assert(fr->data[i].base); + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, + ngtcp2_ack *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->largest_ack) + + ngtcp2_put_varint_len(fr->ack_delay) + + ngtcp2_put_varint_len(fr->num_blks) + + ngtcp2_put_varint_len(fr->first_ack_blklen); + uint8_t *p; + size_t i; + const ngtcp2_ack_blk *blk; + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + len += ngtcp2_put_varint_len(blk->gap); + len += ngtcp2_put_varint_len(blk->blklen); + } + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_ACK; + p = ngtcp2_put_varint(p, (uint64_t)fr->largest_ack); + p = ngtcp2_put_varint(p, fr->ack_delay); + p = ngtcp2_put_varint(p, fr->num_blks); + p = ngtcp2_put_varint(p, fr->first_ack_blklen); + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + p = ngtcp2_put_varint(p, blk->gap); + p = ngtcp2_put_varint(p, blk->blklen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, + const ngtcp2_padding *fr) { + if (outlen < fr->len) { + return NGTCP2_ERR_NOBUF; + } + + memset(out, 0, fr->len); + + return (ngtcp2_ssize)fr->len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, + const ngtcp2_reset_stream *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->app_error_code) + + ngtcp2_put_varint_len(fr->final_size); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_RESET_STREAM; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->app_error_code); + p = ngtcp2_put_varint(p, fr->final_size); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, + const ngtcp2_connection_close *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->error_code) + + (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE + ? ngtcp2_put_varint_len(fr->frame_type) + : 0) + + ngtcp2_put_varint_len(fr->reasonlen) + fr->reasonlen; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->error_code); + if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { + p = ngtcp2_put_varint(p, fr->frame_type); + } + p = ngtcp2_put_varint(p, fr->reasonlen); + if (fr->reasonlen) { + p = ngtcp2_cpymem(p, fr->reason, fr->reasonlen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_data *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->max_data); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_MAX_DATA; + p = ngtcp2_put_varint(p, fr->max_data); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_stream_data *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->max_stream_data); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_MAX_STREAM_DATA; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->max_stream_data); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_streams *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->max_streams); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, + const ngtcp2_ping *fr) { + (void)fr; + + if (outlen < 1) { + return NGTCP2_ERR_NOBUF; + } + + *out++ = NGTCP2_FRAME_PING; + + return 1; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_data_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->offset); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_DATA_BLOCKED; + p = ngtcp2_put_varint(p, fr->offset); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( + uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->offset); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->offset); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_streams_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->stream_limit); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->stream_limit); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_connection_id *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->seq) + + ngtcp2_put_varint_len(fr->retire_prior_to) + 1 + + fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_NEW_CONNECTION_ID; + p = ngtcp2_put_varint(p, fr->seq); + p = ngtcp2_put_varint(p, fr->retire_prior_to); + *p++ = (uint8_t)fr->cid.datalen; + p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen); + p = ngtcp2_cpymem(p, fr->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, + const ngtcp2_stop_sending *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->app_error_code); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_STOP_SENDING; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->app_error_code); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_challenge *fr) { + size_t len = 1 + 8; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_PATH_CHALLENGE; + p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_response *fr) { + size_t len = 1 + 8; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_PATH_RESPONSE; + p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, + const ngtcp2_crypto *fr) { + size_t len = 1; + uint8_t *p; + size_t i; + size_t datalen = 0; + + len += ngtcp2_put_varint_len(fr->offset); + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + len += ngtcp2_put_varint_len(datalen); + len += datalen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_CRYPTO; + + p = ngtcp2_put_varint(p, fr->offset); + p = ngtcp2_put_varint(p, datalen); + + for (i = 0; i < fr->datacnt; ++i) { + assert(fr->data[i].base); + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_token *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->tokenlen) + fr->tokenlen; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_NEW_TOKEN; + + p = ngtcp2_put_varint(p, fr->tokenlen); + if (fr->tokenlen) { + p = ngtcp2_cpymem(p, fr->token, fr->tokenlen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( + uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->seq); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + + p = ngtcp2_put_varint(p, fr->seq); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr) { + (void)fr; + + if (outlen < 1) { + return NGTCP2_ERR_NOBUF; + } + + *out++ = NGTCP2_FRAME_HANDSHAKE_DONE; + + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( + uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, + size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, + size_t nsv) { + size_t len = 1 + 4 + 1 + dcidlen + 1 + scidlen + nsv * 4; + uint8_t *p; + size_t i; + + assert(dcidlen < 256); + assert(scidlen < 256); + + if (destlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = dest; + + *p++ = 0x80 | unused_random; + p = ngtcp2_put_uint32be(p, 0); + *p++ = (uint8_t)dcidlen; + if (dcidlen) { + p = ngtcp2_cpymem(p, dcid, dcidlen); + } + *p++ = (uint8_t)scidlen; + if (scidlen) { + p = ngtcp2_cpymem(p, scid, scidlen); + } + + for (i = 0; i < nsv; ++i) { + p = ngtcp2_put_uint32be(p, sv[i]); + } + + assert((size_t)(p - dest) == len); + + return (ngtcp2_ssize)len; +} + +size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *end = payload + payloadlen; + + assert((payloadlen % sizeof(uint32_t)) == 0); + + for (; payload != end; payload += sizeof(uint32_t)) { + *dest++ = ngtcp2_get_uint32(payload); + } + + return payloadlen / sizeof(uint32_t); +} + +int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *p = payload; + + if (payloadlen < + NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + sr->rand = p; + sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN; + p += sr->randlen; + memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); + + return 0; +} + +int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, + size_t payloadlen) { + size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN; + + if (payloadlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->token = payload; + dest->tokenlen = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN); + ngtcp2_cpymem(dest->tag, payload + dest->tokenlen, NGTCP2_RETRY_TAGLEN); + + return 0; +} + +int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, + size_t n) { + int64_t expected = max_pkt_num + 1; + int64_t win = (int64_t)1 << n; + int64_t hwin = win / 2; + int64_t mask = win - 1; + int64_t cand = (expected & ~mask) | pkt_num; + + if (cand <= expected - hwin) { + assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win); + return cand + win; + } + if (cand > expected + hwin && cand >= win) { + return cand - win; + } + return cand; +} + +int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) { + int64_t largest_ack = fr->largest_ack; + size_t i; + + if (largest_ack < (int64_t)fr->first_ack_blklen) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->first_ack_blklen; + + for (i = 0; i < fr->num_blks; ++i) { + if (largest_ack < (int64_t)fr->blks[i].gap + 2) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->blks[i].gap + 2; + + if (largest_ack < (int64_t)fr->blks[i].blklen) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->blks[i].blklen; + } + + return 0; +} + +ngtcp2_ssize +ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, + const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen) { + uint8_t *p; + + if (destlen < + NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { + return NGTCP2_ERR_NOBUF; + } + + if (randlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = dest; + + randlen = ngtcp2_min(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen); + + p = ngtcp2_cpymem(p, rand, randlen); + p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN); + *dest = (uint8_t)((*dest & 0x7fu) | 0x40u); + + return p - dest; +} + +static const uint8_t retry_key[] = + "\x4d\x32\xec\xdb\x2a\x21\x33\xc8\x41\xe4\x04\x3d\xf2\x7d\x44\x30"; +static const uint8_t retry_nonce[] = + "\x4d\x16\x11\xd0\x55\x13\xa5\x52\xc5\x87\xd5\x75"; + +ngtcp2_ssize +ngtcp2_pkt_write_retry(uint8_t *dest, size_t destlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen, + ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead) { + ngtcp2_pkt_hd hd; + uint8_t pseudo_retry[1500]; + ngtcp2_ssize pseudo_retrylen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + int rv; + uint8_t *p; + size_t offset; + + assert(tokenlen > 0); + assert(!ngtcp2_cid_eq(scid, odcid)); + + /* Retry packet is sent at most once per one connection attempt. In + the first connection attempt, client has to send random DCID + which is at least 8 bytes long. */ + if (odcid->datalen < 8) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid, + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, + NGTCP2_PROTO_VER, /* len = */ 0); + + pseudo_retrylen = + ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd, + /* unused = */ 0, odcid, token, tokenlen); + if (pseudo_retrylen < 0) { + return pseudo_retrylen; + } + + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(tag, aead, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(retry_nonce) - 1, pseudo_retry, (size_t)pseudo_retrylen); + if (rv != 0) { + return rv; + } + + offset = 1 + odcid->datalen; + if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) { + return NGTCP2_ERR_NOBUF; + } + + p = ngtcp2_cpymem(dest, pseudo_retry + offset, + (size_t)pseudo_retrylen - offset); + p = ngtcp2_cpymem(p, tag, sizeof(tag)); + + return p - dest; +} + +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) { + uint8_t *p = dest; + ngtcp2_ssize nwrite; + + if (destlen < 1 + odcid->datalen) { + return NGTCP2_ERR_NOBUF; + } + + *p++ = (uint8_t)odcid->datalen; + p = ngtcp2_cpymem(p, odcid->data, odcid->datalen); + destlen -= (size_t)(p - dest); + + nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd); + if (nwrite < 0) { + return nwrite; + } + + if (destlen < (size_t)nwrite + tokenlen) { + return NGTCP2_ERR_NOBUF; + } + + *p &= 0xf0; + *p |= unused; + + p += nwrite; + + p = ngtcp2_cpymem(p, token, tokenlen); + + return p - dest; +} + +int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead) { + uint8_t pseudo_retry[1500]; + size_t pseudo_retrylen; + uint8_t *p = pseudo_retry; + int rv; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + + assert(pktlen >= sizeof(retry->tag)); + + if (sizeof(pseudo_retry) < + 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) { + return NGTCP2_ERR_PROTO; + } + + *p++ = (uint8_t)retry->odcid.datalen; + p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen); + p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag)); + + pseudo_retrylen = (size_t)(p - pseudo_retry); + + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(tag, aead, (const uint8_t *)"", 0, retry_key, retry_nonce, + sizeof(retry_nonce) - 1, pseudo_retry, pseudo_retrylen); + if (rv != 0) { + return rv; + } + + if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) { + return NGTCP2_ERR_PROTO; + } + + return 0; +} + +size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, + size_t len, size_t left) { + size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + + (offset ? ngtcp2_put_varint_len(offset) : 0); + + if (left <= n) { + return (size_t)-1; + } + + left -= n; + + if (left > 8 + 1073741823 && len > 1073741823) { + len = ngtcp2_min(len, 4611686018427387903lu); + return ngtcp2_min(len, left - 8); + } + + if (left > 4 + 16383 && len > 16383) { + len = ngtcp2_min(len, 1073741823); + return ngtcp2_min(len, left - 4); + } + + if (left > 2 + 63 && len > 63) { + len = ngtcp2_min(len, 16383); + return ngtcp2_min(len, left - 2); + } + + len = ngtcp2_min(len, 63); + return ngtcp2_min(len, left - 1); +} + +size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { + size_t n = 1 /* type */ + ngtcp2_put_varint_len(offset); + + if (left <= n) { + return (size_t)-1; + } + + left -= n; + + if (left > 8 + 1073741823 && len > 1073741823) { + len = ngtcp2_min(len, 4611686018427387903lu); + return ngtcp2_min(len, left - 8); + } + + if (left > 4 + 16383 && len > 16383) { + len = ngtcp2_min(len, 1073741823); + return ngtcp2_min(len, left - 4); + } + + if (left > 2 + 63 && len > 63) { + len = ngtcp2_min(len, 16383); + return ngtcp2_min(len, left - 2); + } + + len = ngtcp2_min(len, 63); + return ngtcp2_min(len, left - 1); +} + +uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { + return (c & NGTCP2_LONG_TYPE_MASK) >> 4; +} + +int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { + if (c & NGTCP2_HEADER_FORM_BIT) { + return (c & NGTCP2_LONG_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; + } + return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; +} diff --git a/deps/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/lib/ngtcp2_pkt.h new file mode 100644 index 00000000000000..962218057afcca --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pkt.h @@ -0,0 +1,1118 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PKT_H +#define NGTCP2_PKT_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* QUIC header macros */ +#define NGTCP2_HEADER_FORM_BIT 0x80 +#define NGTCP2_FIXED_BIT_MASK 0x40 +#define NGTCP2_PKT_NUMLEN_MASK 0x03 + +/* Long header specific macros */ +#define NGTCP2_LONG_TYPE_MASK 0x30 +#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c + +/* Short header specific macros */ +#define NGTCP2_SHORT_SPIN_BIT_MASK 0x20 +#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18 +#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04 + +/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */ +#define NGTCP2_SR_TYPE 0x1f + +/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header. + That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> + + LENGTH<1> + PKN<1> */ +#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1 + 1) + +#define NGTCP2_STREAM_FIN_BIT 0x01 +#define NGTCP2_STREAM_LEN_BIT 0x02 +#define NGTCP2_STREAM_OFF_BIT 0x04 + +/* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required + other than payload for STREAM frame. That is from type field to + the beginning of the payload. */ +#define NGTCP2_STREAM_OVERHEAD (1 + 8 + 8 + 8) + +/* NGTCP2_CRYPTO_OVERHEAD is the maximum number of bytes required + other than payload for CRYPTO frame. That is from type field to + the beginning of the payload. */ +#define NGTCP2_CRYPTO_OVERHEAD (1 + 8 + 8) + +/* NGTCP2_MIN_FRAME_PAYLOADLEN is the minimum frame payload length. */ +#define NGTCP2_MIN_FRAME_PAYLOADLEN 16 + +/* NGTCP2_MAX_VARINT is the maximum value which can be encoded in + variable-length integer encoding */ +#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1) + +/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional + server stream ID. */ +#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll) +/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional + client stream ID. */ +#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll) +/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional + server stream ID. */ +#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll) +/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional + client stream ID. */ +#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell) + +/* NGTCP2_MAX_NUM_ACK_BLK is the maximum number of Additional ACK + blocks which this library can create, or decode. */ +#define NGTCP2_MAX_ACK_BLKS 255 + +/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ +#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1)) + +/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion in + addition to the minimum DCID length to hide/trigger Stateless + Reset. */ +#define NGTCP2_MIN_PKT_EXPANDLEN 22 + +typedef enum { + NGTCP2_FRAME_PADDING = 0x00, + NGTCP2_FRAME_PING = 0x01, + NGTCP2_FRAME_ACK = 0x02, + NGTCP2_FRAME_ACK_ECN = 0x03, + NGTCP2_FRAME_RESET_STREAM = 0x04, + NGTCP2_FRAME_STOP_SENDING = 0x05, + NGTCP2_FRAME_CRYPTO = 0x06, + NGTCP2_FRAME_NEW_TOKEN = 0x07, + NGTCP2_FRAME_STREAM = 0x08, + NGTCP2_FRAME_MAX_DATA = 0x10, + NGTCP2_FRAME_MAX_STREAM_DATA = 0x11, + NGTCP2_FRAME_MAX_STREAMS_BIDI = 0x12, + NGTCP2_FRAME_MAX_STREAMS_UNI = 0x13, + NGTCP2_FRAME_DATA_BLOCKED = 0x14, + NGTCP2_FRAME_STREAM_DATA_BLOCKED = 0x15, + NGTCP2_FRAME_STREAMS_BLOCKED_BIDI = 0x16, + NGTCP2_FRAME_STREAMS_BLOCKED_UNI = 0x17, + NGTCP2_FRAME_NEW_CONNECTION_ID = 0x18, + NGTCP2_FRAME_RETIRE_CONNECTION_ID = 0x19, + NGTCP2_FRAME_PATH_CHALLENGE = 0x1a, + NGTCP2_FRAME_PATH_RESPONSE = 0x1b, + NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c, + NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d, + NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e, +} ngtcp2_frame_type; + +typedef struct { + uint8_t type; + /** + * flags of decoded STREAM frame. This gets ignored when encoding + * STREAM frame. + */ + uint8_t flags; + uint8_t fin; + int64_t stream_id; + uint64_t offset; + /* datacnt is the number of elements that data contains. Although + the length of data is 1 in this definition, the library may + allocate extra bytes to hold more elements. */ + size_t datacnt; + /* data is the array of ngtcp2_vec which references data. */ + ngtcp2_vec data[1]; +} ngtcp2_stream; + +typedef struct { + uint64_t gap; + uint64_t blklen; +} ngtcp2_ack_blk; + +typedef struct { + uint8_t type; + int64_t largest_ack; + uint64_t ack_delay; + /** + * ack_delay_unscaled is an ack_delay multiplied by + * 2**ack_delay_component * NGTCP2_DURATION_TICK / + * NGTCP2_MICROSECONDS. + */ + ngtcp2_duration ack_delay_unscaled; + uint64_t first_ack_blklen; + size_t num_blks; + ngtcp2_ack_blk blks[1]; +} ngtcp2_ack; + +typedef struct { + uint8_t type; + /** + * The length of contiguous PADDING frames. + */ + size_t len; +} ngtcp2_padding; + +typedef struct { + uint8_t type; + int64_t stream_id; + uint64_t app_error_code; + uint64_t final_size; +} ngtcp2_reset_stream; + +typedef struct { + uint8_t type; + uint64_t error_code; + uint64_t frame_type; + size_t reasonlen; + uint8_t *reason; +} ngtcp2_connection_close; + +typedef struct { + uint8_t type; + /** + * max_data is Maximum Data. + */ + uint64_t max_data; +} ngtcp2_max_data; + +typedef struct { + uint8_t type; + int64_t stream_id; + uint64_t max_stream_data; +} ngtcp2_max_stream_data; + +typedef struct { + uint8_t type; + uint64_t max_streams; +} ngtcp2_max_streams; + +typedef struct { + uint8_t type; +} ngtcp2_ping; + +typedef struct { + uint8_t type; + uint64_t offset; +} ngtcp2_data_blocked; + +typedef struct { + uint8_t type; + int64_t stream_id; + uint64_t offset; +} ngtcp2_stream_data_blocked; + +typedef struct { + uint8_t type; + uint64_t stream_limit; +} ngtcp2_streams_blocked; + +typedef struct { + uint8_t type; + uint64_t seq; + uint64_t retire_prior_to; + ngtcp2_cid cid; + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_new_connection_id; + +typedef struct { + uint8_t type; + int64_t stream_id; + uint64_t app_error_code; +} ngtcp2_stop_sending; + +typedef struct { + uint8_t type; + uint8_t data[8]; +} ngtcp2_path_challenge; + +typedef struct { + uint8_t type; + uint8_t data[8]; +} ngtcp2_path_response; + +typedef struct { + uint8_t type; + uint64_t offset; + /* datacnt is the number of elements that data contains. Although + the length of data is 1 in this definition, the library may + allocate extra bytes to hold more elements. */ + size_t datacnt; + /* data is the array of ngtcp2_vec which references data. */ + ngtcp2_vec data[1]; +} ngtcp2_crypto; + +typedef struct { + uint8_t type; + size_t tokenlen; + const uint8_t *token; +} ngtcp2_new_token; + +typedef struct { + uint8_t type; + uint64_t seq; +} ngtcp2_retire_connection_id; + +typedef struct { + uint8_t type; +} ngtcp2_handshake_done; + +typedef union { + uint8_t type; + ngtcp2_stream stream; + ngtcp2_ack ack; + ngtcp2_padding padding; + ngtcp2_reset_stream reset_stream; + ngtcp2_connection_close connection_close; + ngtcp2_max_data max_data; + ngtcp2_max_stream_data max_stream_data; + ngtcp2_max_streams max_streams; + ngtcp2_ping ping; + ngtcp2_data_blocked data_blocked; + ngtcp2_stream_data_blocked stream_data_blocked; + ngtcp2_streams_blocked streams_blocked; + ngtcp2_new_connection_id new_connection_id; + ngtcp2_stop_sending stop_sending; + ngtcp2_path_challenge path_challenge; + ngtcp2_path_response path_response; + ngtcp2_crypto crypto; + ngtcp2_new_token new_token; + ngtcp2_retire_connection_id retire_connection_id; + ngtcp2_handshake_done handshake_done; +} ngtcp2_frame; + +struct ngtcp2_pkt_chain; +typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain; + +/* + * ngtcp2_pkt_chain is the chain of incoming packets buffered. + */ +struct ngtcp2_pkt_chain { + ngtcp2_path_storage path; + ngtcp2_pkt_chain *next; + uint8_t *pkt; + size_t pktlen; + ngtcp2_tstamp ts; +}; + +/* + * ngtcp2_pkt_chain_new allocates ngtcp2_pkt_chain objects, and + * assigns its pointer to |*ppc|. The content of buffer pointed by + * |pkt| of length |pktlen| is copied into |*ppc|. The packet is + * obtained via the network |path|. The values of path->local and + * path->remote are copied into |*ppc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, + const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pkt_chain_del deallocates |pc|. It also frees the memory + * pointed by |pc|. + */ +void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem); + +/* + * ngtcp2_pkt_hd_init initializes |hd| with the given values. If + * |dcid| and/or |scid| is NULL, DCID and SCID of |hd| is empty + * respectively. |pkt_numlen| is the number of bytes used to encode + * |pkt_num| and either 1, 2, or 4. |version| is QUIC version for + * long header. |len| is the length field of Initial, 0RTT, and + * Handshake packets. + */ +void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, + const ngtcp2_cid *dcid, const ngtcp2_cid *scid, + int64_t pkt_num, size_t pkt_numlen, uint32_t version, + size_t len); + +/* + * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into + * |out| which has length |outlen|. It returns the number of bytes + * written into |outlen| if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too short + */ +ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_pkt_encode_hd_short encodes |hd| as QUIC short header into + * |out| which has length |outlen|. It returns the number of bytes + * written into |outlen| if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too short + */ +ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd); + +/** + * @function + * + * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer + * pointed by |payload| whose length is |payloadlen|. + * + * This function returns the number of bytes read to decode a single + * frame if it succeeds, or one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_FRAME_ENCODING` + * Frame is badly formatted; or frame type is unknown. + */ +ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, + size_t payloadlen); + +/** + * @function + * + * `ngtcp2_pkt_encode_frame` encodes a frame |fm| into the buffer + * pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, + ngtcp2_frame *fr); + +/* + * ngtcp2_pkt_decode_version_negotiation decodes Version Negotiation + * packet payload |payload| of length |payloadlen|, and stores the + * result in |dest|. |dest| must have enough capacity to store the + * result. |payloadlen| also must be a multiple of sizeof(uint32_t). + * + * This function returns the number of versions written in |dest|. + */ +size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stateless_reset decodes Stateless Reset payload + * |payload| of length |payloadlen|. The |payload| must start with + * Stateless Reset Token. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Payloadlen is too short. + */ +int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of + * length |payloadlen|. The |payload| must start with Retry token + * field. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Payloadlen is too short. + */ +int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stream_frame decodes STREAM frame from |payload| + * of length |payloadlen|. The result is stored in the object pointed + * by |dest|. STREAM frame must start at payload[0]. This function + * finishes when it decodes one STREAM frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_ack_frame decodes ACK frame from |payload| of + * length |payloadlen|. The result is stored in the object pointed by + * |dest|. ACK frame must start at payload[0]. This function + * finishes when it decodes one ACK frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include ACK frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_padding_frame decodes contiguous PADDING frames + * from |payload| of length |payloadlen|. It continues to parse + * frames as long as the frame type is PADDING. This finishes when it + * encounters the frame type which is not PADDING, or all input data + * is read. The first byte (payload[0]) must be NGTCP2_FRAME_PADDING. + * This function returns the exact number of bytes read to decode + * PADDING frames. + */ +size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_reset_stream_frame decodes RESET_STREAM frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. RESET_STREAM frame must start at + * payload[0]. This function finishes when it decodes one + * RESET_STREAM frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include RESET_STREAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_connection_close_frame decodes CONNECTION_CLOSE + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. CONNECTION_CLOSE frame must start + * at payload[0]. This function finishes it decodes one + * CONNECTION_CLOSE frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include CONNECTION_CLOSE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( + ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_data_frame decodes MAX_DATA frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. MAX_DATA frame must start at payload[0]. + * This function finishes when it decodes one MAX_DATA frame, and + * returns the exact number of bytes read to decode a frame if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_DATA frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_stream_data_frame decodes MAX_STREAM_DATA + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. MAX_STREAM_DATA frame must start + * at payload[0]. This function finishes when it decodes one + * MAX_STREAM_DATA frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_STREAM_DATA frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( + ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_streams_frame decodes MAX_STREAMS frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. MAX_STREAMS frame must start at + * payload[0]. This function finishes when it decodes one MAX_STREAMS + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_STREAMS frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_ping_frame decodes PING frame from |payload| of + * length |payloadlen|. The result is stored in the object pointed by + * |dest|. PING frame must start at payload[0]. This function + * finishes when it decodes one PING frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PING frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_data_blocked_frame decodes DATA_BLOCKED frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. DATA_BLOCKED frame must start at + * payload[0]. This function finishes when it decodes one + * DATA_BLOCKED frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include DATA_BLOCKED frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stream_data_blocked_frame decodes + * STREAM_DATA_BLOCKED frame from |payload| of length |payloadlen|. + * The result is stored in the object pointed by |dest|. + * STREAM_DATA_BLOCKED frame must start at payload[0]. This function + * finishes when it decodes one STREAM_DATA_BLOCKED frame, and returns + * the exact number of bytes read to decode a frame if it succeeds, or + * one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAM_DATA_BLOCKED frame. + */ +ngtcp2_ssize +ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_streams_blocked_frame decodes STREAMS_BLOCKED + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. STREAMS_BLOCKED frame must start + * at payload[0]. This function finishes when it decodes one + * STREAMS_BLOCKED frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAMS_BLOCKED frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( + ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_new_connection_id_frame decodes NEW_CONNECTION_ID + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. NEW_CONNECTION_ID frame must + * start at payload[0]. This function finishes when it decodes one + * NEW_CONNECTION_ID frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include NEW_CONNECTION_ID frame; or the + * length of CID is strictly less than NGTCP2_MIN_CIDLEN or + * greater than NGTCP2_MAX_CIDLEN. + */ +ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( + ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. STOP_SENDING frame must start at + * payload[0]. This function finishes when it decodes one + * STOP_SENDING frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STOP_SENDING frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_path_challenge_frame decodes PATH_CHALLENGE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. PATH_CHALLENGE frame must start at + * payload[0]. This function finishes when it decodes one + * PATH_CHALLENGE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PATH_CHALLENGE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_path_response_frame decodes PATH_RESPONSE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. PATH_RESPONSE frame must start at + * payload[0]. This function finishes when it decodes one + * PATH_RESPONSE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PATH_RESPONSE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_crypto_frame decodes CRYPTO frame from |payload| + * of length |payloadlen|. The result is stored in the object pointed + * by |dest|. CRYPTO frame must start at payload[0]. This function + * finishes when it decodes one CRYPTO frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include CRYPTO frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. NEW_TOKEN frame must start at + * payload[0]. This function finishes when it decodes one NEW_TOKEN + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include NEW_TOKEN frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_retire_connection_id_frame decodes RETIRE_CONNECTION_ID + * frame from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. RETIRE_CONNECTION_ID frame must start at + * payload[0]. This function finishes when it decodes one RETIRE_CONNECTION_ID + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include RETIRE_CONNECTION_ID frame. + */ +ngtcp2_ssize +ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. HANDSHAKE_DONE frame must start at + * payload[0]. This function finishes when it decodes one + * HANDSHAKE_DONE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include HANDSHAKE_DONE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function assigns & + * ~NGTCP2_FRAME_STREAM to fr->flags. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, + ngtcp2_stream *fr); + +/* + * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer + * pointed by |out| of length |outlen|. + * + * This function assigns & + * ~NGTCP2_FRAME_ACK to fr->flags. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, + ngtcp2_ack *fr); + +/* + * ngtcp2_pkt_encode_padding_frame encodes PADDING frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function encodes consecutive fr->len PADDING frames. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write frame(s). + */ +ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, + const ngtcp2_padding *fr); + +/* + * ngtcp2_pkt_encode_reset_stream_frame encodes RESET_STREAM frame + * |fr| into the buffer pointed by |out| of length |buflen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, + const ngtcp2_reset_stream *fr); + +/* + * ngtcp2_pkt_encode_connection_close_frame encodes CONNECTION_CLOSE + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, + const ngtcp2_connection_close *fr); + +/* + * ngtcp2_pkt_encode_max_data_frame encodes MAX_DATA frame |fr| into + * the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_data *fr); + +/* + * ngtcp2_pkt_encode_max_stream_data_frame encodes MAX_STREAM_DATA + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_stream_data *fr); + +/* + * ngtcp2_pkt_encode_max_streams_frame encodes MAX_STREAMS + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_streams *fr); + +/* + * ngtcp2_pkt_encode_ping_frame encodes PING frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, + const ngtcp2_ping *fr); + +/* + * ngtcp2_pkt_encode_data_blocked_frame encodes DATA_BLOCKED frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_data_blocked *fr); + +/* + * ngtcp2_pkt_encode_stream_data_blocked_frame encodes + * STREAM_DATA_BLOCKED frame |fr| into the buffer pointed by |out| of + * length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( + uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr); + +/* + * ngtcp2_pkt_encode_streams_blocked_frame encodes STREAMS_BLOCKED + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_streams_blocked *fr); + +/* + * ngtcp2_pkt_encode_new_connection_id_frame encodes NEW_CONNECTION_ID + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_connection_id *fr); + +/* + * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, + const ngtcp2_stop_sending *fr); + +/* + * ngtcp2_pkt_encode_path_challenge_frame encodes PATH_CHALLENGE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_challenge *fr); + +/* + * ngtcp2_pkt_encode_path_response_frame encodes PATH_RESPONSE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_response *fr); + +/* + * ngtcp2_pkt_encode_crypto_frame encodes CRYPTO frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, + const ngtcp2_crypto *fr); + +/* + * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into + * the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_token *fr); + +/* + * ngtcp2_pkt_encode_retire_connection_id_frame encodes RETIRE_CONNECTION_ID + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( + uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr); + +/* + * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr); + +/* + * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for + * |pkt_num|, which is expected to be least significant |n| bits. The + * |max_pkt_num| is the highest successfully authenticated packet + * number. + */ +int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, + size_t n); + +/* + * ngtcp2_pkt_validate_ack checks that ack is malformed or not. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed + */ +int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr); + +/* + * ngtcp2_pkt_stream_max_datalen returns the maximum number of bytes + * which can be sent for stream denoted by |stream_id|. |offset| is + * an offset of within the stream. |len| is the estimated number of + * bytes to be sent. |left| is the size of buffer. If |left| is too + * small to write STREAM frame, this function returns (size_t)-1. + */ +size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, + size_t len, size_t left); + +/* + * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes + * which can be sent for crypto stream. |offset| is an offset of + * within the crypto stream. |len| is the estimated number of bytes + * to be sent. |left| is the size of buffer. If |left| is too small + * to write CRYPTO frame, this function returns (size_t)-1. + */ +size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left); + +/* + * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of + * the packet header has the correct reserved bits. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Reserved bits has wrong value. + */ +int ngtcp2_pkt_verify_reserved_bits(uint8_t c); + +/* + * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the + * buffer pointed by |dest| of length |destlen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_BUF + * Buffer is too short. + */ +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen); + +/* + * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer + * pointed by |pkt| of length |pktlen| must contain Retry packet + * including packet header. The odcid and tag fields of |retry| must + * be specified. |aead| must be AEAD_AES_128_GCM. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Verification failed. + */ +int ngtcp2_pkt_verify_retry_tag(const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead); + +#endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/lib/ngtcp2_ppe.c new file mode 100644 index 00000000000000..e4aab22ca33f83 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ppe.c @@ -0,0 +1,230 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ppe.h" + +#include +#include + +#include "ngtcp2_str.h" +#include "ngtcp2_conv.h" + +void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, + ngtcp2_crypto_cc *cc) { + ngtcp2_buf_init(&ppe->buf, out, outlen); + + ppe->hdlen = 0; + ppe->len_offset = 0; + ppe->pkt_num_offset = 0; + ppe->pkt_numlen = 0; + ppe->pkt_num = 0; + ppe->sample_offset = 0; + ppe->cc = cc; +} + +int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { + ngtcp2_ssize rv; + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(buf) < cc->aead_overhead) { + return NGTCP2_ERR_NOBUF; + } + + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + 1 + hd->scid.datalen; + if (hd->type == NGTCP2_PKT_INITIAL) { + ppe->len_offset += ngtcp2_put_varint_len(hd->tokenlen) + hd->tokenlen; + } + ppe->pkt_num_offset = ppe->len_offset + 2; + rv = ngtcp2_pkt_encode_hd_long( + buf->last, ngtcp2_buf_left(buf) - cc->aead_overhead, hd); + } else { + ppe->pkt_num_offset = 1 + hd->dcid.datalen; + rv = ngtcp2_pkt_encode_hd_short( + buf->last, ngtcp2_buf_left(buf) - cc->aead_overhead, hd); + } + if (rv < 0) { + return (int)rv; + } + + ppe->sample_offset = ppe->pkt_num_offset + 4; + + buf->last += rv; + + ppe->pkt_numlen = hd->pkt_numlen; + ppe->hdlen = (size_t)rv; + + ppe->pkt_num = hd->pkt_num; + + return 0; +} + +int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { + ngtcp2_ssize rv; + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(buf) < cc->aead_overhead) { + return NGTCP2_ERR_NOBUF; + } + + rv = ngtcp2_pkt_encode_frame(buf->last, + ngtcp2_buf_left(buf) - cc->aead_overhead, fr); + if (rv < 0) { + return (int)rv; + } + + buf->last += rv; + + return 0; +} + +ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + uint8_t *payload = buf->begin + ppe->hdlen; + size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen; + uint8_t mask[NGTCP2_HP_SAMPLELEN]; + uint8_t *p; + size_t i; + int rv; + + assert(cc->encrypt); + assert(cc->hp_mask); + + if (ppe->len_offset) { + ngtcp2_put_varint14( + buf->begin + ppe->len_offset, + (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead_overhead)); + } + + ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len, + ppe->pkt_num); + + rv = cc->encrypt(payload, &cc->aead, payload, payloadlen, cc->ckm->key.base, + ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + buf->last = payload + payloadlen + cc->aead_overhead; + + /* TODO Check that we have enough space to get sample */ + assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf)); + + rv = cc->hp_mask(mask, &cc->hp, cc->hp_key->base, + buf->begin + ppe->sample_offset); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + p = buf->begin; + if (*p & NGTCP2_HEADER_FORM_BIT) { + *p = (uint8_t)(*p ^ (mask[0] & 0x0f)); + } else { + *p = (uint8_t)(*p ^ (mask[0] & 0x1f)); + } + + p = buf->begin + ppe->pkt_num_offset; + for (i = 0; i < ppe->pkt_numlen; ++i) { + *(p + i) ^= mask[i + 1]; + } + + if (ppkt != NULL) { + *ppkt = buf->begin; + } + + return (ngtcp2_ssize)ngtcp2_buf_len(buf); +} + +size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(&ppe->buf) < cc->aead_overhead) { + return 0; + } + + return ngtcp2_buf_left(&ppe->buf) - cc->aead_overhead; +} + +size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + + return ngtcp2_buf_len(&ppe->buf) + cc->aead_overhead; +} + +size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t len; + + assert(ngtcp2_buf_left(buf) >= cc->aead_overhead); + + len = ngtcp2_buf_left(buf) - cc->aead_overhead; + memset(buf->last, 0, len); + buf->last += len; + + return len; +} + +size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t max_samplelen; + size_t len = 0; + + assert(cc->aead_overhead); + + max_samplelen = ngtcp2_buf_len(buf) + cc->aead_overhead - ppe->sample_offset; + if (max_samplelen < NGTCP2_HP_SAMPLELEN) { + len = NGTCP2_HP_SAMPLELEN - max_samplelen; + assert(ngtcp2_ppe_left(ppe) >= len); + memset(buf->last, 0, len); + buf->last += len; + } + + return len; +} + +size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t pktlen = ngtcp2_buf_len(buf) + cc->aead_overhead; + size_t len; + + if (pktlen >= n) { + return 0; + } + + len = n - pktlen; + buf->last = ngtcp2_setmem(buf->last, 0, len); + + return len; +} + +int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe) { + ngtcp2_buf *buf = &ppe->buf; + return ngtcp2_buf_left(buf) >= (4 - ppe->pkt_numlen) + NGTCP2_HP_SAMPLELEN; +} diff --git a/deps/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/lib/ngtcp2_ppe.h new file mode 100644 index 00000000000000..ca6b21dce0b41a --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ppe.h @@ -0,0 +1,153 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PPE_H +#define NGTCP2_PPE_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_crypto.h" + +/* + * ngtcp2_ppe is the Protected Packet Encoder. + */ +typedef struct { + ngtcp2_buf buf; + ngtcp2_crypto_cc *cc; + /* hdlen is the number of bytes for packet header written in buf. */ + size_t hdlen; + /* len_offset is the offset to Length field. */ + size_t len_offset; + /* pkt_num_offset is the offset to packet number field. */ + size_t pkt_num_offset; + /* pkt_numlen is the number of bytes used to encode a packet + number */ + size_t pkt_numlen; + /* sample_offset is the offset to sample for packet number + encryption. */ + size_t sample_offset; + /* pkt_num is the packet number written in buf. */ + int64_t pkt_num; + /* nonce is the buffer to store nonce. It should be equal or longer + than then length of IV. */ + uint8_t nonce[32]; +} ngtcp2_ppe; + +/* + * ngtcp2_ppe_init initializes |ppe| with the given buffer. + */ +void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, + ngtcp2_crypto_cc *cc); + +/* + * ngtcp2_ppe_encode_hd encodes |hd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * The buffer is too small. + */ +int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_ppe_encode_frame encodes |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * The buffer is too small. + */ +int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr); + +/* + * ngtcp2_ppe_final encrypts QUIC packet payload. If |**ppkt| is not + * NULL, the pointer to the packet is assigned to it. + * + * This function returns the length of QUIC packet, including header, + * and payload if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt); + +/* + * ngtcp2_ppe_left returns the number of bytes left to write + * additional frames. This does not count AEAD overhead. + */ +size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_pktlen returns the provisional packet length. It + * includes AEAD overhead. + */ +size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe); + +/** + * @function + * + * `ngtcp2_ppe_padding` encodes PADDING frames to the end of the + * buffer. This function returns the number of bytes padded. + */ +size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_padding_hp_sample adds PADDING frame if the current + * payload does not have enough space for header protection sample. + * This function should be called just before calling + * ngtcp2_ppe_final(). + * + * This function returns the number of bytes added as padding. + */ +size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC + * packet is at least |n| bytes long. If it is unable to add PADDING + * in that way, this function still adds PADDING frame as much as + * possible. This function should be called just before calling + * ngtcp2_ppe_final(). For Short packet, this function should be + * called instead of ngtcp2_ppe_padding_hp_sample. + * + * This function returns the number of bytes added as padding. + */ +size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n); + +/* + * ngtcp2_ppe_ensure_hp_sample returns nonzero if the buffer has + * enough space for header protection sample. This should be called + * right after packet header is written. + */ +int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe); + +#endif /* NGTCP2_PPE_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pq.c b/deps/ngtcp2/lib/ngtcp2_pq.c new file mode 100644 index 00000000000000..5e1003d7942e53 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pq.c @@ -0,0 +1,164 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pq.h" + +#include + +#include "ngtcp2_macro.h" + +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; +} + +void ngtcp2_pq_free(ngtcp2_pq *pq) { + ngtcp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(ngtcp2_pq *pq, size_t i, size_t j) { + ngtcp2_pq_entry *a = pq->q[i]; + ngtcp2_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(ngtcp2_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = ngtcp2_max(4, (pq->capacity * 2)); + + nq = ngtcp2_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(ngtcp2_pq_entry *)); + if (nq == NULL) { + return NGTCP2_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq) { + assert(pq->length); + return pq->q[0]; +} + +static void bubble_down(ngtcp2_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void ngtcp2_pq_pop(ngtcp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + ngtcp2_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int ngtcp2_pq_empty(ngtcp2_pq *pq) { return pq->length == 0; } + +size_t ngtcp2_pq_size(ngtcp2_pq *pq) { return pq->length; } + +int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_pq.h b/deps/ngtcp2/lib/ngtcp2_pq.h new file mode 100644 index 00000000000000..b7e4a77ba5bb70 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pq.h @@ -0,0 +1,126 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PQ_H +#define NGTCP2_PQ_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" + +/* Implementation of priority queue */ + +/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates + that an entry is not queued. Assigning this value to + ngtcp2_pq_entry.index can check that the entry is queued or not. */ +#define NGTCP2_PQ_BAD_INDEX SIZE_MAX + +typedef struct { + size_t index; +} ngtcp2_pq_entry; + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*ngtcp2_less)(const ngtcp2_pq_entry *lhs, + const ngtcp2_pq_entry *rhs); + +typedef struct { + /* The pointer to the pointer to the item stored */ + ngtcp2_pq_entry **q; + /* Memory allocator */ + const ngtcp2_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + ngtcp2_less less; +} ngtcp2_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + */ +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void ngtcp2_pq_free(ngtcp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. It is undefined if the + * queue is empty. + */ +ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void ngtcp2_pq_pop(ngtcp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int ngtcp2_pq_empty(ngtcp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t ngtcp2_pq_size(ngtcp2_pq *pq); + +typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item); + +#endif /* NGTCP2_PQ_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_psl.c b/deps/ngtcp2/lib/ngtcp2_psl.c new file mode 100644 index 00000000000000..54a8e894d503a3 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_psl.c @@ -0,0 +1,621 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_psl.h" + +#include +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +int ngtcp2_psl_init(ngtcp2_psl *psl, const ngtcp2_mem *mem) { + ngtcp2_psl_blk *head; + + psl->mem = mem; + psl->head = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_psl_blk)); + if (!psl->head) { + return NGTCP2_ERR_NOMEM; + } + psl->front = psl->head; + psl->n = 0; + + head = psl->head; + + head->next = NULL; + head->n = 1; + head->leaf = 1; + head->nodes[0].range.begin = UINT64_MAX; + head->nodes[0].range.end = UINT64_MAX; + head->nodes[0].data = NULL; + + return 0; +} + +/* + * free_blk frees |blk| recursively. + */ +static void free_blk(ngtcp2_psl_blk *blk, const ngtcp2_mem *mem) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + free_blk(blk->nodes[i].blk, mem); + } + } + + ngtcp2_mem_free(mem, blk); +} + +void ngtcp2_psl_free(ngtcp2_psl *psl) { + if (!psl) { + return; + } + + free_blk(psl->head, psl->mem); +} + +/* + * psl_split_blk splits |blk| into 2 ngtcp2_psl_blk objects. The new + * ngtcp2_psl_blk is always the "right" block. + * + * It returns the pointer to the ngtcp2_psl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static ngtcp2_psl_blk *psl_split_blk(ngtcp2_psl *psl, ngtcp2_psl_blk *blk) { + ngtcp2_psl_blk *rblk; + + rblk = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, &blk->nodes[blk->n - rblk->n], + sizeof(ngtcp2_psl_node) * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGTCP2_PSL_MIN_NBLK); + assert(rblk->n >= NGTCP2_PSL_MIN_NBLK); + + return rblk; +} + +/* + * psl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_split_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_blk *lblk = blk->nodes[i].blk, *rblk; + + rblk = psl_split_blk(psl, lblk); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + memmove(&blk->nodes[i + 2], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + blk->nodes[i + 1].blk = rblk; + + ++blk->n; + + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + blk->nodes[i + 1].range = rblk->nodes[rblk->n - 1].range; + + return 0; +} + +/* + * psl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_split_head(ngtcp2_psl *psl) { + ngtcp2_psl_blk *rblk = NULL, *lblk, *nhead = NULL; + + rblk = psl_split_blk(psl, psl->head); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + lblk = psl->head; + + nhead = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (nhead == NULL) { + ngtcp2_mem_free(psl->mem, rblk); + return NGTCP2_ERR_NOMEM; + } + nhead->next = NULL; + nhead->n = 2; + nhead->leaf = 0; + + nhead->nodes[0].range = lblk->nodes[lblk->n - 1].range; + nhead->nodes[0].blk = lblk; + nhead->nodes[1].range = rblk->nodes[rblk->n - 1].range; + nhead->nodes[1].blk = rblk; + + psl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose range is |range| with the + * associated |data| at the index of |i|. This function assumes that + * the number of nodes contained by |blk| is strictly less than + * NGTCP2_PSL_MAX_NBLK. + */ +static void insert_node(ngtcp2_psl_blk *blk, size_t i, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_node *node; + + assert(blk->n < NGTCP2_PSL_MAX_NBLK); + + memmove(&blk->nodes[i + 1], &blk->nodes[i], + sizeof(ngtcp2_psl_node) * (blk->n - i)); + + node = &blk->nodes[i]; + node->range = *range; + node->data = data; + + ++blk->n; +} + +static int range_intersect(const ngtcp2_range *a, const ngtcp2_range *b) { + return ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end); +} + +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + int rv; + + if (blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_head(psl); + if (rv != 0) { + return rv; + } + blk = psl->head; + } + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + assert(!range_intersect(&node->range, range)); + + if (blk->leaf) { + insert_node(blk, i, range, data); + ++psl->n; + if (it) { + ngtcp2_psl_it_init(it, blk, i); + } + return 0; + } + + if (node->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i); + if (rv != 0) { + return rv; + } + if (node->range.begin < range->begin) { + node = &blk->nodes[i + 1]; + } + } + + blk = node->blk; + } +} + +/* + * remove_node removes the node included in |blk| at the index of |i|. + */ +static void remove_node(ngtcp2_psl_blk *blk, size_t i) { + memmove(&blk->nodes[i], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * psl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |psl| by 1. + * + * This function returns the pointer to the merged block. + */ +static ngtcp2_psl_blk *psl_merge_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, + size_t i) { + ngtcp2_psl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = blk->nodes[i].blk; + rblk = blk->nodes[i + 1].blk; + + assert(lblk->n + rblk->n < NGTCP2_PSL_MAX_NBLK); + + memcpy(&lblk->nodes[lblk->n], &rblk->nodes[0], + sizeof(ngtcp2_psl_node) * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + + ngtcp2_mem_free(psl->mem, rblk); + + if (psl->head == blk && blk->n == 2) { + ngtcp2_mem_free(psl->mem, psl->head); + psl->head = lblk; + } else { + remove_node(blk, i + 1); + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + } + + return lblk; +} + +/* + * psl_relocate_node replaces the key at the index |*pi| in + * *pblk->nodes with something other without violating contract. It + * might involve merging 2 nodes or moving a node to left or right. + * + * It assigns the index of the block in |*pblk| where the node is + * moved to |*pi|. If merging 2 nodes occurs and it becomes new head, + * the new head is assigned to |*pblk| and it still contains the key. + * The caller should handle this situation. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_relocate_node(ngtcp2_psl *psl, ngtcp2_psl_blk **pblk, + size_t *pi) { + ngtcp2_psl_blk *blk = *pblk; + size_t i = *pi; + ngtcp2_psl_node *node = &blk->nodes[i]; + ngtcp2_psl_node *rnode = &blk->nodes[i + 1]; + size_t j; + int rv; + + assert(i + 1 < blk->n); + + if (node->blk->n == NGTCP2_PSL_MIN_NBLK && + node->blk->n + rnode->blk->n < NGTCP2_PSL_MAX_NBLK) { + j = node->blk->n - 1; + blk = psl_merge_node(psl, blk, i); + if (blk != psl->head) { + return 0; + } + *pblk = blk; + i = j; + if (blk->leaf) { + *pi = i; + return 0; + } + node = &blk->nodes[i]; + rnode = &blk->nodes[i + 1]; + + if (node->blk->n == NGTCP2_PSL_MIN_NBLK && + node->blk->n + rnode->blk->n < NGTCP2_PSL_MAX_NBLK) { + j = node->blk->n - 1; + blk = psl_merge_node(psl, blk, i); + assert(blk != psl->head); + *pi = j; + return 0; + } + } + + if (node->blk->n < rnode->blk->n) { + node->blk->nodes[node->blk->n] = rnode->blk->nodes[0]; + memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1], + sizeof(ngtcp2_psl_node) * (rnode->blk->n - 1)); + --rnode->blk->n; + ++node->blk->n; + node->range = node->blk->nodes[node->blk->n - 1].range; + *pi = i; + return 0; + } + + if (rnode->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i + 1); + if (rv != 0) { + return rv; + } + } + + memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0], + sizeof(ngtcp2_psl_node) * rnode->blk->n); + + rnode->blk->nodes[0] = node->blk->nodes[node->blk->n - 1]; + ++rnode->blk->n; + + --node->blk->n; + + node->range = node->blk->nodes[node->blk->n - 1].range; + + *pi = i + 1; + return 0; +} + +/* + * shift_left moves the first node in blk->nodes[i]->blk->nodes to + * blk->nodes[i - 1]->blk->nodes. + */ +static void shift_left(ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_node *lnode, *rnode; + + assert(i > 0); + + lnode = &blk->nodes[i - 1]; + rnode = &blk->nodes[i]; + + assert(lnode->blk->n < NGTCP2_PSL_MAX_NBLK); + assert(rnode->blk->n > NGTCP2_PSL_MIN_NBLK); + + lnode->blk->nodes[lnode->blk->n] = rnode->blk->nodes[0]; + lnode->range = lnode->blk->nodes[lnode->blk->n].range; + ++lnode->blk->n; + + --rnode->blk->n; + memmove(&rnode->blk->nodes[0], &rnode->blk->nodes[1], + sizeof(ngtcp2_psl_node) * rnode->blk->n); +} + +/* + * shift_right moves the last node in blk->nodes[i]->blk->nodes to + * blk->nodes[i + 1]->blk->nodes. + */ +static void shift_right(ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_node *lnode, *rnode; + + assert(i < blk->n - 1); + + lnode = &blk->nodes[i]; + rnode = &blk->nodes[i + 1]; + + assert(lnode->blk->n > NGTCP2_PSL_MIN_NBLK); + assert(rnode->blk->n < NGTCP2_PSL_MAX_NBLK); + + memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0], + sizeof(ngtcp2_psl_node) * rnode->blk->n); + ++rnode->blk->n; + rnode->blk->nodes[0] = lnode->blk->nodes[lnode->blk->n - 1]; + + --lnode->blk->n; + lnode->range = lnode->blk->nodes[lnode->blk->n - 1].range; +} + +int ngtcp2_psl_remove(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head, *lblk, *rblk; + ngtcp2_psl_node *node; + size_t i, j; + int rv; + + if (!blk->leaf && blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_head(psl); + if (rv != 0) { + return rv; + } + blk = psl->head; + } + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + if (blk->leaf) { + assert(i < blk->n); + remove_node(blk, i); + --psl->n; + if (it) { + if (blk->n == i) { + ngtcp2_psl_it_init(it, blk->next, 0); + } else { + ngtcp2_psl_it_init(it, blk, i); + } + } + return 0; + } + + if (node->blk->n == NGTCP2_PSL_MAX_NBLK) { + rv = psl_split_node(psl, blk, i); + if (rv != 0) { + return rv; + } + if (node->range.begin < range->begin) { + ++i; + node = &blk->nodes[i]; + } + } + + if (ngtcp2_range_eq(&node->range, range)) { + rv = psl_relocate_node(psl, &blk, &i); + if (rv != 0) { + return rv; + } + if (!blk->leaf) { + node = &blk->nodes[i]; + blk = node->blk; + } + } else if (node->blk->n == NGTCP2_PSL_MIN_NBLK) { + j = i == 0 ? 0 : i - 1; + + lblk = blk->nodes[j].blk; + rblk = blk->nodes[j + 1].blk; + + if (lblk->n + rblk->n < NGTCP2_PSL_MAX_NBLK) { + blk = psl_merge_node(psl, blk, j); + } else { + if (i == j) { + shift_left(blk, j + 1); + } else { + shift_right(blk, j); + } + blk = node->blk; + } + } else { + blk = node->blk; + } + } +} + +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin && + !range_intersect(&node->range, range); + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + ngtcp2_psl_it it = {blk, i}; + return it; + } + + blk = node->blk; + } +} + +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + assert(old_range->begin <= new_range->begin); + assert(new_range->end <= old_range->end); + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < old_range->begin; + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + assert(ngtcp2_range_eq(&node->range, old_range)); + node->range = *new_range; + return; + } + + if (ngtcp2_range_eq(&node->range, old_range)) { + node->range = *new_range; + } else { + assert(!range_intersect(&node->range, old_range)); + } + + blk = node->blk; + } +} + +static void psl_print(ngtcp2_psl *psl, const ngtcp2_psl_blk *blk, + size_t level) { + size_t i; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + fprintf(stderr, " [%" PRIu64 ", %" PRIu64 ")", blk->nodes[i].range.begin, + blk->nodes[i].range.end); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + psl_print(psl, blk->nodes[i].blk, level + 1); + } +} + +void ngtcp2_psl_print(ngtcp2_psl *psl) { psl_print(psl, psl->head, 0); } + +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl) { + ngtcp2_psl_it it = {psl->front, 0}; + return it; +} + +size_t ngtcp2_psl_len(ngtcp2_psl *psl) { return psl->n; } + +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, + size_t i) { + it->blk = blk; + it->i = i; +} + +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it) { + return it->blk->nodes[it->i].data; +} + +void ngtcp2_psl_it_next(ngtcp2_psl_it *it) { + assert(!ngtcp2_psl_it_end(it)); + + if (++it->i == it->blk->n) { + it->blk = it->blk->next; + it->i = 0; + } +} + +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it) { + ngtcp2_range end = {UINT64_MAX, UINT64_MAX}; + return ngtcp2_range_eq(&end, &it->blk->nodes[it->i].range); +} + +ngtcp2_range ngtcp2_psl_it_range(const ngtcp2_psl_it *it) { + return it->blk->nodes[it->i].range; +} diff --git a/deps/ngtcp2/lib/ngtcp2_psl.h b/deps/ngtcp2/lib/ngtcp2_psl.h new file mode 100644 index 00000000000000..f0310a87e41bab --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_psl.h @@ -0,0 +1,231 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PSL_H +#define NGTCP2_PSL_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include + +#include "ngtcp2_range.h" + +/* + * Skip List implementation inspired by + * https://github.com/jabr/olio/blob/master/skiplist.c + */ + +#define NGTCP2_PSL_DEGR 8 +/* NGTCP2_PSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGTCP2_PSL_MAX_NBLK (2 * NGTCP2_PSL_DEGR - 1) +/* NGTCP2_PSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGTCP2_PSL_MIN_NBLK (NGTCP2_PSL_DEGR - 1) + +struct ngtcp2_psl_node; +typedef struct ngtcp2_psl_node ngtcp2_psl_node; + +struct ngtcp2_psl_blk; +typedef struct ngtcp2_psl_blk ngtcp2_psl_blk; + +/* + * ngtcp2_psl_node is a node which contains either ngtcp2_psl_blk or + * opaque data. If a node is an internal node, it contains + * ngtcp2_psl_blk. Otherwise, it has data. The invariant is that the + * range of internal node dictates the maximum range in its + * descendants, and the corresponding leaf node must exist. + */ +struct ngtcp2_psl_node { + ngtcp2_range range; + union { + ngtcp2_psl_blk *blk; + void *data; + }; +}; + +/* + * ngtcp2_psl_blk contains ngtcp2_psl_node objects. + */ +struct ngtcp2_psl_blk { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_psl_blk *next; + /* n is the number of nodes this object contains in nodes. */ + size_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + int leaf; + ngtcp2_psl_node nodes[NGTCP2_PSL_MAX_NBLK]; +}; + +struct ngtcp2_psl_it; +typedef struct ngtcp2_psl_it ngtcp2_psl_it; + +/* + * ngtcp2_psl_it is a forward iterator to iterate nodes. + */ +struct ngtcp2_psl_it { + const ngtcp2_psl_blk *blk; + size_t i; +}; + +struct ngtcp2_psl; +typedef struct ngtcp2_psl ngtcp2_psl; + +/* + * ngtcp2_psl is a deterministic paged skip list. + */ +struct ngtcp2_psl { + /* head points to the root block. */ + ngtcp2_psl_blk *head; + /* front points to the first leaf block. */ + ngtcp2_psl_blk *front; + size_t n; + const ngtcp2_mem *mem; +}; + +/* + * ngtcp2_psl_init initializes |psl|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_init(ngtcp2_psl *psl, const ngtcp2_mem *mem); + +/* + * ngtcp2_psl_free frees resources allocated for |psl|. If |psl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |psl| itself. + */ +void ngtcp2_psl_free(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_insert inserts |range| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function assumes that the existing ranges do not intersect + * with |range|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data); + +/* + * ngtcp2_psl_remove removes the |range| from |psl|. It assumes such + * the range is included in |psl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_remove(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range); + +/* + * ngtcp2_psl_update_range replaces the range of nodes which has + * |old_range| with |new_range|. |old_range| must include + * |new_range|. + */ +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range); + +/* + * ngtcp2_psl_lower_bound returns the iterator which points to the + * first node whose range intersects with |range|. If there is no + * such node, it returns the iterator which satisfies + * ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range); + +/* + * ngtcp2_psl_begin returns the iterator which points to the first + * node. If there is no node in |psl|, it returns the iterator which + * satisfies ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl); + +/* + * ngtcp2_psl_len returns the number of elements stored in |ksl|. + */ +size_t ngtcp2_psl_len(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_print prints its internal state in stderr. This + * function should be used for the debugging purpose only. + */ +void ngtcp2_psl_print(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_it_init initializes |it|. + */ +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, size_t i); + +/* + * ngtcp2_psl_it_get returns the data associated to the node which + * |it| points to. If this function is called when + * ngtcp2_psl_it_end(it) returns nonzero, it returns NULL. + */ +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_next advances the iterator by one. It is undefined + * if this function is called when ngtcp2_psl_it_end(it) returns + * nonzero. + */ +void ngtcp2_psl_it_next(ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_range returns the range of the node which |it| points + * to. It is OK to call this function when ngtcp2_psl_it_end(it) + * returns nonzero. In this case, this function returns {UINT64_MAX, + * UINT64_MAX}. + */ +ngtcp2_range ngtcp2_psl_it_range(const ngtcp2_psl_it *it); + +#endif /* NGTCP2_PSL_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/lib/ngtcp2_pv.c new file mode 100644 index 00000000000000..171f02dc43dd29 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pv.c @@ -0,0 +1,155 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pv.h" + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_addr.h" + +void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, + ngtcp2_tstamp expiry) { + memcpy(pvent->data, data, sizeof(pvent->data)); + pvent->expiry = expiry; +} + +int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, + ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, + const ngtcp2_mem *mem) { + int rv; + + (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); + if (*ppv == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, + sizeof(ngtcp2_pv_entry), mem); + if (rv != 0) { + ngtcp2_mem_free(mem, *ppv); + return 0; + } + + ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); + + (*ppv)->mem = mem; + (*ppv)->log = log; + (*ppv)->timeout = timeout; + (*ppv)->started_ts = 0; + (*ppv)->loss_count = 0; + (*ppv)->flags = flags; + + return 0; +} + +void ngtcp2_pv_del(ngtcp2_pv *pv) { + if (pv == NULL) { + return; + } + ngtcp2_ringbuf_free(&pv->ents); + ngtcp2_mem_free(pv->mem, pv); +} + +void ngtcp2_pv_ensure_start(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + if (pv->started_ts) { + return; + } + pv->started_ts = ts; +} + +void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, + ngtcp2_tstamp expiry) { + ngtcp2_pv_entry *ent = ngtcp2_ringbuf_push_back(&pv->ents); + ngtcp2_pv_entry_init(ent, data, expiry); +} + +int ngtcp2_pv_full(ngtcp2_pv *pv) { return ngtcp2_ringbuf_full(&pv->ents); } + +int ngtcp2_pv_validate(ngtcp2_pv *pv, const uint8_t *data) { + size_t len = ngtcp2_ringbuf_len(&pv->ents); + size_t i; + ngtcp2_pv_entry *ent; + + if (len == 0) { + return NGTCP2_ERR_INVALID_STATE; + } + + for (i = 0; i < len; ++i) { + ent = ngtcp2_ringbuf_get(&pv->ents, i); + if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { + ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); + return 0; + } + } + + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + ngtcp2_pv_entry *ent; + + if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + return; + } + + ent = ngtcp2_ringbuf_get(&pv->ents, 0); + if (ent->expiry <= ts) { + ++pv->loss_count; + + ngtcp2_ringbuf_pop_front(&pv->ents); + + for (; ngtcp2_ringbuf_len(&pv->ents);) { + ent = ngtcp2_ringbuf_get(&pv->ents, 0); + if (ent->expiry <= ts) { + ngtcp2_ringbuf_pop_front(&pv->ents); + continue; + } + break; + } + } +} + +int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + return pv->started_ts + pv->timeout <= ts; +} + +ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { + ngtcp2_tstamp t = UINT64_MAX; + ngtcp2_pv_entry *ent; + + if (pv->started_ts) { + t = pv->started_ts + pv->timeout; + } + + if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + return t; + } + + ent = ngtcp2_ringbuf_get(&pv->ents, 0); + + return ngtcp2_min(t, ent->expiry); +} diff --git a/deps/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/lib/ngtcp2_pv.h new file mode 100644 index 00000000000000..0f1734ec3e1bf5 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_pv.h @@ -0,0 +1,163 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PV_H +#define NGTCP2_PV_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_cid.h" +#include "ngtcp2_ringbuf.h" + +/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that + ngtcp2_pv can contain. It must be power of 2. */ +#define NGTCP2_PV_MAX_ENTRIES 4 + +struct ngtcp2_log; +typedef struct ngtcp2_log ngtcp2_log; + +struct ngtcp2_frame_chain; +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +typedef struct { + /* expiry is the timestamp when this PATH_CHALLENGE expires. */ + ngtcp2_tstamp expiry; + /* data is a byte string included in PATH_CHALLENGE. */ + uint8_t data[8]; +} ngtcp2_pv_entry; + +void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, + ngtcp2_tstamp expiry); + +typedef enum { + NGTCP2_PV_FLAG_NONE, + /* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID + is available in ngtcp2_pv. If path validation fails, fallback to + the fallback DCID. If path validation succeeds, fallback DCID is + retired if it does not equal to the current DCID. */ + NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE = 0x04, +} ngtcp2_pv_flag; + +struct ngtcp2_pv; +typedef struct ngtcp2_pv ngtcp2_pv; + +/* + * ngtcp2_pv is the context of a single path validation. + */ +struct ngtcp2_pv { + const ngtcp2_mem *mem; + ngtcp2_log *log; + /* dcid is DCID and path this path validation uses. */ + ngtcp2_dcid dcid; + /* fallback_dcid is the usually validated DCID and used as a + fallback if this path validation fails. */ + ngtcp2_dcid fallback_dcid; + /* ents is the ring buffer of ngtcp2_pv_entry */ + ngtcp2_ringbuf ents; + /* timeout is the duration within which this path validation should + succeed. */ + ngtcp2_duration timeout; + /* started_ts is the timestamp this path validation starts. */ + ngtcp2_tstamp started_ts; + /* loss_count is the number of lost PATH_CHALLENGE */ + size_t loss_count; + /* flags is bitwise-OR of zero or more of ngtcp2_pv_flag. */ + uint8_t flags; +}; + +/* + * ngtcp2_pv_new creates new ngtcp2_pv object and assigns its pointer + * to |*ppv|. This function makes a copy of |dcid|. |timeout| is a + * duration within which this path validation must succeed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, + ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pv_del deallocates |pv|. This function frees memory |pv| + * points too. + */ +void ngtcp2_pv_del(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_ensure_start sets started_ts field to |ts| if it is zero. + */ +void ngtcp2_pv_ensure_start(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the + * expiry time of the entry. + */ +void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, + ngtcp2_tstamp expiry); + +/* + * ngtcp2_pv_full returns nonzero if |pv| is full of ngtcp2_pv_entry. + */ +int ngtcp2_pv_full(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_validate validates that the received |data| matches the + * one of the existing entry. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PATH_VALIDATION_FAILED + * path validation has failed and must be abandoned + * NGTCP2_ERR_INVALID_STATE + * |pv| includes no entry + * NGTCP2_ERR_INVALID_ARGUMENT + * |pv| does not have an entry which has |data| and |path| + */ +int ngtcp2_pv_validate(ngtcp2_pv *pv, const uint8_t *data); + +/* + * ngtcp2_pv_handle_entry_expiry checks expiry for each entry. + */ +void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_validation_timed_out returns nonzero if the path + * validation fails because of timeout. + */ +int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_next_expiry returns the earliest expiry. + */ +ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv); + +#endif /* NGTCP2_PV_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/lib/ngtcp2_qlog.c new file mode 100644 index 00000000000000..2f6d2390c9afee --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_qlog.c @@ -0,0 +1,1202 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_qlog.h" + +#include + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" + +void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, + ngtcp2_tstamp ts, void *user_data) { + qlog->write = write; + qlog->ts = qlog->last_ts = ts; + qlog->user_data = user_data; +} + +static uint8_t *write_string(uint8_t *p, const ngtcp2_vec *s) { + *p++ = '"'; + if (s->len) { + p = ngtcp2_cpymem(p, s->base, s->len); + } + *p++ = '"'; + return p; +} + +#define NGTCP2_LOWER_XDIGITS "0123456789abcdef" + +static uint8_t *write_hex(uint8_t *p, const ngtcp2_vec *s) { + const uint8_t *b = s->base, *end = s->base + s->len; + *p++ = '"'; + for (; b != end; ++b) { + *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4]; + *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf]; + } + *p++ = '"'; + return p; +} + +static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) { + ngtcp2_vec value; + return write_hex(p, ngtcp2_vec_init(&value, cid->data, cid->datalen)); +} + +static uint8_t *write_number(uint8_t *p, uint64_t n) { + size_t nlen = 0; + uint64_t t; + uint8_t *res; + + if (n == 0) { + *p++ = '0'; + return p; + } + for (t = n; t; t /= 10, ++nlen) + ; + p += nlen; + res = p; + for (; n; n /= 10) { + *--p = (uint8_t)((n % 10) + '0'); + } + return res; +} + +static uint8_t *write_numstr(uint8_t *p, uint64_t n) { + *p++ = '"'; + p = write_number(p, n); + *p++ = '"'; + return p; +} + +static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) { + return write_number(p, ts / NGTCP2_MILLISECONDS); +} + +static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) { + return write_number(p, duration / NGTCP2_MILLISECONDS); +} + +static uint8_t *write_bool(uint8_t *p, int b) { + if (b) { + return ngtcp2_cpymem(p, "true", sizeof("true") - 1); + } + return ngtcp2_cpymem(p, "false", sizeof("false") - 1); +} + +static uint8_t *write_pair(uint8_t *p, const ngtcp2_vec *name, + const ngtcp2_vec *value) { + p = write_string(p, name); + *p++ = ':'; + return write_string(p, value); +} + +static uint8_t *write_pair_hex(uint8_t *p, const ngtcp2_vec *name, + const ngtcp2_vec *value) { + p = write_string(p, name); + *p++ = ':'; + return write_hex(p, value); +} + +static uint8_t *write_pair_numstr(uint8_t *p, const ngtcp2_vec *name, + uint64_t value) { + p = write_string(p, name); + *p++ = ':'; + p = write_numstr(p, value); + return p; +} + +static uint8_t *write_pair_number(uint8_t *p, const ngtcp2_vec *name, + uint64_t value) { + p = write_string(p, name); + *p++ = ':'; + return write_number(p, value); +} + +static uint8_t *write_pair_duration(uint8_t *p, const ngtcp2_vec *name, + ngtcp2_tstamp duration) { + p = write_string(p, name); + *p++ = ':'; + return write_duration(p, duration); +} + +static uint8_t *write_pair_bool(uint8_t *p, const ngtcp2_vec *name, int b) { + p = write_string(p, name); + *p++ = ':'; + return write_bool(p, b); +} + +static uint8_t *write_pair_cid(uint8_t *p, const ngtcp2_vec *name, + const ngtcp2_cid *cid) { + p = write_string(p, name); + *p++ = ':'; + return write_cid(p, cid); +} + +static uint8_t *write_trace_start(uint8_t *p, int server) { +#define NGTCP2_M \ + "\"traces\":[{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":\"" + p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); +#undef NGTCP2_M + if (server) { + p = ngtcp2_cpymem(p, "server", sizeof("server") - 1); + } else { + p = ngtcp2_cpymem(p, "client", sizeof("client") - 1); + } + *p++ = '"'; + *p++ = '}'; + *p++ = ','; + return p; +} + +static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { + ngtcp2_vec name; +#define NGTCP2_M \ + "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"reference_" \ + "time\":\"0\"," + p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); +#undef NGTCP2_M + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "group_id"), odcid); + *p++ = ','; + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "ODCID"), odcid); + *p++ = '}'; + *p++ = ','; + return p; +} + +static uint8_t *write_event_fields(uint8_t *p) { +#define NGTCP2_M \ + "\"event_fields\":[\"relative_time\",\"category\",\"event\",\"data\"]," + p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); +#undef NGTCP2_M + return p; +} + +static uint8_t *write_events_start(uint8_t *p) { +#define NGTCP2_M "\"events\":[" + p = ngtcp2_cpymem(p, NGTCP2_M, sizeof(NGTCP2_M) - 1); +#undef NGTCP2_M + return p; +} + +static uint8_t *write_events_end(uint8_t *p) { + *p++ = '['; + *p++ = ']'; + *p++ = ']'; + return p; +} + +static uint8_t *write_trace_end(uint8_t *p) { + *p++ = '}'; + *p++ = ']'; + return p; +} + +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { + uint8_t buf[1024]; + ngtcp2_vec name, value; + uint8_t *p = buf; + + if (!qlog->write) { + return; + } + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "qlog_version"), + ngtcp2_vec_lit(&value, "draft-01")); + *p++ = ','; + p = write_trace_start(p, server); + p = write_common_fields(p, odcid); + p = write_event_fields(p); + p = write_events_start(p); + + qlog->write(qlog->user_data, buf, (size_t)(p - buf)); +} + +void ngtcp2_qlog_end(ngtcp2_qlog *qlog) { + uint8_t buf[256]; + uint8_t *p = buf; + + if (!qlog->write) { + return; + } + + p = write_events_end(p); + p = write_trace_end(p); + *p++ = '}'; + + qlog->write(qlog->user_data, buf, (size_t)(p - buf)); +} + +static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + ngtcp2_vec value; + + /* + * {"packet_number":"0000000000000000000","packet_size":0000000000000000000} + */ +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 73 + + *p++ = '{'; + p = write_pair_numstr(p, ngtcp2_vec_lit(&value, "packet_number"), + (uint64_t)hd->pkt_num); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&value, "packet_size"), pktlen); + /* TODO Write DCIL and DCID */ + /* TODO Write SCIL and SCID */ + *p++ = '}'; + return p; +} + +static ngtcp2_vec *qlog_pkt_type(ngtcp2_vec *dest, const ngtcp2_pkt_hd *hd) { + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + switch (hd->type) { + case NGTCP2_PKT_INITIAL: + return ngtcp2_vec_lit(dest, "initial"); + case NGTCP2_PKT_HANDSHAKE: + return ngtcp2_vec_lit(dest, "handshake"); + case NGTCP2_PKT_0RTT: + return ngtcp2_vec_lit(dest, "0RTT"); + default: + return ngtcp2_vec_lit(dest, "unknown"); + } + } + + return ngtcp2_vec_lit(dest, "1RTT"); +} + +static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* {"frame_type":"padding"} */ +#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "padding")); + *p++ = '}'; + + return p; +} + +static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* {"frame_type":"ping"} */ +#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "ping")); + *p++ = '}'; + + return p; +} + +static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { + ngtcp2_vec name, value; + int64_t largest_ack, min_ack; + size_t i; + const ngtcp2_ack_blk *blk; + + /* + * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]} + * + * each range: + * ["0000000000000000000","0000000000000000000"], + */ +#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70 +#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 46 + + *p++ = '{'; + /* TODO Handle ACK ECN */ + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "ack")); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "ack_delay"), + fr->ack_delay_unscaled); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&name, "acked_ranges")); + *p++ = ':'; + *p++ = '['; + + largest_ack = fr->largest_ack; + min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; + + *p++ = '['; + p = write_numstr(p, (uint64_t)min_ack); + if (largest_ack != min_ack) { + *p++ = ','; + p = write_numstr(p, (uint64_t)largest_ack); + } + *p++ = ']'; + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + largest_ack = min_ack - (int64_t)blk->gap - 2; + min_ack = largest_ack - (int64_t)blk->blklen; + *p++ = ','; + *p++ = '['; + p = write_numstr(p, (uint64_t)min_ack); + if (largest_ack != min_ack) { + *p++ = ','; + p = write_numstr(p, (uint64_t)largest_ack); + } + *p++ = ']'; + } + + *p++ = ']'; + *p++ = '}'; + + return p; +} + +static uint8_t *write_reset_stream_frame(uint8_t *p, + const ngtcp2_reset_stream *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"reset_stream","stream_id":"0000000000000000000","error_code":0000000000000000000,"final_size":"0000000000000000000"} + */ +#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 131 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "reset_stream")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), + (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), + fr->app_error_code); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "final_size"), fr->final_size); + *p++ = '}'; + + return p; +} + +static uint8_t *write_stop_sending_frame(uint8_t *p, + const ngtcp2_stop_sending *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"stop_sending","stream_id":"0000000000000000000","error_code":0000000000000000000} + */ +#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 96 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "stop_sending")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), + (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), + fr->app_error_code); + *p++ = '}'; + + return p; +} + +static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"crypto","offset":"0000000000000000000","length":0000000000000000000} + */ +#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 83 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "crypto")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "offset"), fr->offset); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), + ngtcp2_vec_len(fr->data, fr->datacnt)); + *p++ = '}'; + + return p; +} + +static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"new_token"} + */ +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 26 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "new_token")); + /* TODO Write token here */ + *p++ = '}'; + + return p; +} + +static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"stream","stream_id":"0000000000000000000","offset":"0000000000000000000","length":0000000000000000000,"fin":true} + */ +#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 128 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "stream")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), + (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "offset"), fr->offset); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), + ngtcp2_vec_len(fr->data, fr->datacnt)); + if (fr->fin) { + *p++ = ','; + p = write_pair_bool(p, ngtcp2_vec_lit(&name, "fin"), 1); + } + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"max_data","maximum":"0000000000000000000"} + */ +#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 57 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "max_data")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), fr->max_data); + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_stream_data_frame(uint8_t *p, + const ngtcp2_max_stream_data *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"max_stream_data","stream_id":"0000000000000000000","maximum":"0000000000000000000"} + */ +#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 98 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "max_stream_data")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "stream_id"), + (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), + fr->max_stream_data); + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_streams_frame(uint8_t *p, + const ngtcp2_max_streams *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":"0000000000000000000"} + */ +#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 91 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "max_streams")); + *p++ = ','; + p = write_pair(p, ngtcp2_vec_lit(&name, "stream_type"), + fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI + ? ngtcp2_vec_lit(&value, "bidirectional") + : ngtcp2_vec_lit(&value, "unidirectional")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "maximum"), fr->max_streams); + *p++ = '}'; + + return p; +} + +static uint8_t *write_data_blocked_frame(uint8_t *p, + const ngtcp2_data_blocked *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"data_blocked"} + */ +#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 29 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "data_blocked")); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_stream_data_blocked_frame(uint8_t *p, + const ngtcp2_stream_data_blocked *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"stream_data_blocked"} + */ +#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 36 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "stream_data_blocked")); + *p++ = '}'; + + return p; +} + +static uint8_t *write_streams_blocked_frame(uint8_t *p, + const ngtcp2_streams_blocked *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"streams_blocked"} + */ +#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 32 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "streams_blocked")); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"new_connection_id","sequence_number":"0000000000000000000","retire_prior_to":"0000000000000000000","length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 251 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "new_connection_id")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "sequence_number"), fr->seq); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "retire_prior_to"), + fr->retire_prior_to); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "length"), fr->cid.datalen); + *p++ = ','; + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "connection_id"), &fr->cid); + *p++ = ','; + p = write_pair_hex(p, ngtcp2_vec_lit(&name, "reset_token"), + ngtcp2_vec_init(&value, fr->stateless_reset_token, + sizeof(fr->stateless_reset_token))); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_retire_connection_id_frame(uint8_t *p, + const ngtcp2_retire_connection_id *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"retire_connection_id","sequence_number":"0000000000000000000"} + */ +#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 77 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "retire_connection_id")); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "sequence_number"), fr->seq); + *p++ = '}'; + + return p; +} + +static uint8_t *write_path_challenge_frame(uint8_t *p, + const ngtcp2_path_challenge *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "path_challenge")); + *p++ = ','; + p = write_pair_hex(p, ngtcp2_vec_lit(&name, "data"), + ngtcp2_vec_init(&value, fr->data, sizeof(fr->data))); + *p++ = '}'; + + return p; +} + +static uint8_t *write_path_response_frame(uint8_t *p, + const ngtcp2_path_response *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "path_response")); + *p++ = ','; + p = write_pair_hex(p, ngtcp2_vec_lit(&name, "data"), + ngtcp2_vec_init(&value, fr->data, sizeof(fr->data))); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { + ngtcp2_vec name, value; + + /* + * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000,"reason":""} + */ +#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 143 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "connection_close")); + *p++ = ','; + p = write_pair(p, ngtcp2_vec_lit(&name, "error_space"), + fr->type == NGTCP2_FRAME_CONNECTION_CLOSE + ? ngtcp2_vec_lit(&value, "transport") + : ngtcp2_vec_lit(&value, "application")); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "error_code"), fr->error_code); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "raw_error_code"), + fr->error_code); + *p++ = ','; + /* TODO Write reason by escaping non-printables */ + p = write_pair(p, ngtcp2_vec_lit(&name, "reason"), + ngtcp2_vec_lit(&value, "")); + /* TODO Write trigger_frame_type */ + *p++ = '}'; + + return p; +} + +static uint8_t *write_handshake_done_frame(uint8_t *p, + const ngtcp2_handshake_done *fr) { + ngtcp2_vec name, value; + (void)fr; + + /* + * {"frame_type":"handshake_done"} + */ +#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31 + + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "frame_type"), + ngtcp2_vec_lit(&value, "handshake_done")); + *p++ = '}'; + + return p; +} + +static void qlog_pkt_write_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + int sent) { + uint8_t *p; + ngtcp2_vec name, value; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_reset(&qlog->buf); + p = qlog->buf.last; + + *p++ = '['; + p = write_tstamp(p, qlog->last_ts - qlog->ts); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "transport")); + *p++ = ','; + p = write_string(p, sent ? ngtcp2_vec_lit(&value, "packet_sent") + : ngtcp2_vec_lit(&value, "packet_received")); + *p++ = ','; + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "packet_type"), + qlog_pkt_type(&value, hd)); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "frames")); + *p++ = ':'; + *p++ = '['; + + qlog->buf.last = p; +} + +static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + uint8_t *p = qlog->buf.last; + ngtcp2_vec value; + + if (!qlog->write) { + return; + } + + /* + * ],"header":}], + */ +#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD (14 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + + assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); + assert(ngtcp2_buf_len(&qlog->buf)); + + /* Eat last ',' */ + if (*(p - 1) == ',') { + --p; + } + *p++ = ']'; + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "header")); + *p++ = ':'; + p = write_pkt_hd(p, hd, pktlen); + *p++ = '}'; + *p++ = ']'; + *p++ = ','; + + qlog->buf.last = p; + + qlog->write(qlog->user_data, qlog->buf.pos, ngtcp2_buf_len(&qlog->buf)); +} + +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { + uint8_t *p = qlog->buf.last; + + if (!qlog->write) { + return; + } + + switch (fr->type) { + case NGTCP2_FRAME_PADDING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_padding_frame(p, &fr->padding); + break; + case NGTCP2_FRAME_PING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_ping_frame(p, &fr->ping); + break; + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD + + NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_ack_frame(p, &fr->ack); + break; + case NGTCP2_FRAME_RESET_STREAM: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_reset_stream_frame(p, &fr->reset_stream); + break; + case NGTCP2_FRAME_STOP_SENDING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stop_sending_frame(p, &fr->stop_sending); + break; + case NGTCP2_FRAME_CRYPTO: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_crypto_frame(p, &fr->crypto); + break; + case NGTCP2_FRAME_NEW_TOKEN: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_new_token_frame(p, &fr->new_token); + break; + case NGTCP2_FRAME_STREAM: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stream_frame(p, &fr->stream); + break; + case NGTCP2_FRAME_MAX_DATA: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_data_frame(p, &fr->max_data); + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_stream_data_frame(p, &fr->max_stream_data); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_streams_frame(p, &fr->max_streams); + break; + case NGTCP2_FRAME_DATA_BLOCKED: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_data_blocked_frame(p, &fr->data_blocked); + break; + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_streams_blocked_frame(p, &fr->streams_blocked); + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_new_connection_id_frame(p, &fr->new_connection_id); + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_retire_connection_id_frame(p, &fr->retire_connection_id); + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_path_challenge_frame(p, &fr->path_challenge); + break; + case NGTCP2_FRAME_PATH_RESPONSE: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_path_response_frame(p, &fr->path_response); + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_connection_close_frame(p, &fr->connection_close); + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_handshake_done_frame(p, &fr->handshake_done); + break; + default: + assert(0); + } + + *p++ = ','; + + qlog->buf.last = p; +} + +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd) { + qlog_pkt_write_start(qlog, hd, /* sent = */ 0); +} + +void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + qlog_pkt_write_end(qlog, hd, pktlen); +} + +void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd) { + qlog_pkt_write_start(qlog, hd, /* sent = */ 1); +} + +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + qlog_pkt_write_end(qlog, hd, pktlen); +} + +void ngtcp2_qlog_parameters_set_transport_params( + ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int local) { + uint8_t buf[1024]; + uint8_t *p = buf; + ngtcp2_vec name, value; + const ngtcp2_preferred_addr *paddr; + + if (!qlog->write) { + return; + } + + *p++ = '['; + p = write_tstamp(p, qlog->last_ts - qlog->ts); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "transport")); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "parameters_set")); + *p++ = ','; + *p++ = '{'; + p = write_pair(p, ngtcp2_vec_lit(&name, "owner"), + local ? ngtcp2_vec_lit(&value, "local") + : ngtcp2_vec_lit(&value, "remote")); + *p++ = ','; + if (params->original_connection_id_present) { + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "original_connection_id"), + ¶ms->original_connection_id); + *p++ = ','; + } + if (params->stateless_reset_token_present) { + p = write_pair_hex(p, ngtcp2_vec_lit(&name, "stateless_reset_token"), + ngtcp2_vec_init(&value, params->stateless_reset_token, + sizeof(params->stateless_reset_token))); + *p++ = ','; + } + p = write_pair_bool(p, ngtcp2_vec_lit(&name, "disable_active_migration"), + params->disable_active_migration); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "max_idle_timeout"), + params->max_idle_timeout); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "max_packet_size"), + params->max_packet_size); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "ack_delay_exponent"), + params->ack_delay_exponent); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "max_ack_delay"), + params->max_ack_delay); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "active_connection_id_limit"), + params->active_connection_id_limit); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_data"), + params->initial_max_data); + *p++ = ','; + p = write_pair_numstr( + p, ngtcp2_vec_lit(&name, "initial_max_stream_data_bidi_local"), + params->initial_max_stream_data_bidi_local); + *p++ = ','; + p = write_pair_numstr( + p, ngtcp2_vec_lit(&name, "initial_max_stream_data_bidi_remote"), + params->initial_max_stream_data_bidi_remote); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_stream_data_uni"), + params->initial_max_stream_data_uni); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_streams_bidi"), + params->initial_max_streams_bidi); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "initial_max_streams_uni"), + params->initial_max_streams_uni); + if (params->preferred_address_present) { + *p++ = ','; + paddr = ¶ms->preferred_address; + p = write_string(p, ngtcp2_vec_lit(&name, "preferred_address")); + *p++ = ':'; + *p++ = '{'; + p = write_pair_hex( + p, ngtcp2_vec_lit(&name, "ip_v4"), + ngtcp2_vec_init(&value, paddr->ipv4_addr, sizeof(paddr->ipv4_addr))); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "port_v4"), + paddr->ipv4_port); + *p++ = ','; + p = write_pair_hex( + p, ngtcp2_vec_lit(&name, "ip_v6"), + ngtcp2_vec_init(&value, paddr->ipv6_addr, sizeof(paddr->ipv6_addr))); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "port_v6"), + paddr->ipv6_port); + *p++ = ','; + p = write_pair_cid(p, ngtcp2_vec_lit(&name, "connection_id"), &paddr->cid); + *p++ = ','; + p = write_pair_hex(p, ngtcp2_vec_lit(&name, "stateless_reset_token"), + ngtcp2_vec_init(&value, paddr->stateless_reset_token, + sizeof(paddr->stateless_reset_token))); + *p++ = '}'; + } + *p++ = '}'; + *p++ = ']'; + *p++ = ','; + + qlog->write(qlog->user_data, buf, (size_t)(p - buf)); +} + +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_cc_stat *ccs) { + uint8_t buf[1024]; + uint8_t *p = buf; + ngtcp2_vec name, value; + + if (!qlog->write) { + return; + } + + *p++ = '['; + p = write_tstamp(p, qlog->last_ts - qlog->ts); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "recovery")); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "metrics_updated")); + *p++ = ','; + *p++ = '{'; + + if (rcs->min_rtt != UINT64_MAX) { + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "min_rtt"), rcs->min_rtt); + *p++ = ','; + } + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "smoothed_rtt"), + rcs->smoothed_rtt); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "latest_rtt"), + rcs->latest_rtt); + *p++ = ','; + p = write_pair_duration(p, ngtcp2_vec_lit(&name, "rtt_variance"), + rcs->rttvar); + *p++ = ','; + /* TODO max_ack_delay? */ + p = write_pair_number(p, ngtcp2_vec_lit(&name, "pto_count"), rcs->pto_count); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "congestion_window"), + ccs->cwnd); + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "bytes_in_flight"), + ccs->bytes_in_flight); + if (ccs->ssthresh != UINT64_MAX) { + *p++ = ','; + p = write_pair_number(p, ngtcp2_vec_lit(&name, "ssthresh"), ccs->ssthresh); + } + + *p++ = '}'; + *p++ = ']'; + *p++ = ','; + + qlog->write(qlog->user_data, buf, (size_t)(p - buf)); +} + +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { + uint8_t buf[256]; + uint8_t *p = buf; + ngtcp2_vec name, value; + ngtcp2_pkt_hd hd; + + if (!qlog->write) { + return; + } + + *p++ = '['; + p = write_tstamp(p, qlog->last_ts - qlog->ts); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "recovery")); + *p++ = ','; + p = write_string(p, ngtcp2_vec_lit(&value, "packet_lost")); + *p++ = ','; + *p++ = '{'; + + hd.type = ent->hd.type; + hd.flags = ent->hd.flags; + + p = write_pair(p, ngtcp2_vec_lit(&name, "packet_type"), + qlog_pkt_type(&value, &hd)); + *p++ = ','; + p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "packet_number"), + (uint64_t)ent->hd.pkt_num); + *p++ = '}'; + *p++ = ']'; + *p++ = ','; + + qlog->write(qlog->user_data, buf, (size_t)(p - buf)); +} diff --git a/deps/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/lib/ngtcp2_qlog.h new file mode 100644 index 00000000000000..160cacd2322ed7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_qlog.h @@ -0,0 +1,134 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_QLOG_H +#define NGTCP2_QLOG_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_rtb.h" + +/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for + qlog. */ +#define NGTCP2_QLOG_BUFLEN 4096 + +typedef struct ngtcp2_qlog { + /* write is a callback function to write qlog. */ + ngtcp2_qlog_write write; + /* ts is the initial timestamp */ + ngtcp2_tstamp ts; + /* last_ts is the timestamp observed last time. */ + ngtcp2_tstamp last_ts; + /* buf is a heap allocated buffer to write exclusively + packet_received and packet_sent. */ + ngtcp2_buf buf; + /* user_data is an opaque pointer which is passed to write + callback. */ + void *user_data; +} ngtcp2_qlog; + +/* + * ngtcp2_qlog_init initializes |qlog|. + */ +void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, + ngtcp2_tstamp ts, void *user_data); + +/* + * ngtcp2_qlog_start writes qlog preamble. + */ +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server); + +/* + * ngtcp2_qlog_end writes closing part of qlog. + */ +void ngtcp2_qlog_end(ngtcp2_qlog *qlog); + +/* + * ngtcp2_qlog_write_frame writes |fr| to qlog->buf. + * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must + * be called before calling this function. + */ +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr); + +/* + * ngtcp2_qlog_pkt_received_start starts to write packet_received + * event. It initializes qlog->buf. It writes qlog to qlog->buf. + * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to + * write callback. + */ +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_qlog_pkt_received_end ends packet_received event and sends + * the content of qlog->buf to qlog->write callback. + */ +void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen); + +/* + * ngtcp2_qlog_pkt_sent_start starts to write packet_sent event. It + * initializes qlog->buf. It writes qlog to qlog->buf. + * ngtcp2_qlog_pkt_sent_end will flush the content of qlog->buf to + * write callback. + */ +void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the + * content of qlog->buf to qlog->write callback. + */ +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen); + +/* + * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog + * as parameters_set event. If |local| is nonzero, it is "owner" + * field becomes "local", otherwise "remote". + */ +void ngtcp2_qlog_parameters_set_transport_params( + ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int local); + +/* + * ngtcp2_qlog_metrics_updated writes metrics_updated event of + * recovery category. + */ +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_rcvry_stat *rcs, + ngtcp2_cc_stat *ccs); + +/* + * ngtcp2_qlog_pkt_lost writes packet_lost event. + */ +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); + +/* connection_id_updated */ + +#endif /* NGTCP2_QLOG_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_range.c b/deps/ngtcp2/lib/ngtcp2_range.c new file mode 100644 index 00000000000000..9379496b7d4b53 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_range.c @@ -0,0 +1,61 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_range.h" +#include "ngtcp2_macro.h" + +void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) { + r->begin = begin; + r->end = end; +} + +ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, + const ngtcp2_range *b) { + ngtcp2_range r = {0, 0}; + uint64_t begin = ngtcp2_max(a->begin, b->begin); + uint64_t end = ngtcp2_min(a->end, b->end); + if (begin < end) { + ngtcp2_range_init(&r, begin, end); + } + return r; +} + +uint64_t ngtcp2_range_len(const ngtcp2_range *r) { return r->end - r->begin; } + +int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) { + return a->begin == b->begin && a->end == b->end; +} + +void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, + const ngtcp2_range *a, const ngtcp2_range *b) { + /* Assume that b is included in a */ + left->begin = a->begin; + left->end = b->begin; + right->begin = b->end; + right->end = a->end; +} + +int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) { + return a->end <= b->end; +} diff --git a/deps/ngtcp2/lib/ngtcp2_range.h b/deps/ngtcp2/lib/ngtcp2_range.h new file mode 100644 index 00000000000000..96b9a54b28f2d6 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_range.h @@ -0,0 +1,80 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RANGE_H +#define NGTCP2_RANGE_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * ngtcp2_range represents half-closed range [begin, end). + */ +typedef struct { + uint64_t begin; + uint64_t end; +} ngtcp2_range; + +/* + * ngtcp2_range_init initializes |r| with the range [|begin|, |end|). + */ +void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end); + +/* + * ngtcp2_range_intersect returns the intersection of |a| and |b|. If + * they do not overlap, it returns empty range. + */ +ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, + const ngtcp2_range *b); + +/* + * ngtcp2_range_len returns the length of |r|. + */ +uint64_t ngtcp2_range_len(const ngtcp2_range *r); + +/* + * ngtcp2_range_eq returns nonzero if |a| equals |b|, such that + * a->begin == b->begin, and a->end == b->end hold. + */ +int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b); + +/* + * ngtcp2_range_cut returns the left and right range after removing + * |b| from |a|. This function assumes that |a| completely includes + * |b|. In other words, a->begin <= b->begin and b->end <= a->end + * hold. + */ +void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, + const ngtcp2_range *a, const ngtcp2_range *b); + +/* + * ngtcp2_range_not_after returns nonzero if the right edge of |a| + * does not go beyond of the right edge of |b|. + */ +int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b); + +#endif /* NGTCP2_RANGE_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/lib/ngtcp2_rcvry.h new file mode 100644 index 00000000000000..254c3c797a0301 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rcvry.h @@ -0,0 +1,44 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RCVRY_H +#define NGTCP2_RCVRY_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in + draft-ietf-quic-recovery-22. */ +#define NGTCP2_PKT_THRESHOLD 3 + +/* NGTCP2_GRANULARITY is kGranularity described in + draft-ietf-quic-recovery-17. */ +#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS + +#define NGTCP2_DEFAULT_INITIAL_RTT (500 * NGTCP2_MILLISECONDS) + +#endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/lib/ngtcp2_ringbuf.c new file mode 100644 index 00000000000000..6f5e61a2d69bd0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ringbuf.c @@ -0,0 +1,106 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ringbuf.h" + +#include +#ifdef WIN32 +# include +#endif + +#include "ngtcp2_macro.h" + +int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + const ngtcp2_mem *mem) { +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); + if (rb->buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rb->mem = mem; + rb->nmemb = nmemb; + rb->size = size; + rb->first = 0; + rb->len = 0; + + return 0; +} + +void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { + if (rb == NULL) { + return; + } + + ngtcp2_mem_free(rb->mem, rb->buf); +} + +void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb) { + rb->first = (rb->first - 1) & (rb->nmemb - 1); + rb->len = ngtcp2_min(rb->nmemb, rb->len + 1); + + return (void *)&rb->buf[rb->first * rb->size]; +} + +void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) { + size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); + + if (rb->len == rb->nmemb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + } else { + ++rb->len; + } + + return (void *)&rb->buf[offset * rb->size]; +} + +void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + --rb->len; +} + +void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb) { + assert(rb->len); + --rb->len; +} + +void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len) { + assert(len <= rb->nmemb); + rb->len = len; +} + +void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset) { + assert(offset < rb->len); + offset = (rb->first + offset) & (rb->nmemb - 1); + return &rb->buf[offset * rb->size]; +} + +size_t ngtcp2_ringbuf_len(ngtcp2_ringbuf *rb) { return rb->len; } + +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->nmemb; } diff --git a/deps/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/lib/ngtcp2_ringbuf.h new file mode 100644 index 00000000000000..05e9a577f1948e --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_ringbuf.h @@ -0,0 +1,110 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RINGBUF_H +#define NGTCP2_RINGBUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" + +typedef struct { + /* buf points to the underlying buffer. */ + uint8_t *buf; + const ngtcp2_mem *mem; + /* nmemb is the number of elements that can be stored in this ring + buffer. */ + size_t nmemb; + /* size is the size of each element. */ + size_t size; + /* first is the offset to the first element. */ + size_t first; + /* len is the number of elements actually stored. */ + size_t len; +} ngtcp2_ringbuf; + +/* + * ngtcp2_ringbuf_init initializes |rb|. |nmemb| is the number of + * elements that can be stored in this buffer. |size| is the size of + * each element. |size| must be power of 2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + const ngtcp2_mem *mem); + +/* + * ngtcp2_ringbuf_free frees resources allocated for |rb|. This + * function does not free the memory pointed by |rb|. + */ +void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_push_front moves the offset to the first element in + the buffer backward, and returns the pointer to the element. + Caller can store data to the buffer pointed by the returned + pointer. If this action exceeds the capacity of the ring buffer, + the last element is silently overwritten, and rb->len remains + unchanged. */ +void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_push_back moves the offset to the last element in + the buffer forward, and returns the pointer to the element. Caller + can store data to the buffer pointed by the returned pointer. If + this action exceeds the capacity of the ring buffer, the first + element is silently overwritten, and rb->len remains unchanged. */ +void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb); + +/* + * ngtcp2_ringbuf_pop_front removes first element in |rb|. + */ +void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb); + +/* + * ngtcp2_ringbuf_pop_back removes the last element in |rb|. + */ +void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_resize changes the number of elements stored. This + does not change the capacity of the underlying buffer. */ +void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len); + +/* ngtcp2_ringbuf_get returns the pointer to the element at + |offset|. */ +void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); + +/* ngtcp2_ringbuf_len returns the number of elements stored. */ +size_t ngtcp2_ringbuf_len(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); + +#endif /* NGTCP2_RINGBUF_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/lib/ngtcp2_rob.c new file mode 100644 index 00000000000000..ab7f44ecb88cb7 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rob.c @@ -0,0 +1,341 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rob.h" + +#include +#include + +#include "ngtcp2_macro.h" + +int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, + const ngtcp2_mem *mem) { + *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap)); + if (*pg == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pg)->range.begin = begin; + (*pg)->range.end = end; + + return 0; +} + +void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, g); +} + +int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, + const ngtcp2_mem *mem) { + *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk); + if (*pd == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pd)->range.begin = offset; + (*pd)->range.end = offset + chunk; + (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data); + (*pd)->end = (*pd)->begin + chunk; + + return 0; +} + +void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, d); +} + +int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { + int rv; + ngtcp2_rob_gap *g; + ngtcp2_ksl_key key; + + rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + goto fail_gapksl_ksl_init; + } + + rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); + if (rv != 0) { + goto fail_rob_gap_new; + } + + rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, + ngtcp2_ksl_key_ptr(&key, &g->range), g); + if (rv != 0) { + goto fail_gapksl_ksl_insert; + } + + rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + goto fail_dataksl_ksl_init; + } + + rob->chunk = chunk; + rob->mem = mem; + + return 0; + +fail_dataksl_ksl_init: +fail_gapksl_ksl_insert: + ngtcp2_rob_gap_del(g, mem); +fail_rob_gap_new: + ngtcp2_ksl_free(&rob->gapksl); +fail_gapksl_ksl_init: + return rv; +} + +void ngtcp2_rob_free(ngtcp2_rob *rob) { + ngtcp2_ksl_it it; + + if (rob == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem); + } + + for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem); + } + + ngtcp2_ksl_free(&rob->dataksl); + ngtcp2_ksl_free(&rob->gapksl); +} + +static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t len) { + size_t n; + int rv; + ngtcp2_rob_data *d; + ngtcp2_range range = {offset, offset + len}; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, + ngtcp2_ksl_key_ptr(&key, &range), + ngtcp2_ksl_range_exclusive_compar); + len; ngtcp2_ksl_it_next(&it)) { + if (ngtcp2_ksl_it_end(&it)) { + d = NULL; + } else { + d = ngtcp2_ksl_it_get(&it); + } + + if (d == NULL || offset < d->range.begin) { + rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk, + rob->chunk, rob->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_insert(&rob->dataksl, &it, + ngtcp2_ksl_key_ptr(&key, &d->range), d); + if (rv != 0) { + ngtcp2_rob_data_del(d, rob->mem); + return rv; + } + } + + n = ngtcp2_min(len, d->range.begin + rob->chunk - offset); + memcpy(d->begin + (offset - d->range.begin), data, n); + offset += n; + data += n; + len -= n; + } + + return 0; +} + +int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t datalen) { + int rv; + ngtcp2_rob_gap *g; + ngtcp2_range m, l, r, q = {offset, offset + datalen}; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key, old_key; + + it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, ngtcp2_ksl_key_ptr(&key, &q), + ngtcp2_ksl_range_exclusive_compar); + + for (; !ngtcp2_ksl_it_end(&it);) { + g = ngtcp2_ksl_it_get(&it); + + m = ngtcp2_range_intersect(&q, &g->range); + if (!ngtcp2_range_len(&m)) { + break; + } + if (ngtcp2_range_eq(&g->range, &m)) { + ngtcp2_ksl_remove(&rob->gapksl, &it, ngtcp2_ksl_key_ptr(&key, &g->range)); + ngtcp2_rob_gap_del(g, rob->mem); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; + } + + continue; + } + ngtcp2_range_cut(&l, &r, &g->range, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_ksl_update_key(&rob->gapksl, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &l)); + g->range = l; + + if (ngtcp2_range_len(&r)) { + ngtcp2_rob_gap *ng; + rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); + if (rv != 0) { + return rv; + } + rv = ngtcp2_ksl_insert(&rob->gapksl, &it, + ngtcp2_ksl_key_ptr(&key, &ng->range), ng); + if (rv != 0) { + ngtcp2_rob_gap_del(ng, rob->mem); + return rv; + } + } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_ksl_update_key(&rob->gapksl, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &r)); + g->range = r; + } + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; + } + ngtcp2_ksl_it_next(&it); + } + return 0; +} + +int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key, old_key; + + it = ngtcp2_ksl_begin(&rob->gapksl); + + for (; !ngtcp2_ksl_it_end(&it);) { + g = ngtcp2_ksl_it_get(&it); + if (offset <= g->range.begin) { + break; + } + if (offset < g->range.end) { + ngtcp2_range r = {offset, g->range.end}; + ngtcp2_ksl_update_key(&rob->gapksl, + ngtcp2_ksl_key_ptr(&old_key, &g->range), + ngtcp2_ksl_key_ptr(&key, &r)); + g->range.begin = offset; + break; + } + ngtcp2_ksl_remove(&rob->gapksl, &it, ngtcp2_ksl_key_ptr(&key, &g->range)); + ngtcp2_rob_gap_del(g, rob->mem); + } + + it = ngtcp2_ksl_begin(&rob->dataksl); + + for (; !ngtcp2_ksl_it_end(&it);) { + d = ngtcp2_ksl_it_get(&it); + if (offset < d->range.begin + rob->chunk) { + return 0; + } + ngtcp2_ksl_remove(&rob->dataksl, &it, ngtcp2_ksl_key_ptr(&key, &d->range)); + ngtcp2_rob_data_del(d, rob->mem); + } + + return 0; +} + +size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, + uint64_t offset) { + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + + it = ngtcp2_ksl_begin(&rob->gapksl); + if (ngtcp2_ksl_it_end(&it)) { + return 0; + } + + g = ngtcp2_ksl_it_get(&it); + + if (g->range.begin <= offset) { + return 0; + } + + it = ngtcp2_ksl_begin(&rob->dataksl); + d = ngtcp2_ksl_it_get(&it); + + assert(d); + assert(d->range.begin <= offset); + assert(offset < d->range.begin + rob->chunk); + + *pdest = d->begin + (offset - d->range.begin); + + return ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) - offset; +} + +void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + ngtcp2_rob_data *d; + + it = ngtcp2_ksl_begin(&rob->dataksl); + d = ngtcp2_ksl_it_get(&it); + + assert(d); + + if (offset + len < d->range.begin + rob->chunk) { + return; + } + + ngtcp2_ksl_remove(&rob->dataksl, NULL, ngtcp2_ksl_key_ptr(&key, &d->range)); + ngtcp2_rob_data_del(d, rob->mem); +} + +uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl); + ngtcp2_rob_gap *g; + + if (ngtcp2_ksl_it_end(&it)) { + return UINT64_MAX; + } + + g = ngtcp2_ksl_it_get(&it); + + return g->range.begin; +} + +int ngtcp2_rob_data_buffered(ngtcp2_rob *rob) { + return ngtcp2_ksl_len(&rob->dataksl) != 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_rob.h b/deps/ngtcp2/lib/ngtcp2_rob.h new file mode 100644 index 00000000000000..c6a039ce408de6 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rob.h @@ -0,0 +1,203 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ROB_H +#define NGTCP2_ROB_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" +#include "ngtcp2_ksl.h" + +struct ngtcp2_rob_gap; +typedef struct ngtcp2_rob_gap ngtcp2_rob_gap; + +/* + * ngtcp2_rob_gap represents the gap, which is the range of stream + * data that is not received yet. + */ +struct ngtcp2_rob_gap { + /* range is the range of this gap. */ + ngtcp2_range range; +}; + +/* + * ngtcp2_rob_gap_new allocates new ngtcp2_rob_gap object, and assigns + * its pointer to |*pg|. The caller should call ngtcp2_rob_gap_del to + * delete it when it is no longer used. The range of the gap is + * [begin, end). |mem| is custom memory allocator to allocate memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, + const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_gap_del deallocates |g|. It deallocates the memory + * pointed by |g| it self. |mem| is custom memory allocator to + * deallocate memory. + */ +void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem); + +struct ngtcp2_rob_data; +typedef struct ngtcp2_rob_data ngtcp2_rob_data; + +/* + * ngtcp2_rob_data holds the buffered stream data. + */ +struct ngtcp2_rob_data { + /* range is the range of this gap. */ + ngtcp2_range range; + /* begin points to the buffer. */ + uint8_t *begin; + /* end points to the one beyond of the last byte of the buffer */ + uint8_t *end; +}; + +/* + * ngtcp2_rob_data_new allocates new ngtcp2_rob_data object, and + * assigns its pointer to |*pd|. The caller should call + * ngtcp2_rob_data_del to delete it when it is no longer used. + * |offset| is the stream offset of the first byte of this data. + * |chunk| is the size of the buffer. |offset| must be multiple of + * |chunk|. |mem| is custom memory allocator to allocate memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, + const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_data_del deallocates |d|. It deallocates the memory + * pointed by |d| itself. |mem| is custom memory allocator to + * deallocate memory. + */ +void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem); + +/* + * ngtcp2_rob is the reorder buffer which reassembles stream data + * received in out of order. + */ +typedef struct { + /* gapksl maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + ngtcp2_ksl gapksl; + /* dataksl maintains the list of buffers which store received data + ordered by stream offset. */ + ngtcp2_ksl dataksl; + /* mem is custom memory allocator */ + const ngtcp2_mem *mem; + /* chunk is the size of each buffer in data field */ + size_t chunk; +} ngtcp2_rob; + +/* + * ngtcp2_rob_init initializes |rob|. |chunk| is the size of buffer + * per chunk. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_free frees resources allocated for |rob|. + */ +void ngtcp2_rob_free(ngtcp2_rob *rob); + +/* + * ngtcp2_rob_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t datalen); + +/* + * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It + * also removes data buffer if it is completely included in |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset); + +/* + * ngtcp2_rob_data_at stores the pointer to the buffer of stream + * offset |offset| to |*pdest| if it is available, and returns the + * valid length of available data. If no data is available, it + * returns 0. + */ +size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, + uint64_t offset); + +/* + * ngtcp2_rob_pop clears data at stream offset |offset| of length + * |len|. + * + * |offset| must be the offset given in ngtcp2_rob_data_at. |len| + * must be the return value of ngtcp2_rob_data_at when |offset| is + * passed. + * + * Caller should call this function from offset 0 in non-decreasing + * order. + */ +void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len); + +/* + * ngtcp2_rob_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob); + +/* + * ngtcp2_rob_data_buffered returns nonzero if any data is buffered. + */ +int ngtcp2_rob_data_buffered(ngtcp2_rob *rob); + +#endif /* NGTCP2_ROB_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/lib/ngtcp2_rst.c new file mode 100644 index 00000000000000..c9c994e2bae16b --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rst.c @@ -0,0 +1,107 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rst.h" +#include "ngtcp2_rtb.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_macro.h" + +void ngtcp2_rst_init(ngtcp2_rst *rst) { + ngtcp2_rs *rs = &rst->rs; + + rst->delivered = 0; + rst->delivered_ts = 0; + rst->first_sent_ts = 0; + rst->app_limited = 0; + + rs->delivery_rate = 0.; + rs->interval = UINT64_MAX; + rs->delivered = 0; + rs->prior_delivered = 0; + rs->prior_ts = 0; + rs->send_elapsed = 0; + rs->ack_elapsed = 0; + rs->is_app_limited = 0; +} + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_cc_stat *ccs) { + if (ccs->bytes_in_flight == 0) { + rst->first_sent_ts = rst->delivered_ts = ent->ts; + } + ent->rst.first_sent_ts = rst->first_sent_ts; + ent->rst.delivered_ts = rst->delivered_ts; + ent->rst.delivered = rst->delivered; + ent->rst.is_app_limited = rst->app_limited != 0; +} + +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, const ngtcp2_rcvry_stat *rcs) { + ngtcp2_rs *rs = &rst->rs; + + if (rst->app_limited && rst->delivered > rst->app_limited) { + rst->app_limited = 0; + } + + if (rs->prior_ts == 0) { + return 0; + } + + rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed); + + rs->delivered = rst->delivered - rs->prior_delivered; + + if (rs->interval < rcs->min_rtt) { + rs->interval = UINT64_MAX; + return 0; + } + + if (rs->interval) { + rs->delivery_rate = (double)rs->delivered / (double)rs->interval; + } + + return 1; +} + +void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts) { + ngtcp2_rs *rs = &rst->rs; + + rst->delivered += ent->pktlen; + rst->delivered_ts = ts; + + if (ent->rst.delivered > rs->prior_delivered) { + rs->prior_delivered = ent->rst.delivered; + rs->prior_ts = ent->rst.delivered_ts; + rs->is_app_limited = ent->rst.is_app_limited; + rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; + rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; + rst->first_sent_ts = ent->ts; + } +} + +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, const ngtcp2_cc_stat *ccs) { + (void)rst; + (void)ccs; + /* TODO Not implemented */ +} diff --git a/deps/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/lib/ngtcp2_rst.h new file mode 100644 index 00000000000000..b7137b79c957b8 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rst.h @@ -0,0 +1,69 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RST_H +#define NGTCP2_RST_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +struct ngtcp2_rtb_entry; +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; + +typedef struct ngtcp2_rs { + double delivery_rate; + ngtcp2_duration interval; + uint64_t delivered; + uint64_t prior_delivered; + ngtcp2_tstamp prior_ts; + ngtcp2_duration send_elapsed; + ngtcp2_duration ack_elapsed; + int is_app_limited; +} ngtcp2_rs; + +/* + * ngtcp2_rst implements delivery rate estimation described in + * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00 + */ +typedef struct ngtcp2_rst { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t app_limited; + ngtcp2_rs rs; +} ngtcp2_rst; + +void ngtcp2_rst_init(ngtcp2_rst *rst); + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_cc_stat *ccs); +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, const ngtcp2_rcvry_stat *rcs); +void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts); +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, const ngtcp2_cc_stat *ccs); + +#endif /* NGTCP2_RST_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/lib/ngtcp2_rtb.c new file mode 100644 index 00000000000000..83936f2aa69013 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rtb.c @@ -0,0 +1,670 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rtb.h" + +#include +#include + +#include "ngtcp2_macro.h" +#include "ngtcp2_conn.h" +#include "ngtcp2_log.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { + *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain)); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + +int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, + const ngtcp2_mem *mem) { + *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + +int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem) { + size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); + size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); + + if (datacnt > 0 && need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } + + return ngtcp2_frame_chain_new(pfrc, mem); +} + +int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem) { + size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); + size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); + + if (datacnt > 0 && need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } + + return ngtcp2_frame_chain_new(pfrc, mem); +} + +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, frc); +} + +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { frc->next = NULL; } + +void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain *next; + + for (; frc;) { + next = frc->next; + ngtcp2_mem_free(mem, frc); + frc = next; + } +} + +static void frame_chain_insert(ngtcp2_frame_chain **pfrc, + ngtcp2_frame_chain *frc) { + ngtcp2_frame_chain **plast; + + assert(frc); + + for (plast = &frc; *plast; plast = &(*plast)->next) + ; + + *plast = *pfrc; + *pfrc = frc; +} + +int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { + (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); + if (*pent == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pent)->hd.pkt_num = hd->pkt_num; + (*pent)->hd.type = hd->type; + (*pent)->hd.flags = hd->flags; + (*pent)->frc = frc; + (*pent)->ts = ts; + (*pent)->pktlen = pktlen; + (*pent)->flags = flags; + + return 0; +} + +void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { + if (ent == NULL) { + return; + } + + ngtcp2_frame_chain_list_del(ent->frc, mem); + + ngtcp2_mem_free(mem, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *lhs->i > *rhs->i; +} + +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, ngtcp2_rst *rst, + ngtcp2_default_cc *cc, ngtcp2_log *log, ngtcp2_qlog *qlog, + const ngtcp2_mem *mem) { + ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); + rtb->crypto = crypto; + rtb->rst = rst; + rtb->cc = cc; + rtb->log = log; + rtb->qlog = qlog; + rtb->mem = mem; + rtb->largest_acked_tx_pkt_num = -1; + rtb->num_ack_eliciting = 0; + rtb->loss_time = UINT64_MAX; + rtb->probe_pkt_left = 0; + rtb->crypto_level = crypto_level; + rtb->cc_pkt_num = 0; +} + +void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { + ngtcp2_ksl_it it; + + if (rtb == NULL) { + return; + } + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); + } + + ngtcp2_ksl_free(&rtb->ents); +} + +static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { + ngtcp2_rst_on_pkt_sent(rtb->rst, ent, rtb->cc->ccs); + + assert(rtb->cc_pkt_num <= ent->hd.pkt_num); + + rtb->cc->ccs->bytes_in_flight += ent->pktlen; + + ngtcp2_rst_update_app_limited(rtb->rst, rtb->cc->ccs); + + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + ++rtb->num_ack_eliciting; + } +} + +static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { + if (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING) { + assert(rtb->num_ack_eliciting); + --rtb->num_ack_eliciting; + } + + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + assert(rtb->cc->ccs->bytes_in_flight >= ent->pktlen); + rtb->cc->ccs->bytes_in_flight -= ent->pktlen; + } +} + +static void rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rtb_entry *ent) { + ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, + ent->ts); + + if (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } + + if (!(ent->flags & NGTCP2_RTB_FLAG_PROBE)) { + if (ent->flags & NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " CRYPTO has already been retransmitted", + ent->hd.pkt_num); + } else if (ent->frc) { + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + /* TODO Reconsider the order of pfrc */ + frame_chain_insert(pfrc, ent->frc); + ent->frc = NULL; + } + } + + ngtcp2_rtb_entry_del(ent, rtb->mem); +} + +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent) { + int rv; + ngtcp2_ksl_key key; + + ent->next = NULL; + + rv = ngtcp2_ksl_insert(&rtb->ents, NULL, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num), ent); + if (rv != 0) { + return rv; + } + + rtb_on_add(rtb, ent); + + return 0; +} + +ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) { + return ngtcp2_ksl_begin(&rtb->ents); +} + +static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, + ngtcp2_rtb_entry *ent) { + ngtcp2_ksl_key key; + + ngtcp2_ksl_remove(&rtb->ents, it, ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + rtb_on_remove(rtb, ent); + ngtcp2_rtb_entry_del(ent, rtb->mem); +} + +static int rtb_call_acked_stream_offset(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn *conn) { + ngtcp2_frame_chain *frc; + uint64_t prev_stream_offset, stream_offset; + ngtcp2_strm *strm; + int rv; + size_t datalen; + ngtcp2_strm *crypto = rtb->crypto; + + for (frc = ent->frc; frc; frc = frc->next) { + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id); + if (strm == NULL) { + break; + } + prev_stream_offset = + ngtcp2_gaptr_first_gap_offset(&strm->tx.acked_offset); + rv = ngtcp2_gaptr_push( + &strm->tx.acked_offset, frc->fr.stream.offset, + ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); + if (rv != 0) { + return rv; + } + + if (conn->callbacks.acked_stream_data_offset) { + stream_offset = ngtcp2_gaptr_first_gap_offset(&strm->tx.acked_offset); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0 && !frc->fr.stream.fin) { + break; + } + + rv = conn->callbacks.acked_stream_data_offset( + conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data, + strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_CRYPTO: + prev_stream_offset = + ngtcp2_gaptr_first_gap_offset(&crypto->tx.acked_offset); + rv = ngtcp2_gaptr_push( + &crypto->tx.acked_offset, frc->fr.crypto.offset, + ngtcp2_vec_len(frc->fr.crypto.data, frc->fr.crypto.datacnt)); + if (rv != 0) { + return rv; + } + + if (conn->callbacks.acked_crypto_offset) { + stream_offset = ngtcp2_gaptr_first_gap_offset(&crypto->tx.acked_offset); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0) { + break; + } + + rv = conn->callbacks.acked_crypto_offset(conn, rtb->crypto_level, + prev_stream_offset, datalen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + break; + case NGTCP2_FRAME_RESET_STREAM: + strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id); + if (strm == NULL) { + break; + } + strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED; + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + if (rv != 0) { + return rv; + } + break; + } + } + return 0; +} + +static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts) { + ngtcp2_cc_pkt pkt; + + ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); + + ngtcp2_default_cc_on_pkt_acked( + rtb->cc, ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, ent->ts)); +} + +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, + ngtcp2_conn *conn, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent; + int64_t largest_ack = fr->largest_ack, min_ack; + size_t i; + int rv; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + ngtcp2_ssize num_acked = 0; + int largest_pkt_acked = 0; + int rtt_updated = 0; + ngtcp2_tstamp largest_pkt_sent_ts = 0; + + if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && + largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->crypto.key_update.confirmed_ts = ts; + + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); + } + + rtb->largest_acked_tx_pkt_num = + ngtcp2_max(rtb->largest_acked_tx_pkt_num, largest_ack); + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + it = ngtcp2_ksl_lower_bound(&rtb->ents, + ngtcp2_ksl_key_ptr(&key, &largest_ack)); + if (ngtcp2_ksl_it_end(&it)) { + return 0; + } + + min_ack = largest_ack - (int64_t)fr->first_ack_blklen; + + for (; !ngtcp2_ksl_it_end(&it);) { + key = ngtcp2_ksl_it_key(&it); + if (min_ack <= *key.i && *key.i <= largest_ack) { + ent = ngtcp2_ksl_it_get(&it); + if (conn) { + rv = rtb_call_acked_stream_offset(rtb, ent, conn); + if (rv != 0) { + return rv; + } + if (largest_ack == *key.i) { + largest_pkt_sent_ts = ent->ts; + largest_pkt_acked = 1; + } + if (!rtt_updated && largest_pkt_acked && + (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + rtt_updated = 1; + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, + fr->ack_delay_unscaled); + } + rtb_on_pkt_acked(rtb, ent, ts); + /* At this point, it is invalided because rtb->ents might be + modified. */ + } + rtb_remove(rtb, &it, ent); + ++num_acked; + continue; + } + break; + } + + for (i = 0; i < fr->num_blks;) { + largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; + min_ack = largest_ack - (int64_t)fr->blks[i].blklen; + + it = ngtcp2_ksl_lower_bound(&rtb->ents, + ngtcp2_ksl_key_ptr(&key, &largest_ack)); + if (ngtcp2_ksl_it_end(&it)) { + break; + } + + for (; !ngtcp2_ksl_it_end(&it);) { + key = ngtcp2_ksl_it_key(&it); + if (*key.i < min_ack) { + break; + } + ent = ngtcp2_ksl_it_get(&it); + if (conn) { + rv = rtb_call_acked_stream_offset(rtb, ent, conn); + if (rv != 0) { + return rv; + } + if (!rtt_updated && largest_pkt_acked && + (ent->flags & NGTCP2_RTB_FLAG_ACK_ELICITING)) { + rtt_updated = 1; + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, + fr->ack_delay_unscaled); + } + rtb_on_pkt_acked(rtb, ent, ts); + } + rtb_remove(rtb, &it, ent); + ++num_acked; + } + + ++i; + } + + if (conn) { + ngtcp2_rst_on_ack_recv(rtb->rst, &conn->rcs); + ngtcp2_default_cc_on_ack_recv(rtb->cc, + rtt_updated ? conn->rcs.latest_rtt : 0, ts); + } + + return num_acked; +} + +static int rtb_pkt_lost(ngtcp2_rtb *rtb, const ngtcp2_rtb_entry *ent, + uint64_t loss_delay, ngtcp2_tstamp lost_send_time) { + if (ent->ts <= lost_send_time || + rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + NGTCP2_PKT_THRESHOLD) { + return 1; + } + + if (rtb->loss_time == UINT64_MAX) { + rtb->loss_time = ent->ts + loss_delay; + } else { + rtb->loss_time = ngtcp2_min(rtb->loss_time, ent->ts + loss_delay); + } + + return 0; +} + +/* + * rtb_compute_pkt_loss_delay computes delay until packet is + * considered lost in NGTCP2_MICROSECONDS resolution. + */ +static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_rcvry_stat *rcs) { + /* 9/8 is kTimeThreshold */ + ngtcp2_duration loss_delay = + ngtcp2_max(rcs->latest_rtt, rcs->smoothed_rtt) * 9 / 8; + return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY); +} + +void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent; + ngtcp2_duration loss_delay; + ngtcp2_tstamp lost_send_time; + ngtcp2_ksl_it it; + ngtcp2_tstamp latest_ts, oldest_ts; + int64_t last_lost_pkt_num; + ngtcp2_ksl_key key; + + rtb->loss_time = UINT64_MAX; + loss_delay = compute_pkt_loss_delay(rcs); + lost_send_time = ts - loss_delay; + + it = ngtcp2_ksl_lower_bound( + &rtb->ents, ngtcp2_ksl_key_ptr(&key, &rtb->largest_acked_tx_pkt_num)); + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + if (rtb_pkt_lost(rtb, ent, loss_delay, lost_send_time)) { + /* All entries from ent are considered to be lost. */ + latest_ts = oldest_ts = ent->ts; + last_lost_pkt_num = ent->hd.pkt_num; + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + + if (last_lost_pkt_num == ent->hd.pkt_num + 1) { + last_lost_pkt_num = ent->hd.pkt_num; + } else { + last_lost_pkt_num = -1; + } + + oldest_ts = ent->ts; + rtb_on_remove(rtb, ent); + rtb_on_pkt_lost(rtb, pfrc, ent); + } + + ngtcp2_default_cc_congestion_event(rtb->cc, latest_ts, ts); + + if (last_lost_pkt_num != -1) { + ngtcp2_default_cc_handle_persistent_congestion( + rtb->cc, latest_ts - oldest_ts, pto); + } + + return; + } + } +} + +void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + rtb_on_remove(rtb, ent); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + + rtb_on_pkt_lost(rtb, pfrc, ent); + } +} + +int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_frame_chain *nfrc; + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it gapit; + ngtcp2_range gap, range; + ngtcp2_crypto *fr; + int all_acked; + int rv; + ngtcp2_ksl_key key; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + if ((ent->flags & NGTCP2_RTB_FLAG_PROBE) || + !(ent->flags & NGTCP2_RTB_FLAG_CRYPTO_PKT)) { + ngtcp2_ksl_it_next(&it); + continue; + } + + all_acked = 1; + + for (frc = ent->frc; frc; frc = frc->next) { + assert(frc->fr.type == NGTCP2_FRAME_CRYPTO); + + fr = &frc->fr.crypto; + + /* Don't resend CRYPTO frame if the whole region it contains has + been acknowledged */ + gapit = ngtcp2_gaptr_get_first_gap_after(&rtb->crypto->tx.acked_offset, + fr->offset); + gap = *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit).ptr; + + range.begin = fr->offset; + range.end = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); + range = ngtcp2_range_intersect(&range, &gap); + if (ngtcp2_range_len(&range) == 0) { + continue; + } + + all_acked = 0; + + if (!(ent->flags & NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED)) { + rv = ngtcp2_frame_chain_crypto_datacnt_new( + &nfrc, frc->fr.crypto.datacnt, rtb->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr = frc->fr; + ngtcp2_vec_copy(nfrc->fr.crypto.data, frc->fr.crypto.data, + frc->fr.crypto.datacnt); + + frame_chain_insert(pfrc, nfrc); + } + } + + if (all_acked) { + /* If the frames that ent contains have been acknowledged, + remove it from rtb. Otherwise crypto timer keeps firing. */ + rtb_on_remove(rtb, ent); + ngtcp2_ksl_remove(&rtb->ents, &it, + ngtcp2_ksl_key_ptr(&key, &ent->hd.pkt_num)); + ngtcp2_rtb_entry_del(ent, rtb->mem); + continue; + } + + ent->flags |= NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED; + + ngtcp2_ksl_it_next(&it); + } + + return 0; +} + +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { + return ngtcp2_ksl_len(&rtb->ents) == 0; +} + +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb) { + uint64_t bytes_in_flight = 0; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + for (it = ngtcp2_ksl_begin(&rtb->ents); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + bytes_in_flight += ent->pktlen; + } + } + + return bytes_in_flight; +} + +size_t ngtcp2_rtb_num_ack_eliciting(ngtcp2_rtb *rtb) { + return rtb->num_ack_eliciting; +} diff --git a/deps/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/lib/ngtcp2_rtb.h new file mode 100644 index 00000000000000..0e11cea5d4f0f0 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_rtb.h @@ -0,0 +1,329 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RTB_H +#define NGTCP2_RTB_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_pkt.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" + +struct ngtcp2_conn; +typedef struct ngtcp2_conn ngtcp2_conn; + +struct ngtcp2_frame_chain; +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +struct ngtcp2_log; +typedef struct ngtcp2_log ngtcp2_log; + +struct ngtcp2_qlog; +typedef struct ngtcp2_qlog ngtcp2_qlog; + +struct ngtcp2_default_cc; +typedef struct ngtcp2_default_cc ngtcp2_default_cc; + +struct ngtcp2_strm; +typedef struct ngtcp2_strm ngtcp2_strm; + +struct ngtcp2_rst; +typedef struct ngtcp2_rst ngtcp2_rst; + +/* + * ngtcp2_frame_chain chains frames in a single packet. + */ +struct ngtcp2_frame_chain { + ngtcp2_frame_chain *next; + ngtcp2_frame fr; +}; + +/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that + a ngtcp2_stream can include. */ +#define NGTCP2_MAX_STREAM_DATACNT 256 + +/* NGTCP2_MAX_CRYPTO_DATACNT is the maximum number of ngtcp2_vec that + a ngtcp2_crypto can include. */ +#define NGTCP2_MAX_CRYPTO_DATACNT 8 + +/* + * ngtcp2_frame_chain_new allocates ngtcp2_frame_chain object and + * assigns its pointer to |*pfrc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, + * but it allocates extra memory |extralen| in order to extend + * ngtcp2_frame. + */ +int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_stream_datacnt_new works like + * ngtcp2_frame_chain_new, but it allocates enough data to store + * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream + * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called + * internally. + */ +int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_crypto_datacnt_new works like + * ngtcp2_frame_chain_new, but it allocates enough data to store + * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto + * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called + * internally. + */ +int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the + * memory pointed by |frc|. + */ +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_init initializes |frc|. + */ +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); + +/* + * ngtcp2_frame_chain_list_del deletes |frc|, and all objects + * connected by next field. + */ +void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, + const ngtcp2_mem *mem); + +typedef enum { + NGTCP2_RTB_FLAG_NONE = 0x00, + /* NGTCP2_RTB_FLAG_PROBE indicates that the entry includes a probe + packet. */ + NGTCP2_RTB_FLAG_PROBE = 0x01, + /* NGTCP2_RTB_FLAG_CRYPTO_PKT indicates that the entry includes + handshake CRYPTO frame. */ + NGTCP2_RTB_FLAG_CRYPTO_PKT = 0x02, + /* NGTCP2_RTB_FLAG_ACK_ELICITING indicates that the entry elicits + acknowledgement. */ + NGTCP2_RTB_FLAG_ACK_ELICITING = 0x04, + /* NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED indicates that the + CRYPTO frames have been retransmitted. */ + NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED = 0x08, +} ngtcp2_rtb_flag; + +struct ngtcp2_rtb_entry; +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; + +/* + * ngtcp2_rtb_entry is an object stored in ngtcp2_rtb. It corresponds + * to the one packet which is waiting for its ACK. + */ +struct ngtcp2_rtb_entry { + ngtcp2_rtb_entry *next; + + struct { + int64_t pkt_num; + uint8_t type; + uint8_t flags; + } hd; + ngtcp2_frame_chain *frc; + /* ts is the time point when a packet included in this entry is sent + to a peer. */ + ngtcp2_tstamp ts; + /* pktlen is the length of QUIC packet */ + size_t pktlen; + struct { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + int is_app_limited; + } rst; + /* flags is bitwise-OR of zero or more of ngtcp2_rtb_flag. */ + uint8_t flags; +}; + +/* + * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns + * its pointer to |*pent|. On success, |*pent| takes ownership of + * |frc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory + * pointed by |ent|. + */ +void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb tracks sent packets, and its ACK timeout for + * retransmission. + */ +typedef struct { + /* ents includes ngtcp2_rtb_entry sorted by decreasing order of + packet number. */ + ngtcp2_ksl ents; + /* crypto is CRYPTO stream. */ + ngtcp2_strm *crypto; + ngtcp2_rst *rst; + ngtcp2_default_cc *cc; + ngtcp2_log *log; + ngtcp2_qlog *qlog; + const ngtcp2_mem *mem; + /* largest_acked_tx_pkt_num is the largest packet number + acknowledged by the peer. */ + int64_t largest_acked_tx_pkt_num; + size_t num_ack_eliciting; + ngtcp2_tstamp loss_time; + /* probe_pkt_left is the number of probe packet to send */ + size_t probe_pkt_left; + /* crypto_level is encryption level which |crypto| belongs to. */ + ngtcp2_crypto_level crypto_level; + /* cc_pkt_num is the smallest packet number that is contributed to + bytes_in_flight. */ + int64_t cc_pkt_num; +} ngtcp2_rtb; + +/* + * ngtcp2_rtb_init initializes |rtb|. + */ +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, ngtcp2_rst *rst, + ngtcp2_default_cc *cc, ngtcp2_log *log, ngtcp2_qlog *qlog, + const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb_free deallocates resources allocated for |rtb|. + */ +void ngtcp2_rtb_free(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_add adds |ent| to |rtb|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent); + +/* + * ngtcp2_rtb_head returns the iterator which points to the entry + * which has the largest packet number. If there is no entry, + * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. + */ +ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|. + * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of newly acknowledged packets if + * it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed + * NGTCP2_ERR_NOMEM + * Out of memory + */ +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, + ngtcp2_conn *conn, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the + * frames contained them to |*pfrc|. Even when this function fails, + * some frames might be prepended to |*pfrc| and the caller should + * handle them. |pto| is PTO. + */ +void ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc, + ngtcp2_rcvry_stat *rcs, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends + * all frames to |*pfrc|. Even when this function fails, some frames + * might be prepended to |*pfrc| and the caller should handle them. + */ +void ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc); + +/* + * ngtcp2_rtb_on_crypto_timeout copies all unacknowledged CRYPTO + * frames and links them to |*pfrc|. The affected ngtcp2_rtb_entry + * will have NGTCP2_RTB_FLAG_CRYPTO_TIMEOUT_RETRANSMITTED set. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rtb_on_crypto_timeout(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc); + +/* + * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. + */ +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_get_bytes_in_flight returns the sum of bytes in flight + * for the stored entries. + */ +uint64_t ngtcp2_rtb_get_bytes_in_flight(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_num_ack_eliciting returns the number of ACK eliciting + * entries. + */ +size_t ngtcp2_rtb_num_ack_eliciting(ngtcp2_rtb *rtb); + +#endif /* NGTCP2_RTB_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/lib/ngtcp2_str.c new file mode 100644 index 00000000000000..b7502e3bd0a5af --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_str.c @@ -0,0 +1,98 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_str.h" + +#include + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n) { + memcpy(dest, src, n); + return (uint8_t *)dest + n; +} + +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) { + memset(dest, b, n); + return dest + n; +} + +#define LOWER_XDIGITS "0123456789abcdef" + +uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) { + size_t i; + uint8_t *p = dest; + + for (i = 0; i < len; ++i) { + *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4]; + *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf]; + } + + *p = '\0'; + + return dest; +} + +char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, + size_t len) { + size_t i; + char *p = dest; + uint8_t c; + + for (i = 0; i < len; ++i) { + c = data[i]; + if (0x20 <= c && c <= 0x7e) { + *p++ = (char)c; + } else { + *p++ = '.'; + } + } + + *p = '\0'; + + return dest; +} + +int ngtcp2_verify_stateless_reset_token(const uint8_t *want, + const uint8_t *got) { + return !ngtcp2_check_invalid_stateless_reset_token(got) && + ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) + ? 0 + : NGTCP2_ERR_INVALID_ARGUMENT; +} + +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) { + static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0}; + + return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) { + size_t i; + int rv = 0; + + for (i = 0; i < n; ++i) { + rv |= a[i] ^ b[i]; + } + + return rv == 0; +} diff --git a/deps/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/lib/ngtcp2_str.h new file mode 100644 index 00000000000000..104b1f1a03873b --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_str.h @@ -0,0 +1,85 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STR_H +#define NGTCP2_STR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n); + +/* + * ngtcp2_setmem writes a string of length |n| consisting only |b| to + * the buffer pointed by |dest|. It returns dest + n; + */ +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n); +/* + * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It + * writes additional NULL bytes at the end of the buffer. The buffer + * pointed by |dest| must have at least |len| * 2 + 1 bytes space. + * This function returns |dest|. + */ +uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len); + +/* + * ngtcp2_encode_printable_ascii encodes |data| of length |len| in + * |dest| in the following manner: printable ascii characters are + * copied as is. The other characters are converted to ".". It + * writes additional NULL bytes at the end of the buffer. |dest| must + * have at least |len| + 1 bytes. This function returns |dest|. + */ +char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, + size_t len); + +/* + * ngtcp2_verify_stateless_reset_token verifies stateless reset token + * |want| and |got|. This function returns 0 if |want| equals |got| + * and |got| is not all zero, or one of the following negative error + * codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Token does not match; or token is all zero. + */ +int ngtcp2_verify_stateless_reset_token(const uint8_t *want, + const uint8_t *got); + +/* + * ngtcp2_check_invalid_stateless_reset_token returns nonzero if + * |token| is invalid stateless reset token. Currently, token which + * consists of all zeros is considered invalid. + */ +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token); + +/* + * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers + * pointed by |a| and |b| are equal. The comparison is done in a + * constant time manner. + */ +int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n); + +#endif /* NGTCP2_STR_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/lib/ngtcp2_strm.c new file mode 100644 index 00000000000000..b6b315489dc798 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_strm.c @@ -0,0 +1,325 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_strm.h" + +#include +#include + +#include "ngtcp2_rtb.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_vec.h" + +static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *lhs->i < *rhs->i; +} + +int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, const ngtcp2_mem *mem) { + int rv; + + strm->cycle = 0; + strm->tx.offset = 0; + strm->tx.max_offset = max_tx_offset; + strm->rx.last_offset = 0; + strm->stream_id = stream_id; + strm->flags = flags; + strm->stream_user_data = stream_user_data; + strm->rx.max_offset = strm->rx.unsent_max_offset = max_rx_offset; + strm->me.key = (uint64_t)stream_id; + strm->me.next = NULL; + strm->pe.index = NGTCP2_PQ_BAD_INDEX; + strm->mem = mem; + strm->app_error_code = 0; + + rv = ngtcp2_gaptr_init(&strm->tx.acked_offset, mem); + if (rv != 0) { + goto fail_gaptr_init; + } + + rv = ngtcp2_rob_init(&strm->rx.rob, 8 * 1024, mem); + if (rv != 0) { + goto fail_rob_init; + } + + rv = ngtcp2_ksl_init(&strm->tx.streamfrq, offset_less, sizeof(uint64_t), mem); + if (rv != 0) { + goto fail_tx_streamfrq_init; + } + + return 0; + +fail_tx_streamfrq_init: + ngtcp2_rob_free(&strm->rx.rob); +fail_rob_init: + ngtcp2_gaptr_free(&strm->tx.acked_offset); +fail_gaptr_init: + return rv; +} + +void ngtcp2_strm_free(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + if (strm == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(&strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); + } + + ngtcp2_ksl_free(&strm->tx.streamfrq); + ngtcp2_rob_free(&strm->rx.rob); + ngtcp2_gaptr_free(&strm->tx.acked_offset); +} + +uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) { + return ngtcp2_rob_first_gap_offset(&strm->rx.rob); +} + +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset) { + return ngtcp2_rob_push(&strm->rx.rob, offset, data, datalen); +} + +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { + strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR; +} + +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { + ngtcp2_ksl_key key; + + assert(frc->fr.type == NGTCP2_FRAME_STREAM); + assert(frc->next == NULL); + + return ngtcp2_ksl_insert(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &frc->fr.stream.offset), + frc); +} + +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, + size_t left) { + ngtcp2_stream *fr, *nfr; + ngtcp2_frame_chain *frc, *nfrc; + int rv; + size_t nmerged; + size_t datalen; + ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; + ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; + size_t acnt, bcnt; + ngtcp2_ksl_it it; + ngtcp2_ksl_key key, old_key; + uint64_t old_offset; + + if (ngtcp2_ksl_len(&strm->tx.streamfrq) == 0) { + *pfrc = NULL; + return 0; + } + + it = ngtcp2_ksl_begin(&strm->tx.streamfrq); + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.stream; + + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (left == 0) { + /* datalen could be zero if 0 length STREAM has been sent */ + if (datalen || ngtcp2_ksl_len(&strm->tx.streamfrq) > 1) { + *pfrc = NULL; + return 0; + } + } + + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &fr->offset)); + + if (datalen > left) { + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + bcnt = 0; + ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_STREAM_DATACNT); + + assert(acnt > 0); + assert(bcnt > 0); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + nfr->type = NGTCP2_FRAME_STREAM; + nfr->flags = 0; + nfr->fin = fr->fin; + nfr->stream_id = fr->stream_id; + nfr->offset = fr->offset + left; + nfr->datacnt = bcnt; + ngtcp2_vec_copy(nfr->data, b, bcnt); + + rv = ngtcp2_ksl_insert(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset), nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + *nfr = *fr; + nfr->fin = 0; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, strm->mem); + + *pfrc = nfrc; + + return 0; + } + + left -= datalen; + + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + for (; left && ngtcp2_ksl_len(&strm->tx.streamfrq);) { + it = ngtcp2_ksl_begin(&strm->tx.streamfrq); + nfrc = ngtcp2_ksl_it_get(&it); + nfr = &nfrc->fr.stream; + + if (nfr->offset != fr->offset + datalen) { + assert(fr->offset + datalen < nfr->offset); + break; + } + + if (nfr->fin && nfr->datacnt == 0) { + fr->fin = 1; + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + break; + } + + nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, + NGTCP2_MAX_STREAM_DATACNT); + if (nmerged == 0) { + break; + } + + datalen += nmerged; + left -= nmerged; + + if (nfr->datacnt == 0) { + fr->fin = nfr->fin; + ngtcp2_ksl_remove(&strm->tx.streamfrq, NULL, + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + continue; + } + + old_offset = nfr->offset; + nfr->offset += nmerged; + + ngtcp2_ksl_update_key(&strm->tx.streamfrq, + ngtcp2_ksl_key_ptr(&old_key, &old_offset), + ngtcp2_ksl_key_ptr(&key, &nfr->offset)); + + break; + } + + if (acnt == fr->datacnt) { + if (acnt > 0) { + fr->data[acnt - 1] = a[acnt - 1]; + } + + *pfrc = frc; + return 0; + } + + assert(acnt > fr->datacnt); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, strm->mem); + + *pfrc = nfrc; + + return 0; +} + +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + assert(ngtcp2_ksl_len(&strm->tx.streamfrq)); + + it = ngtcp2_ksl_begin(&strm->tx.streamfrq); + return ngtcp2_ksl_it_get(&it); +} + +int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm) { + return ngtcp2_ksl_len(&strm->tx.streamfrq) == 0; +} + +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + for (it = ngtcp2_ksl_begin(&strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + ngtcp2_frame_chain_del(frc, strm->mem); + } + ngtcp2_ksl_clear(&strm->tx.streamfrq); +} + +int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm) { + return strm->pe.index != NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { + return ngtcp2_gaptr_first_gap_offset(&strm->tx.acked_offset) == + strm->tx.offset; +} diff --git a/deps/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/lib/ngtcp2_strm.h new file mode 100644 index 00000000000000..67d49e519e0eda --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_strm.h @@ -0,0 +1,219 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STRM_H +#define NGTCP2_STRM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_rob.h" +#include "ngtcp2_map.h" +#include "ngtcp2_gaptr.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" + +struct ngtcp2_frame_chain; +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +typedef enum { + NGTCP2_STRM_FLAG_NONE = 0, + /* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of + stream data is not allowed. */ + NGTCP2_STRM_FLAG_SHUT_RD = 0x01, + /* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of + stream data is not allowed. */ + NGTCP2_STRM_FLAG_SHUT_WR = 0x02, + NGTCP2_STRM_FLAG_SHUT_RDWR = + NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR, + /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from + the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is + also set. */ + NGTCP2_STRM_FLAG_SENT_RST = 0x04, + /* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received + from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD + is also set. */ + NGTCP2_STRM_FLAG_RECV_RST = 0x08, + /* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent + from the local endpoint. */ + NGTCP2_STRM_FLAG_STOP_SENDING = 0x10, + /* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM + is acknowledged by peer. */ + NGTCP2_STRM_FLAG_RST_ACKED = 0x20, +} ngtcp2_strm_flags; + +struct ngtcp2_strm; +typedef struct ngtcp2_strm ngtcp2_strm; + +struct ngtcp2_strm { + ngtcp2_map_entry me; + ngtcp2_pq_entry pe; + uint64_t cycle; + + struct { + /* acked_offset tracks acknowledged outgoing data. */ + ngtcp2_gaptr acked_offset; + /* streamfrq contains STREAM frame for retransmission. The flow + control credits have been paid when they are transmitted first + time. There are no restriction regarding flow control for + retransmission. */ + ngtcp2_ksl streamfrq; + /* offset is the next offset of outgoing data. In other words, it + is the number of bytes sent in this stream without + duplication. */ + uint64_t offset; + /* max_tx_offset is the maximum offset that local endpoint can + send for this stream. */ + uint64_t max_offset; + } tx; + + struct { + /* rob is the reorder buffer for incoming stream data. The data + received in out of order is buffered and sorted by its offset + in this object. */ + ngtcp2_rob rob; + /* last_offset is the largest offset of stream data received for + this stream. */ + uint64_t last_offset; + /* max_offset is the maximum offset that remote endpoint can send + to this stream. */ + uint64_t max_offset; + /* unsent_max_offset is the maximum offset that remote endpoint + can send to this stream, and it is not notified to the remote + endpoint. unsent_max_offset >= max_offset must be hold. */ + uint64_t unsent_max_offset; + } rx; + + const ngtcp2_mem *mem; + int64_t stream_id; + void *stream_user_data; + /* flags is bit-wise OR of zero or more of ngtcp2_strm_flags. */ + uint32_t flags; + /* app_error_code is an error code the local endpoint sent in + RST_STREAM or STOP_SENDING. */ + uint64_t app_error_code; +}; + +/* + * ngtcp2_strm_init initializes |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, const ngtcp2_mem *mem); + +/* + * ngtcp2_strm_free deallocates memory allocated for |strm|. This + * function does not free the memory pointed by |strm| itself. + */ +void ngtcp2_strm_free(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_rx_offset returns the minimum offset of stream data + * which is not received yet. + */ +uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_recv_reordering handles reordered data. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset); + +/* + * ngtcp2_strm_shutdown shutdowns |strm|. |flags| should be + * NGTCP2_STRM_FLAG_SHUT_RD, and/or NGTCP2_STRM_FLAG_SHUT_WR. + */ +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags); + +/* + * ngtcp2_strm_streamfrq_push pushes |frc| to streamfrq for + * retransmission. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc); + +/* + * ngtcp2_strm_streamfrq_pop pops the first ngtcp2_frame_chain and + * assigns it to |*pfrc|. This function splits into or merges several + * ngtcp2_frame_chain objects so that the returned ngtcp2_frame_chain + * has at most |left| data length. If there is no frames to send, + * this function returns 0 and |*pfrc| is NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, + size_t left); + +/* + * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain. + * The queue must not be empty. + */ +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty. + */ +int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_clear removes all frames from streamfrq. + */ +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_is_tx_queued returns nonzero if |strm| is queued. + */ +int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_is_all_tx_data_acked returns nonzero if all outgoing + * data for |strm| which have sent so far have been acknowledged. + */ +int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm); + +#endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/lib/ngtcp2_vec.c new file mode 100644 index 00000000000000..7a6f8afa051f20 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_vec.c @@ -0,0 +1,232 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_vec.h" + +#include +#include + +#include "ngtcp2_str.h" + +ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) { + vec->base = (uint8_t *)base; + vec->len = len; + return vec; +} + +int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, + const ngtcp2_mem *mem) { + size_t len; + uint8_t *p; + + len = sizeof(ngtcp2_vec) + datalen; + + *pvec = ngtcp2_mem_malloc(mem, len); + if (*pvec == NULL) { + return NGTCP2_ERR_NOMEM; + } + + p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec); + (*pvec)->base = p; + (*pvec)->len = datalen; + if (datalen) { + /* p = */ ngtcp2_cpymem(p, data, datalen); + } + + return 0; +} + +void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, vec); +} + +size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { + size_t i; + size_t res = 0; + + for (i = 0; i < n; ++i) { + res += vec[i].len; + } + + return res; +} + +ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, + size_t *pdstcnt, size_t left, size_t maxcnt) { + size_t i; + size_t srccnt = *psrccnt; + size_t nmove; + size_t extra = 0; + + for (i = 0; i < srccnt; ++i) { + if (left >= src[i].len) { + left -= src[i].len; + continue; + } + + if (*pdstcnt && src[srccnt - 1].base + src[srccnt - 1].len == dst[0].base) { + if (*pdstcnt + srccnt - i - 1 > maxcnt) { + return -1; + } + + dst[0].len += src[srccnt - 1].len; + dst[0].base = src[srccnt - 1].base; + extra = src[srccnt - 1].len; + --srccnt; + } else if (*pdstcnt + srccnt - i > maxcnt) { + return -1; + } + + if (left == 0) { + *psrccnt = i; + } else { + *psrccnt = i + 1; + } + + nmove = srccnt - i; + if (nmove) { + memmove(dst + nmove, dst, sizeof(ngtcp2_vec) * (*pdstcnt)); + *pdstcnt += nmove; + memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove); + } + + dst[0].len -= left; + dst[0].base += left; + src[i].len = left; + + if (nmove == 0) { + extra -= left; + } + + return (ngtcp2_ssize)(ngtcp2_vec_len(dst, nmove) + extra); + } + + return 0; +} + +size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, + size_t *psrccnt, size_t left, size_t maxcnt) { + size_t orig_left = left; + size_t i; + ngtcp2_vec *a, *b; + + assert(maxcnt); + + if (*pdstcnt == 0) { + if (*psrccnt == 0) { + return 0; + } + + a = &dst[0]; + b = &src[0]; + + if (left >= b->len) { + *a = *b; + ++*pdstcnt; + left -= b->len; + i = 1; + } else { + a->len = left; + a->base = b->base; + + b->len -= left; + b->base += left; + + return left; + } + } else { + i = 0; + } + + for (; left && i < *psrccnt; ++i) { + a = &dst[*pdstcnt - 1]; + b = &src[i]; + + if (left >= b->len) { + if (a->base + a->len == b->base) { + a->len += b->len; + } else if (*pdstcnt == maxcnt) { + break; + } else { + dst[(*pdstcnt)++] = *b; + } + left -= b->len; + continue; + } + + if (a->base + a->len == b->base) { + a->len += left; + } else if (*pdstcnt == maxcnt) { + break; + } else { + dst[*pdstcnt].len = left; + dst[*pdstcnt].base = b->base; + ++*pdstcnt; + } + + b->len -= left; + b->base += left; + left = 0; + + break; + } + + memmove(src, src + i, sizeof(ngtcp2_vec) * (*psrccnt - i)); + *psrccnt -= i; + + return orig_left - left; +} + +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, + size_t dstcnt, const ngtcp2_vec *src, + size_t srccnt, size_t left) { + size_t i, j; + size_t len = left; + + *pnwritten = 0; + + for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) { + if (src[i].len == 0) { + ++i; + continue; + } + dst[j] = src[i]; + if (dst[j].len > left) { + dst[j].len = left; + *pnwritten = len; + return j + 1; + } + left -= dst[j].len; + ++i; + ++j; + } + + *pnwritten = len - left; + + return j; +} + +void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) { + memcpy(dst, src, sizeof(ngtcp2_vec) * cnt); +} diff --git a/deps/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/lib/ngtcp2_vec.h new file mode 100644 index 00000000000000..077820a9efed23 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_vec.h @@ -0,0 +1,114 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_VEC_H +#define NGTCP2_VEC_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "ngtcp2_mem.h" + +/* + * ngtcp2_vec_lit is a convenient macro to fill the object pointed by + * |DEST| with the literal string |LIT|. + */ +#define ngtcp2_vec_lit(DEST, LIT) \ + ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST)) + +/* + * ngtcp2_vec_init initializes |vec| with the given parameters. It + * returns |vec|. + */ +ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len); + +/* + * ngtcp2_vec_new allocates and initializes |*pvec| with given |data| + * of length |datalen|. This function allocates memory for |*pvec| + * and the given data with a single allocation, and the contents + * pointed by |data| is copied into the allocated memory space. To + * free the allocated memory, call ngtcp2_vec_del. + */ +int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_vec_del frees the memory allocated by |vec| which is + * allocated and initialized by ngtcp2_vec_new. + */ +void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); + +/* + * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. + */ +size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_split splits |src| to |dst| so that the sum of the + * length in |src| does not exceed |left| bytes. The |maxcnt| is the + * maximum number of elements which |dst| array can contain. The + * caller must set |*psrccnt| to the number of elements of |src|. + * Similarly, the caller must set |*pdstcnt| to the number of elements + * of |dst|. The split does not necessarily occur at the boundary of + * ngtcp2_vec object. After split has done, this function updates + * |*psrccnt| and |*pdstcnt|. This function returns the number of + * bytes moved from |src| to |dst|. If split cannot be made because + * doing so exceeds |maxcnt|, this function returns -1. + */ +ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, + size_t *pdstcnt, size_t left, size_t maxcnt); + +/* + * ngtcp2_vec_merge merges |src| into |dst| by moving at most |left| + * bytes from |src|. The |maxcnt| is the maximum number of elements + * which |dst| array can contain. The caller must set |*pdstcnt| to + * the number of elements of |dst|. Similarly, the caller must set + * |*psrccnt| to the number of elements of |src|. After merge has + * done, this function updates |*psrccnt| and |*pdstcnt|. This + * function returns the number of bytes moved from |src| to |dst|. + */ +size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, + size_t *psrccnt, size_t left, size_t maxcnt); + +/* + * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of + * length |dstcnt|. The total number of bytes which the copied + * ngtcp2_vec refers to is at most |left| and is assigned to + * |*pnwritten|. The empty elements in |src| are ignored. This + * function returns the number of elements copied. + */ +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, + size_t dstcnt, const ngtcp2_vec *src, + size_t srccnt, size_t left); + +/* + * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must + * have sufficient capacity. + */ +void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt); + +#endif /* NGTCP2_VEC_H */ diff --git a/deps/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/lib/ngtcp2_version.c new file mode 100644 index 00000000000000..40f3ae3f9eade4 --- /dev/null +++ b/deps/ngtcp2/lib/ngtcp2_version.c @@ -0,0 +1,39 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, + NGTCP2_VERSION}; + +ngtcp2_info *ngtcp2_version(int least_version) { + if (least_version > NGTCP2_VERSION_NUM) { + return NULL; + } + return &version; +} diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp new file mode 100644 index 00000000000000..1da2cba93dc19d --- /dev/null +++ b/deps/ngtcp2/ngtcp2.gyp @@ -0,0 +1,116 @@ +{ + 'target_defaults': { + 'defines': [ + '_U_=' + ] + }, + 'targets': [ + { + 'target_name': 'ngtcp2', + 'type': 'static_library', + 'include_dirs': [ + 'lib/includes', + 'crypto/includes', + 'lib', + ], + 'defines': [ + 'BUILDING_NGTCP2', + 'NGTCP2_STATICLIB', + ], + 'dependencies': [ + '../openssl/openssl.gyp:openssl' + ], + 'conditions': [ + ['OS=="win"', { + 'defines': [ + 'WIN32', + '_WINDOWS', + 'HAVE_CONFIG_H', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'CompileAs': '1' + }, + }, + }], + ], + 'direct_dependent_settings': { + 'defines': [ 'NGTCP2_STATICLIB' ], + 'include_dirs': [ + 'lib/includes', + 'crypto/includes' + ] + }, + 'sources': [ + 'lib/ngtcp2_acktr.c', + 'lib/ngtcp2_acktr.h', + 'lib/ngtcp2_addr.c', + 'lib/ngtcp2_addr.h', + 'lib/ngtcp2_buf.c', + 'lib/ngtcp2_buf.h', + 'lib/ngtcp2_cc.c', + 'lib/ngtcp2_cc.h', + 'lib/ngtcp2_cid.c', + 'lib/ngtcp2_cid.h', + 'lib/ngtcp2_conn.c', + 'lib/ngtcp2_conn.h', + 'lib/ngtcp2_conv.c', + 'lib/ngtcp2_conv.h', + 'lib/ngtcp2_crypto.c', + 'lib/ngtcp2_crypto.h', + 'lib/ngtcp2_err.c', + 'lib/ngtcp2_err.h', + 'lib/ngtcp2_gaptr.c', + 'lib/ngtcp2_gaptr.h', + 'lib/ngtcp2_idtr.c', + 'lib/ngtcp2_idtr.h', + 'lib/ngtcp2_ksl.c', + 'lib/ngtcp2_ksl.h', + 'lib/ngtcp2_log.c', + 'lib/ngtcp2_log.h', + 'lib/ngtcp2_macro.h', + 'lib/ngtcp2_map.c', + 'lib/ngtcp2_map.h', + 'lib/ngtcp2_mem.c', + 'lib/ngtcp2_mem.h', + 'lib/ngtcp2_net.h', + 'lib/ngtcp2_path.c', + 'lib/ngtcp2_path.h', + 'lib/ngtcp2_pipeack.c', + 'lib/ngtcp2_pipeack.h', + 'lib/ngtcp2_pkt.c', + 'lib/ngtcp2_pkt.h', + 'lib/ngtcp2_ppe.c', + 'lib/ngtcp2_ppe.h', + 'lib/ngtcp2_pq.c', + 'lib/ngtcp2_pq.h', + 'lib/ngtcp2_psl.c', + 'lib/ngtcp2_psl.h', + 'lib/ngtcp2_pv.c', + 'lib/ngtcp2_pv.h', + 'lib/ngtcp2_qlog.c', + 'lib/ngtcp2_qlog.h', + 'lib/ngtcp2_range.c', + 'lib/ngtcp2_range.h', + 'lib/ngtcp2_ringbuf.c', + 'lib/ngtcp2_ringbuf.h', + 'lib/ngtcp2_rob.c', + 'lib/ngtcp2_rob.h', + 'lib/ngtcp2_rtb.c', + 'lib/ngtcp2_rtb.h', + 'lib/ngtcp2_rst.c', + 'lib/ngtcp2_rst.h', + 'lib/ngtcp2_str.c', + 'lib/ngtcp2_str.h', + 'lib/ngtcp2_strm.c', + 'lib/ngtcp2_strm.h', + 'lib/ngtcp2_vec.c', + 'lib/ngtcp2_vec.h', + 'lib/ngtcp2_version.c', + 'crypto/shared.c', + 'crypto/shared.h', + 'crypto/openssl/openssl.c' + ] + } + ] +} From 2350cdfcd89981bbf6999512a4594d20bb389ed2 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 9 Mar 2020 14:35:32 -0700 Subject: [PATCH 4/6] quic: initial quic implementation Co-authored-by: Anna Henningsen Co-authored-by: Daniel Bevenius Co-authored-by: gengjiawen Co-authored-by: James M Snell Co-authored-by: Lucas Pardue Co-authored-by: Ouyang Yadong Co-authored-by: Juan Jos Arboleda Co-authored-by: Trivikram Kamat Co-authored-by: Denys Otrishko --- LICENSE | 52 + configure.py | 57 + deps/openssl/openssl.gyp | 7 + doc/api/errors.md | 119 + doc/api/index.md | 1 + doc/api/quic.md | 2273 ++++++++++ lib/_http_client.js | 9 +- lib/_http_server.js | 9 +- lib/_tls_wrap.js | 20 +- lib/internal/crypto/scrypt.js | 2 +- lib/internal/errors.js | 55 + lib/internal/modules/cjs/helpers.js | 39 +- lib/internal/options.js | 19 +- lib/internal/quic/core.js | 3339 +++++++++++++++ lib/internal/quic/util.js | 717 ++++ lib/internal/stream_base_commons.js | 24 +- lib/internal/validators.js | 40 +- lib/quic.js | 12 + node.gyp | 49 + node.gypi | 18 + src/aliased_buffer.h | 25 + src/async_wrap.h | 6 + src/debug_utils.h | 1 + src/env-inl.h | 15 + src/env.h | 63 +- src/handle_wrap.cc | 4 + src/handle_wrap.h | 5 +- src/js_udp_wrap.cc | 219 + src/node_binding.cc | 8 + src/node_bob-inl.h | 37 + src/node_bob.h | 111 + src/node_crypto.cc | 13 +- src/node_crypto_common.cc | 7 +- src/node_crypto_common.h | 2 +- src/node_errors.h | 6 +- src/node_http_common-inl.h | 14 + src/node_http_common.h | 37 +- src/node_mem.h | 8 +- src/node_metadata.cc | 8 + src/node_metadata.h | 7 + src/node_native_module.cc | 7 + src/quic/node_quic.cc | 244 ++ src/quic/node_quic_buffer-inl.h | 99 + src/quic/node_quic_buffer.cc | 160 + src/quic/node_quic_buffer.h | 239 ++ src/quic/node_quic_crypto.cc | 960 +++++ src/quic/node_quic_crypto.h | 159 + src/quic/node_quic_default_application.cc | 181 + src/quic/node_quic_default_application.h | 61 + src/quic/node_quic_http3_application.cc | 937 ++++ src/quic/node_quic_http3_application.h | 333 ++ src/quic/node_quic_session-inl.h | 614 +++ src/quic/node_quic_session.cc | 3763 +++++++++++++++++ src/quic/node_quic_session.h | 1485 +++++++ src/quic/node_quic_socket-inl.h | 227 + src/quic/node_quic_socket.cc | 1178 ++++++ src/quic/node_quic_socket.h | 577 +++ src/quic/node_quic_state.h | 72 + src/quic/node_quic_stream-inl.h | 158 + src/quic/node_quic_stream.cc | 513 +++ src/quic/node_quic_stream.h | 385 ++ src/quic/node_quic_util-inl.h | 460 ++ src/quic/node_quic_util.h | 392 ++ src/stream_base-inl.h | 3 +- src/udp_wrap.cc | 1 + src/udp_wrap.h | 5 + src/util.h | 10 + test/cctest/test_quic_buffer.cc | 199 + test/cctest/test_quic_cid.cc | 31 + .../test_quic_verifyhostnameidentity.cc | 349 ++ test/common/README.md | 14 + test/common/fixtures.js | 7 +- test/common/index.js | 2 + test/common/quic.js | 38 + test/common/udppair.js | 100 + test/parallel/test-module-cjs-helpers.js | 2 +- test/parallel/test-process-versions.js | 19 +- test/parallel/test-quic-binding.js | 39 + ...t-quic-client-connect-multiple-parallel.js | 57 + ...quic-client-connect-multiple-sequential.js | 56 + ...est-quic-client-empty-preferred-address.js | 57 + test/parallel/test-quic-client-server.js | 372 ++ ...test-quic-errors-quicsession-openstream.js | 84 + .../test-quic-errors-quicsocket-connect.js | 243 ++ .../test-quic-errors-quicsocket-create.js | 187 + .../test-quic-errors-quicsocket-listen.js | 179 + .../parallel/test-quic-http3-client-server.js | 157 + test/parallel/test-quic-http3-push.js | 157 + test/parallel/test-quic-http3-trailers.js | 109 + test/parallel/test-quic-idle-timeout.js | 73 + test/parallel/test-quic-ipv6only.js | 128 + test/parallel/test-quic-keylog.js | 67 + .../test-quic-maxconnectionsperhost.js | 86 + test/parallel/test-quic-process-cleanup.js | 57 + test/parallel/test-quic-qlog.js | 66 + .../test-quic-quicendpoint-address.js | 87 + ...ic-quicession-server-openstream-pending.js | 59 + ...est-quic-quicsession-openstream-pending.js | 64 + test/parallel/test-quic-quicsession-resume.js | 100 + .../parallel/test-quic-quicsession-send-fd.js | 86 + ...quicsession-send-file-close-before-open.js | 46 + ...uicsession-send-file-open-error-handled.js | 49 + ...t-quic-quicsession-send-file-open-error.js | 49 + ...t-quic-quicsession-server-destroy-early.js | 81 + test/parallel/test-quic-quicsocket-close.js | 19 + ...st-quic-quicsocket-packetloss-stream-rx.js | 109 + ...st-quic-quicsocket-packetloss-stream-tx.js | 109 + .../test-quic-quicsocket-serverbusy.js | 62 + test/parallel/test-quic-quicsocket.js | 154 + .../test-quic-quicstream-close-early.js | 122 + test/parallel/test-quic-quicstream-destroy.js | 75 + .../test-quic-quicstream-identifiers.js | 156 + .../test-quic-simple-client-migrate.js | 108 + test/parallel/test-quic-statelessreset.js | 76 + test/parallel/test-quic-with-fake-udp.js | 61 + test/parallel/test-validators.js | 41 +- test/pummel/test-heapdump-quic.js | 152 + test/sequential/test-async-wrap-getasyncid.js | 7 + .../test-quic-preferred-address-ipv6.js | 94 + .../sequential/test-quic-preferred-address.js | 93 + tools/doc/type-parser.js | 31 +- tools/license-builder.sh | 6 + vcbuild.bat | 5 +- 123 files changed, 25625 insertions(+), 91 deletions(-) create mode 100644 doc/api/quic.md create mode 100644 lib/internal/quic/core.js create mode 100644 lib/internal/quic/util.js create mode 100644 lib/quic.js create mode 100644 src/js_udp_wrap.cc create mode 100644 src/node_bob-inl.h create mode 100644 src/node_bob.h create mode 100644 src/quic/node_quic.cc create mode 100644 src/quic/node_quic_buffer-inl.h create mode 100644 src/quic/node_quic_buffer.cc create mode 100644 src/quic/node_quic_buffer.h create mode 100644 src/quic/node_quic_crypto.cc create mode 100644 src/quic/node_quic_crypto.h create mode 100644 src/quic/node_quic_default_application.cc create mode 100644 src/quic/node_quic_default_application.h create mode 100644 src/quic/node_quic_http3_application.cc create mode 100644 src/quic/node_quic_http3_application.h create mode 100644 src/quic/node_quic_session-inl.h create mode 100644 src/quic/node_quic_session.cc create mode 100644 src/quic/node_quic_session.h create mode 100644 src/quic/node_quic_socket-inl.h create mode 100644 src/quic/node_quic_socket.cc create mode 100644 src/quic/node_quic_socket.h create mode 100644 src/quic/node_quic_state.h create mode 100644 src/quic/node_quic_stream-inl.h create mode 100644 src/quic/node_quic_stream.cc create mode 100644 src/quic/node_quic_stream.h create mode 100644 src/quic/node_quic_util-inl.h create mode 100644 src/quic/node_quic_util.h create mode 100644 test/cctest/test_quic_buffer.cc create mode 100644 test/cctest/test_quic_cid.cc create mode 100644 test/cctest/test_quic_verifyhostnameidentity.cc create mode 100644 test/common/quic.js create mode 100644 test/common/udppair.js create mode 100644 test/parallel/test-quic-binding.js create mode 100644 test/parallel/test-quic-client-connect-multiple-parallel.js create mode 100644 test/parallel/test-quic-client-connect-multiple-sequential.js create mode 100644 test/parallel/test-quic-client-empty-preferred-address.js create mode 100644 test/parallel/test-quic-client-server.js create mode 100644 test/parallel/test-quic-errors-quicsession-openstream.js create mode 100644 test/parallel/test-quic-errors-quicsocket-connect.js create mode 100644 test/parallel/test-quic-errors-quicsocket-create.js create mode 100644 test/parallel/test-quic-errors-quicsocket-listen.js create mode 100644 test/parallel/test-quic-http3-client-server.js create mode 100644 test/parallel/test-quic-http3-push.js create mode 100644 test/parallel/test-quic-http3-trailers.js create mode 100644 test/parallel/test-quic-idle-timeout.js create mode 100644 test/parallel/test-quic-ipv6only.js create mode 100644 test/parallel/test-quic-keylog.js create mode 100644 test/parallel/test-quic-maxconnectionsperhost.js create mode 100644 test/parallel/test-quic-process-cleanup.js create mode 100644 test/parallel/test-quic-qlog.js create mode 100644 test/parallel/test-quic-quicendpoint-address.js create mode 100644 test/parallel/test-quic-quicession-server-openstream-pending.js create mode 100644 test/parallel/test-quic-quicsession-openstream-pending.js create mode 100644 test/parallel/test-quic-quicsession-resume.js create mode 100644 test/parallel/test-quic-quicsession-send-fd.js create mode 100644 test/parallel/test-quic-quicsession-send-file-close-before-open.js create mode 100644 test/parallel/test-quic-quicsession-send-file-open-error-handled.js create mode 100644 test/parallel/test-quic-quicsession-send-file-open-error.js create mode 100644 test/parallel/test-quic-quicsession-server-destroy-early.js create mode 100644 test/parallel/test-quic-quicsocket-close.js create mode 100644 test/parallel/test-quic-quicsocket-packetloss-stream-rx.js create mode 100644 test/parallel/test-quic-quicsocket-packetloss-stream-tx.js create mode 100644 test/parallel/test-quic-quicsocket-serverbusy.js create mode 100644 test/parallel/test-quic-quicsocket.js create mode 100644 test/parallel/test-quic-quicstream-close-early.js create mode 100644 test/parallel/test-quic-quicstream-destroy.js create mode 100644 test/parallel/test-quic-quicstream-identifiers.js create mode 100644 test/parallel/test-quic-simple-client-migrate.js create mode 100644 test/parallel/test-quic-statelessreset.js create mode 100644 test/parallel/test-quic-with-fake-udp.js create mode 100644 test/pummel/test-heapdump-quic.js create mode 100644 test/sequential/test-quic-preferred-address-ipv6.js create mode 100644 test/sequential/test-quic-preferred-address.js diff --git a/LICENSE b/LICENSE index 9d65fc76cb949e..8ae68e30e40ca0 100644 --- a/LICENSE +++ b/LICENSE @@ -1316,6 +1316,58 @@ The externally maintained libraries used by Node.js are: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ +- ngtcp2, located at deps/ngtcp2, is licensed as follows: + """ + The MIT License + + Copyright (c) 2016 ngtcp2 contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- nghttp3, located at deps/nghttp3, is licensed as follows: + """ + The MIT License + + Copyright (c) 2019 nghttp3 contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + - node-inspect, located at deps/node-inspect, is licensed as follows: """ Copyright Node.js contributors. All rights reserved. diff --git a/configure.py b/configure.py index 0190e31b41a214..575860e1fd3495 100755 --- a/configure.py +++ b/configure.py @@ -117,6 +117,11 @@ choices=valid_os, help='operating system to build for ({0})'.format(', '.join(valid_os))) +parser.add_option('--experimental-quic', + action='store_true', + dest='experimental_quic', + help='enable experimental quic support') + parser.add_option('--gdb', action='store_true', dest='gdb', @@ -259,6 +264,48 @@ dest='shared_nghttp2_libpath', help='a directory to search for the shared nghttp2 DLLs') +shared_optgroup.add_option('--shared-ngtcp2', + action='store_true', + dest='shared_ngtcp2', + help='link to a shared ngtcp2 DLL instead of static linking') + +shared_optgroup.add_option('--shared-ngtcp2-includes', + action='store', + dest='shared_ngtcp2_includes', + help='directory containing ngtcp2 header files') + +shared_optgroup.add_option('--shared-ngtcp2-libname', + action='store', + dest='shared_ngtcp2_libname', + default='ngtcp2', + help='alternative lib name to link to [default: %default]') + +shared_optgroup.add_option('--shared-ngtcp2-libpath', + action='store', + dest='shared_ngtcp2_libpath', + help='a directory to search for the shared ngtcp2 DLLs') + +shared_optgroup.add_option('--shared-nghttp3', + action='store_true', + dest='shared_nghttp3', + help='link to a shared nghttp3 DLL instead of static linking') + +shared_optgroup.add_option('--shared-nghttp3-includes', + action='store', + dest='shared_nghttp3_includes', + help='directory containing nghttp3 header files') + +shared_optgroup.add_option('--shared-nghttp3-libname', + action='store', + dest='shared_nghttp3_libname', + default='nghttp3', + help='alternative lib name to link to [default: %default]') + +shared_optgroup.add_option('--shared-nghttp3-libpath', + action='store', + dest='shared_nghttp3_libpath', + help='a directory to search for the shared nghttp3 DLLs') + shared_optgroup.add_option('--shared-openssl', action='store_true', dest='shared_openssl', @@ -1146,6 +1193,14 @@ def configure_node(o): else: o['variables']['debug_nghttp2'] = 'false' + if options.experimental_quic: + if options.shared_openssl: + raise Exception('QUIC requires modified version of OpenSSL and cannot be' + ' enabled with --shared-openssl.') + o['variables']['experimental_quic'] = 1 + else: + o['variables']['experimental_quic'] = 'false' + o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) o['variables']['node_shared'] = b(options.shared) @@ -1273,6 +1328,8 @@ def without_ssl_error(option): without_ssl_error('--openssl-no-asm') if options.openssl_fips: without_ssl_error('--openssl-fips') + if options.experimental_quic: + without_ssl_error('--experimental-quic') return if options.use_openssl_ca_store: diff --git a/deps/openssl/openssl.gyp b/deps/openssl/openssl.gyp index 4609d83baabac1..3dfe0b8c8a01f7 100644 --- a/deps/openssl/openssl.gyp +++ b/deps/openssl/openssl.gyp @@ -16,6 +16,13 @@ 'OPENSSL_NO_HW', ], 'conditions': [ + [ + # Disable building QUIC support in openssl if experimental_quic + # is not enabled. + 'experimental_quic!=1', { + 'defines': ['OPENSSL_NO_QUIC=1'], + } + ], [ 'openssl_no_asm==1', { 'includes': ['./openssl_no_asm.gypi'], }, 'target_arch=="arm64" and OS=="win"', { diff --git a/doc/api/errors.md b/doc/api/errors.md index 03853cd18f543f..fe04156e57f6b1 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1674,6 +1674,125 @@ The `package.json` [exports][] field does not export the requested subpath. Because exports are encapsulated, private internal modules that are not exported cannot be imported through the package resolution, unless using an absolute URL. + +### `ERR_QUIC_CANNOT_SET_GROUPS` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUIC_ERROR` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUIC_TLS13_REQUIRED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICCLIENTSESSION_FAILED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICCLIENTSESSION_FAILED_SETSOCKET` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_INVALID_DCID` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_UPDATEKEY` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSESSION_VERSION_NEGOTIATION` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_LISTENING` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSOCKET_UNBOUND` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_DESTROYED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_INVALID_PUSH` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_OPEN_FAILED` + +> Stability: 1 - Experimental + +TBD + + +### `ERR_QUICSTREAM_UNSUPPORTED_PUSH` + +> Stability: 1 - Experimental + +TBD + ### `ERR_REQUIRE_ESM` diff --git a/doc/api/index.md b/doc/api/index.md index 6a39e2102ce14c..6ff590912e17f9 100644 --- a/doc/api/index.md +++ b/doc/api/index.md @@ -43,6 +43,7 @@ * [Process](process.html) * [Punycode](punycode.html) * [Query Strings](querystring.html) +* [QUIC](quic.html) * [Readline](readline.html) * [REPL](repl.html) * [Report](report.html) diff --git a/doc/api/quic.md b/doc/api/quic.md new file mode 100644 index 00000000000000..48506272cc34f8 --- /dev/null +++ b/doc/api/quic.md @@ -0,0 +1,2273 @@ +# QUIC + + + +> Stability: 1 - Experimental + +The `quic` module provides an implementation of the QUIC protocol. To +access it: + +```js +const quic = require('quic'); +``` + +## Example + +```js +'use strict'; + +const key = getTLSKeySomehow(); +const cert = getTLSCertSomehow(); + +const { createSocket } = require('quic'); + +// Create the QUIC UDP IPv4 socket bound to local IP port 1234 +const socket = createSocket({ endpoint: { port: 1234 } }); + +// Tell the socket to operate as a server using the given +// key and certificate to secure new connections, using +// the fictional 'hello' application protocol. +socket.listen({ key, cert, alpn: 'hello' }); + +socket.on('session', (session) => { + // A new server side session has been created! + + session.on('secure', () => { + // Once the TLS handshake is completed, we can + // open streams... + const uni = session.openStream({ halfOpen: true }); + uni.write('hi '); + uni.end('from the server!'); + }); + + // The peer opened a new stream! + session.on('stream', (stream) => { + // Let's say hello + stream.end('Hello World'); + + // Let's see what the peer has to say... + stream.setEncoding('utf8'); + stream.on('data', console.log); + stream.on('end', () => console.log('stream ended')); + }); +}); + +socket.on('listening', () => { + // The socket is listening for sessions! +}); +``` + +## QUIC Basics + +QUIC is a UDP-based network transport protocol that includes built-in security +via TLS 1.3, flow control, error correction, connection migration, +multiplexing, and more. + +Within the Node.js implementation of the QUIC protocol, there are three main +components: the `QuicSocket`, the `QuicSession` and the `QuicStream`. + +### QuicSocket + +A `QuicSocket` encapsulates a binding to one or more local UDP ports. It is +used to send data to, and receive data from, remote endpoints. Once created, +a `QuicSocket` is associated with a local network address and IP port and can +act as both a QUIC client and server simultaneously. User code at the +JavaScript level interacts with the `QuicSocket` object to: + +* Query or modified the properties of the local UDP binding; +* Create client `QuicSession` instances; +* Wait for server `QuicSession` instances; or +* Query activity statistics + +Unlike the `net.Socket` and `tls.TLSSocket`, a `QuicSocket` instance cannot be +directly used by user code at the JavaScript level to send or receive data over +the network. + +### Client and Server QuicSessions + +A `QuicSession` represents a logical connection between two QUIC endpoints (a +client and a server). In the JavaScript API, each is represented by the +`QuicClientSession` and `QuicServerSession` specializations. + +At any given time, a `QuicSession` exists is one of four possible states: + +* `Initial` - Entered as soon as the `QuicSession` is created. +* `Handshake` - Entered as soon as the TLS 1.3 handshake between the client and + server begins. The handshake is always initiated by the client. +* `Ready` - Entered as soon as the TLS 1.3 handshake completes. Once the + `QuicSession` enters the `Ready` state, it may be used to exchange + application data using `QuicStream` instances. +* `Closed` - Entere as soon as the `QuicSession` connection has been + terminated. + +New instances of `QuicClientSession` are created using the `connect()` +function on a `QuicSocket` as in the example below: + +```js +const { createSocket } = require('quic'); + +// Create a QuicSocket associated with localhost and port 1234 +const socket = createSocket({ endpoint: { port: 1234 } }); + +const client = socket.connect({ + address: 'example.com', + port: 4567, + alpn: 'foo' +}); +``` + +As soon as the `QuicClientSession` is created, the `address` provided in +the connect options will be resolved to an IP address (if necessary), and +the TLS 1.3 handshake will begin. The `QuicClientSession` cannot be used +to exchange application data until after the `'secure'` event has been +emitted by the `QuicClientSession` object, signaling the completion of +the TLS 1.3 handshake. + +```js +client.on('secure', () => { + // The QuicClientSession can now be used for application data +}); +``` + +New instances of `QuicServerSession` are created internally by the +`QuicSocket` if it has been configured to listen for new connections +using the `listen()` method. + +```js +const key = getTLSKeySomehow(); +const cert = getTLSCertSomehow(); + +socket.listen({ + key, + cert, + alpn: 'foo' +}); + +socket.on('session', (session) => { + session.on('secure', () => { + // The QuicServerSession can now be used for application data + }); +}); +``` + +As with client `QuicSession` instances, the `QuicServerSession` cannot be +used to exhange application data until the `'secure'` event has been emitted. + +### QuicSession and ALPN + +QUIC uses the TLS 1.3 [ALPN][] ("Application-Layer Protocol Negotiation") +extension to identify the application level protocol that is using the QUIC +connection. Every `QuicSession` instance has an ALPN identifier that *must* be +specified in either the `connect()` or `listen()` options. ALPN identifiers that +are known to Node.js (such as the ALPN identifier for HTTP/3) will alter how the +`QuicSession` and `QuicStream` objects operate internally, but the QUIC +implementation for Node.js has been designed to allow any ALPN to be specified +and used. + +### QuicStream + +Once a `QuicSession` transitions to the `Ready` state, `QuicStream` instances +may be created and used to exchange application data. On a general level, all +`QuicStream` instances are simply Node.js Duplex Streams that allow +bidirectional data flow between the QUIC client and server. However, the +application protocol negotiated for the `QuicSession` may alter the semantics +and operation of a `QuicStream` associated with the session. Specifically, +some features of the `QuicStream` (e.g. headers) are enabled only if the +application protocol selected is known by Node.js to support those features. + +Once the `QuicSession` is ready, a `QuicStream` may be created by either the +client or server, and may be unidirectional or bidirectional. + +The `openStream()` method is used to create a new `QuicStream`: + +```js +// Create a new bidirectional stream +const stream1 = session.openStream(); + +// Create a new unidirectional stream +const stream2 = session.openStream({ halfOpen: true }); +``` + +As suggested by the names, a bidirectional stream allows data to be sent on +a stream in both directions, by both client and server, regardless of which +peer opened the stream. A unidirectional stream can be written to only by the +QuicSession that opened it. + +The `'stream'` event is emitted by the `QuicSession` when a new `QuicStream` +has been initated by the connected peer: + +```js +session.on('stream', (stream) => { + if (stream.bidirectional) { + stream.write('Hello World'); + stream.end(); + } + stream.on('data', console.log); + stream.on('end', () => {}); +}); +``` + +#### QuicStream Headers + +Some QUIC application protocols (like HTTP/3) make use of headers. + +There are four kinds of headers that the Node.js QUIC implementation +is capable of handling dependent entirely on known application protocol +support: + +* Informational Headers +* Initial Headers +* Trailing Headers +* Push Headers + +These categories correlate exactly with the equivalent HTTP +concepts: + +* Informational Headers: Any response headers transmitted within + a block of headers using a `1xx` status code. +* Initial Headers: HTTP request or response headers +* Trailing Headers: A block of headers that follow the body of a + request or response. +* Push Promise Headers: A block of headers included in a promised + push stream. + +If headers are supported by the application protocol in use for +a given `QuicSession`, the `'initialHeaders'`, `'informationalHeaders'`, +and `'trailingHeaders'` events will be emitted by the `QuicStream` +object when headers are received; and the `submitInformationalHeaders()`, +`submitInitialHeaders()`, and `submitTrailingHeaders()` methods can be +used to send headers. + +## QUIC and HTTP/3 + +HTTP/3 is an application layer protocol that uses QUIC as the transport. + +TBD + +## QUIC JavaScript API + +### quic.createSocket(\[options\]) + + +* `options` {Object} + * `client` {Object} A default configuration for QUIC client sessions created + using `quicsocket.connect()`. + * `endpoint` {Object} An object describing the local address to bind to. + * `address` {string} The local address to bind to. This may be an IPv4 or + IPv6 address or a host name. If a host name is given, it will be resolved + to an IP address. + * `port` {number} The local port to bind to. + * `type` {string} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6, + respectively. + * `ipv6Only` {boolean} + * `lookup` {Function} A custom DNS lookup function. Default `dns.lookup()`. + * `maxConnections` {number} The maximum number of total active inbound + connections. + * `maxConnectionsPerHost` {number} The maximum number of inbound connections + allowed per remote host. Default: `100`. + * `maxStatelessResetsPerHost` {number} The maximum number of stateless + resets that the `QuicSocket` is permitted to send per remote host. + Default: `10`. + * `qlog` {boolean} Whether to emit ['qlog'][] events for incoming sessions. + (For outgoing client sessions, set `client.qlog`.) Default: `false`. + * `retryTokenTimeout` {number} The maximum number of *seconds* for retry token + validation. Default: `10` seconds. + * `server` {Object} A default configuration for QUIC server sessions. + * `validateAddress` {boolean} When `true`, the `QuicSocket` will use explicit + address validation using a QUIC `RETRY` frame when listening for new server + sessions. Default: `false`. + * `validateAddressLRU` {boolean} When `true`, validation will be skipped if + the address has been recently validated. Currently, only the 10 most + recently validated addresses are remembered. Setting `validateAddressLRU` + to `true`, will enable the `validateAddress` option as well. Default: + `false`. + +The `quic.createSocket()` function is used to create new `QuicSocket` instances +associated with a local UDP address. + +### Class: QuicEndpoint + + +The `QuicEndpoint` wraps a local UDP binding used by a `QuicSocket` to send +and receive data. A single `QuicSocket` may be bound to multiple +`QuicEndpoint` instances at any given time. + +Users will not create instances of `QuicEndpoint` directly. + +#### quicendpoint.addMembership(address, iface) + + +* `address` {string} +* `iface` {string} + +Tells the kernel to join a multicast group at the given `multicastAddress` and +`multicastInterface` using the `IP_ADD_MEMBERSHIP` socket option. If the +`multicastInterface` argument is not specified, the operating system will +choose one interface and will add membership to it. To add membership to every +available interface, call `addMembership()` multiple times, once per +interface. + +#### quicendpoint.address + + +* Type: Address + +An object containing the address information for a bound `QuicEndpoint`. + +The object will contain the properties: + +* `address` {string} The local IPv4 or IPv6 address to which the `QuicEndpoint` is + bound. +* `family` {string} Either `'IPv4'` or `'IPv6'`. +* `port` {number} The local IP port to which the `QuicEndpoint` is bound. + +If the `QuicEndpoint` is not bound, `quicendpoint.address` is an empty object. + +#### quicendpoint.bound + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is bound to the local UDP port. + +#### quicendpoint.closing + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is in the process of closing. + +#### quicendpoint.destroy(\[error\]) + + +* `error` {Object} An `Error` object. + +Closes and destroys the `QuicEndpoint` instance making it usuable. + +#### quicendpoint.destroyed + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` has been destroyed. + +#### quicendpoint.dropMembership(address, iface) + + +* `address` {string} +* `iface` {string} + +Instructs the kernel to leave a multicast group at `multicastAddress` using the +`IP_DROP_MEMBERSHIP` socket option. This method is automatically called by the +kernel when the socket is closed or the process terminates, so most apps will +never have reason to call this. + +If `multicastInterface` is not specified, the operating system will attempt to +drop membership on all valid interfaces. + +#### quicendpoint.fd + + +* Type: {integer} + +The system file descriptor the `QuicEndpoint` is bound to. This property +is not set on Windows. + +#### quicendpoint.pending + + +* Type: {boolean} + +Set to `true` if the `QuicEndpoint` is in the process of binding to +the local UDP port. + +#### quicendpoint.ref() + + +#### quicendpoint.setBroadcast(\[on\]) + + +* `on` {boolean} + +Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP +packets may be sent to a local interface's broadcast address. + +#### quicendpoint.setMulticastInterface(iface) + + +* `iface` {string} + +All references to scope in this section are referring to IPv6 Zone Indices, +which are defined by [RFC 4007][]. In string form, an IP with a scope index +is written as `'IP%scope'` where scope is an interface name or interface +number. + +Sets the default outgoing multicast interface of the socket to a chosen +interface or back to system interface selection. The multicastInterface must +be a valid string representation of an IP from the socket's family. + +For IPv4 sockets, this should be the IP configured for the desired physical +interface. All packets sent to multicast on the socket will be sent on the +interface determined by the most recent successful use of this call. + +For IPv6 sockets, multicastInterface should include a scope to indicate the +interface as in the examples that follow. In IPv6, individual send calls can +also use explicit scope in addresses, so only packets sent to a multicast +address without specifying an explicit scope are affected by the most recent +successful use of this call. + +##### Examples: IPv6 Outgoing Multicast Interface + +On most systems, where scope format uses the interface name: + +```js +const socket = quic.createSocket({ endpoint: { type: 'udp6', port: 1234 } }); + +socket.on('ready', () => { + socket.endpoints[0].setMulticastInterface('::%eth1'); +}); +``` + +On Windows, where scope format uses an interface number: + +```js +const socket = quic.createSocket({ endpoint: { type: 'udp6', port: 1234 } }); + +socket.on('ready', () => { + socket.endpoints[0].setMulticastInterface('::%2'); +}); +``` + +##### Example: IPv4 Outgoing Multicast Interface + +All systems use an IP of the host on the desired physical interface: + +```js +const socket = quic.createSocket({ endpoint: { type: 'udp4', port: 1234 } }); + +socket.on('ready', () => { + socket.endpoints[0].setMulticastInterface('10.0.0.2'); +}); +``` + +##### Call Results + +A call on a socket that is not ready to send or no longer open may throw a +Not running Error. + +If multicastInterface can not be parsed into an IP then an `EINVAL` System +Error is thrown. + +On IPv4, if `multicastInterface` is a valid address but does not match any +interface, or if the address does not match the family then a System Error +such as `EADDRNOTAVAIL` or `EPROTONOSUP` is thrown. + +On IPv6, most errors with specifying or omitting scope will result in the +socket continuing to use (or returning to) the system's default interface +selection. + +A socket's address family's ANY address (IPv4 `'0.0.0.0'` or IPv6 `'::'`) +can be used to return control of the sockets default outgoing interface to +the system for future multicast packets. + +#### quicendpoint.setMulticastLoopback(\[on\]) + + +* `on` {boolean} + +Sets or clears the `IP_MULTICAST_LOOP` socket option. When set to `true`, +multicast packets will also be received on the local interface. + +#### quicendpoint.setMulticastTTL(ttl) + + +* `ttl` {number} + +Sets the `IP_MULTICAST_TTL` socket option. While TTL generally stands for +"Time to Live", in this context it specifies the number of IP hops that a +packet is allowed to travel through, specifically for multicast traffic. Each +router or gateway that forwards a packet decrements the TTL. If the TTL is +decremented to `0` by a router, it will not be forwarded. + +The argument passed to `setMulticastTTL()` is a number of hops between +`0` and `255`. The default on most systems is `1` but can vary. + +#### quicendpoint.setTTL(ttl) + + +* `ttl` {number} + +Sets the `IP_TTL` socket option. While TTL generally stands for "Time to Live", +in this context it specifies the number of IP hops that a packet is allowed to +travel through. Each router or gateway that forwards a packet decrements the +TTL. If the TTL is decremented to `0` by a router, it will not be forwarded. +Changing TTL values is typically done for network probes or when multicasting. + +The argument to `setTTL()` is a number of hops between `1` and `255`. +The default on most systems is `64` but can vary. + +#### quicendpoint.unref() + + +### Class: QuicSession extends EventEmitter + +* Extends: {EventEmitter} + +The `QuicSession` is an abstract base class that defines events, methods, and +properties that are shared by both `QuicClientSession` and `QuicServerSession`. + +Users will not create instances of `QuicSession` directly. + +#### Event: `'close'` + + +Emitted after the `QuicSession` has been destroyed and is no longer usable. + +The `'close'` event will not be emitted more than once. + +#### Event: `'error'` + + +Emitted immediately before the `'close'` event if the `QuicSession` was +destroyed with an error. + +The callback will be invoked with a single argument: + +* `error` {Object} An `Error` object. + +The `'error'` event will not be emitted more than once. + +#### Event: `'keylog'` + + +Emitted when key material is generated or received by a `QuicSession` +(typically during or immediately following the handshake process). This keying +material can be stored for debugging, as it allows captured TLS traffic to be +decrypted. It may be emitted multiple times per `QuicSession` instance. + +The callback will be invoked with a single argument: + +* `line` Line of ASCII text, in NSS SSLKEYLOGFILE format. + +A typical use case is to append received lines to a common text file, which is +later used by software (such as Wireshark) to decrypt the traffic: + +```js +const log = fs.createWriteStream('/tmp/ssl-keys.log', { flags: 'a' }); +// ... +session.on('keylog', (line) => log.write(line)); +``` + +The `'keylog'` event will be emitted multiple times. + +#### Event: `'pathValidation'` + + +Emitted when a path validation result has been determined. This event +is strictly informational. When path validation is successful, the +`QuicSession` will automatically update to use the new validated path. + +The callback will be invoked with three arguments: + +* `result` {string} Either `'failure'` or `'success'`, denoting the status + of the path challenge. +* `local` {Object} The local address component of the tested path. +* `remote` {Object} The remote address component of the tested path. + +The `'pathValidation'` event will be emitted multiple times. + +#### Event: `'qlog'` + + +* `jsonChunk` {string} A JSON fragment. + +Emitted if the `qlog: true` option was passed to `quicsocket.connect()` or +`quic.createSocket()` functions. + +The argument is a JSON fragment according to the [qlog standard][]. + +The `'qlog'` event will be emitted multiple times. + +#### Event: `'secure'` + + +Emitted after the TLS handshake has been completed. + +The callback will be invoked with two arguments: + +* `servername` {string} The SNI servername requested by the client. +* `alpnProtocol` {string} The negotiated ALPN protocol. +* `cipher` {Object} Information about the selected cipher algorithm. + * `name` {string} The cipher algorithm name. + * `version` {string} The TLS version (currently always `'TLSv1.3'`). + +These will also be available using the `quicsession.servername`, +`quicsession.alpnProtocol`, and `quicsession.cipher` properties. + +The `'secure'` event will not be emitted more than once. + +#### Event: `'stream'` + + +Emitted when a new `QuicStream` has been initiated by the connected peer. + +The `'stream'` event may be emitted multiple times. + +#### quicsession.ackDelayRetransmitCount + + +* Type: {BigInt} + +A `BigInt` representing the number of retransmissions caused by delayed +acknowledgements. + +#### quicsession.address + + +* Type: {Object} + * `address` {string} The local IPv4 or IPv6 address to which the `QuicSession` + is bound. + * `family` {string} Either `'IPv4'` or `'IPv6'`. + * `port` {number} The local IP port to which the `QuicSocket` is bound. + +An object containing the local address information for the `QuicSocket` to which +the `QuicSession` is currently associated. + +#### quicsession.alpnProtocol + + +* Type: {string} + +The ALPN protocol identifier negotiated for this session. + +#### quicsession.authenticated + +* Type: {boolean} + +True if the certificate provided by the peer during the TLS 1.3 +handshake has been verified. + +#### quicsession.authenticationError + +* Type: {Object} An error object + +If `quicsession.authenticated` is false, returns an `Error` object +representing the reason the peer certificate verification failed. + +#### quicsession.bidiStreamCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of bidirectional streams +created for this `QuicSession`. + +#### quicsession.blockCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of times the `QuicSession` has +been blocked from sending stream data due to flow control. + +Such blocks indicate that transmitted stream data is not being consumed +quickly enough by the connected peer. + +#### quicsession.bytesInFlight + + +* Type: {number} + +The total number of unacknowledged bytes this QUIC endpoint has transmitted +to the connected peer. + +#### quicsession.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes received from the peer. + +#### quicsession.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes sent to the peer. + +#### quicsession.cipher + + +* Type: {Object} + * `name` {string} The cipher algorithm name. + * `type` {string} The TLS version (currently always `'TLSv1.3'`). + +Information about the cipher algorithm selected for the session. + +#### quicsession.close(\[callback\]) + + +* `callback` {Function} Callback invoked when the close operation is completed + +Begins a graceful close of the `QuicSession`. Existing `QuicStream` instances +will be permitted to close naturally. New `QuicStream` instances will not be +permitted. Once all `QuicStream` instances have closed, the `QuicSession` +instance will be destroyed. + +#### quicsession.closeCode + +* Type: {Object} + * `code` {number} The error code reported when the `QuicSession` closed. + * `family` {number} The type of error code reported (`0` indicates a QUIC + protocol level error, `1` indicates a TLS error, `2` represents an + application level error.) + +#### quicsession.closing + + +* Type: {boolean} + +Set to `true` if the `QuicSession` is in the process of a graceful shutdown. + +#### quicsession.destroy(\[error\]) + + +* `error` {any} + +Destroys the `QuicSession` immediately causing the `close` event to be emitted. +If `error` is not `undefined`, the `error` event will be emitted immediately +before the `close` event. + +Any `QuicStream` instances that are still opened will be abruptly closed. + +#### quicsession.destroyed + + +* Type: {boolean} + +Set to `true` if the `QuicSession` has been destroyed. + +#### quicsession.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time the `QuicSession` was active. + +#### quicsession.getCertificate() + + +* Returns: {Object} A [Certificate Object][]. + +Returns an object representing the *local* certificate. The returned object has +some properties corresponding to the fields of the certificate. + +If there is no local certificate, or if the `QuicSession` has been destroyed, +an empty object will be returned. + +#### quicsession.getPeerCertificate(\[detailed\]) + + +* `detailed` {boolean} Include the full certificate chain if `true`, otherwise + include just the peer's certificate. **Default**: `false`. +* Returns: {Object} A [Certificate Object][]. + +Returns an object representing the peer's certificate. If the peer does not +provide a certificate, or if the `QuicSession` has been destroyed, an empty +object will be returned. + +If the full certificate chain was requested (`details` equals `true`), each +certificate will include an `issuerCertificate` property containing an object +representing the issuer's certificate. + +#### quicsession.handshakeAckHistogram + + +TBD + +#### quicsession.handshakeContinuationHistogram + + +TBD + +#### quicsession.handshakeComplete + + +* Type: {boolean} + +Set to `true` if the TLS handshake has completed. + +#### quicsession.handshakeConfirmed + + +* Type: {boolean} + +Set to `true` when the TLS handshake completion has been confirmed. + +#### quicsession.handshakeDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time taken to complete the TLS handshake. + +#### quicsession.idleTimeout + + +* Type: {boolean} + +Set to `true` if the `QuicSession` was closed due to an idle timeout. + +#### quicsession.keyUpdateCount + + +* Type: {BigInt} + +A `BigInt` representing the number of key update operations that have +occured. + +#### quicsession.latestRTT + + +* Type: {BigInt} + +The most recently recorded RTT for this `QuicSession`. + +#### quicsession.lossRetransmitCount + + +* Type: {BigInt} + +A `BigInt` representing the number of lost-packet retransmissions that have been +performed on this `QuicSession`. + +#### quicsession.maxDataLeft + + +* Type: {number} + +The total number of bytes the `QuicSession` is *currently* allowed to +send to the connected peer. + +#### quicsession.maxInFlightBytes + + +* Type: {BigInt} + +A `BigInt` representing the maximum number of in-flight bytes recorded +for this `QuicSession`. + +#### quicsession.maxStreams + + +* Type: {Object} + * `uni` {number} The maximum number of unidirectional streams. + * `bidi` {number} The maximum number of bidirectional streams. + +The highest cumulative number of bidirectional and unidirectional streams +that can currently be opened. The values are set initially by configuration +parameters when the `QuicSession` is created, then updated over the lifespan +of the `QuicSession` as the connected peer allows new streams to be created. + +#### quicsession.minRTT + + +* Type: {BigInt} + +The minimum RTT recorded so far for this `QuicSession`. + +#### quicsession.openStream(\[options\]) + +* `options` {Object} + * `halfOpen` {boolean} Set to `true` to open a unidirectional stream, `false` + to open a bidirectional stream. **Default**: `true`. + * `highWaterMark` {number} Total number of bytes that the `QuicStream` may + buffer internally before the `quicstream.write()` function starts returning + `false`. Default: `16384`. + * `defaultEncoding` {string} The default encoding that is used when no + encoding is specified as an argument to `quicstream.write()`. Default: + `'utf8'`. +* Returns: {QuicStream} + +Returns a new `QuicStream`. + +An error will be thrown if the `QuicSession` has been destroyed or is in the +process of a graceful shutdown. + +#### quicsession.ping() + + +The `ping()` method will trigger the underlying QUIC connection to serialize +any frames currently pending in the outbound queue if it is able to do so. +This has the effect of keeping the connection with the peer active and resets +the idle and retransmission timers. The `ping()` method is a best-effort +that ignores any errors that may occur during the serialization and send +operations. There is no return value and there is no way to monitor the status +of the `ping()` operation. + +#### quicsession.peerInitiatedStreamCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of `QuicStreams` initiated by the +connected peer. + +#### quicsession.remoteAddress + + +* Type: {Object} + * `address` {string} The local IPv4 or IPv6 address to which the `QuicSession` + is connected. + * `family` {string} Either `'IPv4'` or `'IPv6'`. + * `port` {number} The local IP port to which the `QuicSocket` is bound. + +An object containing the remote address information for the connected peer. + +#### quicsession.selfInitiatedStreamCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of `QuicStream` instances initiated +by this `QuicSession`. + +#### quicsession.servername + + +* Type: {string} + +The SNI servername requested for this session by the client. + +#### quicsession.smoothedRTT + + +* Type: {BigInt} + +The modified RTT calculated for this `QuicSession`. + +#### quicsession.socket + + +* Type: {QuicSocket} + +The `QuicSocket` the `QuicSession` is associated with. + +#### quicsession.statelessReset + + +* Type: {boolean} + +True if the `QuicSession` was closed due to QUIC stateless reset. + +#### quicsession.uniStreamCount + + +* Type: {BigInt} + +A `BigInt` representing the total number of unidirectional streams +created on this `QuicSession`. + +#### quicsession.updateKey() + + +* Returns: {boolean} `true` if the key update operation is successfully + initiated. + +Initiates QuicSession key update. + +An error will be thrown if called before `quicsession.handshakeConfirmed` +is equal to `true`. + +#### quicsession.usingEarlyData + + +* Type: {boolean} + +On server `QuicSession` instances, set to `true` on completion of the TLS +handshake if early data is enabled. On client `QuicSession` instances, +set to true on handshake completion if early data is enabled *and* was +accepted by the server. + +### Class: QuicClientSession extends QuicSession + + +* Extends: {QuicSession} + +The `QuicClientSession` class implements the client side of a QUIC connection. +Instances are created using the `quicsocket.connect()` method. + +#### Event: `'OCSPResponse'` + + +Emitted when the `QuicClientSession` receives a requested OCSP certificate +status response from the QUIC server peer. + +The callback is invoked with a single argument: + +* `response` {Buffer} + +Node.js does not perform any automatic validation or processing of the +response. + +The `'OCSPResponse'` event will not be emitted more than once. + +#### Event: `'sessionTicket'` + + +The `'sessionTicket'` event is emitted when a new TLS session ticket has been +generated for the current `QuicClientSession`. The callback is invoked with +two arguments: + +* `sessionTicket` {Buffer} The serialized session ticket. +* `remoteTransportParams` {Buffer} The serialized remote transport parameters + provided by the QUIC server. + +The `sessionTicket` and `remoteTransportParams` are useful when creating a new +`QuicClientSession` to more quickly resume an existing session. + +The `'sessionTicket'` event may be emitted multiple times. + +#### Event: `'usePreferredAddress'` + + +The `'usePreferredAddress'` event is emitted when the client `QuicSession` +is updated to use the server-advertised preferred address. The callback is +invoked with a single `address` argument: + +* `address` {Object} + * `address` {string} The preferred host name + * `port` {number} The preferred IP port + * `type` {string} Either `'udp4'` or `'udp6'`. + +This event is purely informational and will be emitted only when +`preferredAddressPolicy` is set to `'accept'`. + +The `'usePreferredAddress'` event will not be emitted more than once. + +#### quicclientsession.ephemeralKeyInfo + + +* Type: {Object} + +An object representing the type, name, and size of parameter of an ephemeral +key exchange in Perfect Forward Secrecy on a client connection. It is an +empty object when the key exchange is not ephemeral. The supported types are +`'DH'` and `'ECDH'`. The `name` property is available only when type is +`'ECDH'`. + +For example: `{ type: 'ECDH', name: 'prime256v1', size: 256 }`. + +#### quicclientsession.ready + + +* Type: {boolean} + +Set to `true` if the `QuicClientSession` is ready for use. False if the +`QuicSocket` has not yet been bound. + +#### quicclientsession.setSocket(socket, callback]) + + +* `socket` {QuicSocket} A `QuicSocket` instance to move this session to. +* `callback` {Function} A callback function that will be invoked once the + migration to the new `QuicSocket` is complete. + +Migrates the `QuicClientSession` to the given `QuicSocket` instance. If the new +`QuicSocket` has not yet been bound to a local UDP port, it will be bound prior +to attempting the migration. If the `QuicClientSession` is not yet ready to +migrate, the callback will be invoked with an `Error` using the code +`ERR_QUICCLIENTSESSION_FAILED_SETSOCKET`. + +### Class: QuicServerSession extends QuicSession + + +* Extends: {QuicSession} + +The `QuicServerSession` class implements the server side of a QUIC connection. +Instances are created internally and are emitted using the `QuicSocket` +`'session'` event. + +#### Event: `'clientHello'` + + +Emitted at the start of the TLS handshake when the `QuicServerSession` receives +the initial TLS Client Hello. + +The event handler is given a callback function that *must* be invoked for the +handshake to continue. + +The callback is invoked with four arguments: + +* `alpn` {string} The ALPN protocol identifier requested by the client. +* `servername` {string} The SNI servername requested by the client. +* `ciphers` {string[]} The list of TLS cipher algorithms requested by the + client. +* `callback` {Function} A callback function that must be called in order for + the TLS handshake to continue. + +The `'clientHello'` event will not be emitted more than once. + +#### Event: `'OCSPRequest'` + + +Emitted when the `QuicServerSession` has received a OCSP certificate status +request as part of the TLS handshake. + +The callback is invoked with three arguments: + +* `servername` {string} +* `context` {tls.SecureContext} +* `callback` {Function} + +The callback *must* be invoked in order for the TLS handshake to continue. + +The `'OCSPRequest'` event will not be emitted more than once. + +#### quicserversession.addContext(servername\[, context\]) + + +* `servername` {string} A DNS name to associate with the given context. +* `context` {tls.SecureContext} A TLS SecureContext to associate with the `servername`. + +TBD + +### Class: QuicSocket + + +New instances of `QuicSocket` are created using the `quic.createSocket()` +method. + +Once created, a `QuicSocket` can be configured to work as both a client and a +server. + +#### Event: `'busy'` + + +Emitted when the server busy state has been toggled using +`quicSocket.setServerBusy()`. The callback is invoked with a single +boolean argument indicating `true` if busy status is enabled, +`false` otherwise. This event is strictly informational. + +```js +const { createSocket } = require('quic'); + +const socket = createSocket(); + +socket.on('busy', (busy) => { + if (busy) + console.log('Server is busy'); + else + console.log('Server is not busy'); +}); + +socket.setServerBusy(true); +socket.setServerBusy(false); +``` + +This `'busy'` event may be emitted multiple times. + +#### Event: `'close'` + + +Emitted after the `QuicSocket` has been destroyed and is no longer usable. + +The `'close'` event will not be emitted multiple times. + +#### Event: `'error'` + + +Emitted before the `'close'` event if the `QuicSocket` was destroyed with an +`error`. + +The `'error'` event will not be emitted multiple times. + +#### Event: `'ready'` + + +Emitted once the `QuicSocket` has been bound to a local UDP port. + +The `'ready'` event will not be emitted multiple times. + +#### Event: `'session'` + + +Emitted when a new `QuicServerSession` has been created. + +The `'session'` event will be emitted multiple times. + +#### quicsocket.addEndpoint(options) + + +* `options`: {Object} An object describing the local address to bind to. + * `address` {string} The local address to bind to. This may be an IPv4 or + IPv6 address or a host name. If a host name is given, it will be resolved + to an IP address. + * `port` {number} The local port to bind to. + * `type` {string} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6, + respectively. + * `ipv6Only` {boolean} +* Returns: {QuicEndpoint} + +Creates and adds a new `QuicEndpoint` to the `QuicSocket` instance. + +#### quicsocket.bound + + +* Type: {boolean} + +Will be `true` if the `QuicSocket` has been successfully bound to the local UDP +port. + +#### quicsocket.boundDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been bound +to a local port. + +#### quicsocket.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the number of bytes received by this `QuicSocket`. + +#### quicsocket.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the number of bytes sent by this `QuicSocket`. + +#### quicsocket.clientSessions + + +* Type: {BigInt} + +A `BigInt` representing the number of client `QuicSession` instances that +have been associated with this `QuicSocket`. + +#### quicsocket.close(\[callback\]) + + +* `callback` {Function} + +Gracefully closes the `QuicSocket`. Existing `QuicSession` instances will be +permitted to close naturally. New `QuicClientSession` and `QuicServerSession` +instances will not be allowed. + +#### quicsocket.connect(\[options\]) + + +* `options` {Object} + * `address` {string} The domain name or IP address of the QUIC server + endpoint. + * `alpn` {string} An ALPN protocol identifier. + * `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA + certificates. Default is to trust the well-known CAs curated by Mozilla. + Mozilla's CAs are completely replaced when CAs are explicitly specified + using this option. The value can be a string or `Buffer`, or an `Array` of + strings and/or `Buffer`s. Any string or `Buffer` can contain multiple PEM + CAs concatenated together. The peer's certificate must be chainable to a CA + trusted by the server for the connection to be authenticated. When using + certificates that are not chainable to a well-known CA, the certificate's CA + must be explicitly specified as a trusted or the connection will fail to + authenticate. + If the peer uses a certificate that doesn't match or chain to one of the + default CAs, use the `ca` option to provide a CA certificate that the peer's + certificate can match or chain to. + For self-signed certificates, the certificate is its own CA, and must be + provided. + For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE", + "X509 CERTIFICATE", and "CERTIFICATE". + * `cert` {string|string[]|Buffer|Buffer[]} Cert chains in PEM format. One cert + chain should be provided per private key. Each cert chain should consist of + the PEM formatted certificate for a provided private `key`, followed by the + PEM formatted intermediate certificates (if any), in order, and not + including the root CA (the root CA must be pre-known to the peer, see `ca`). + When providing multiple cert chains, they do not have to be in the same + order as their private keys in `key`. If the intermediate certificates are + not provided, the peer will not be able to validate the certificate, and the + handshake will fail. + * `ciphers` {string} Cipher suite specification, replacing the default. For + more information, see [modifying the default cipher suite][]. Permitted + ciphers can be obtained via [`tls.getCiphers()`][]. Cipher names must be + uppercased in order for OpenSSL to accept them. + * `clientCertEngine` {string} Name of an OpenSSL engine which can provide the + client certificate. + * `crl` {string|string[]|Buffer|Buffer[]} PEM formatted CRLs (Certificate + Revocation Lists). + * `defaultEncoding` {string} The default encoding that is used when no + encoding is specified as an argument to `quicstream.write()`. Default: + `'utf8'`. + * `dhparam` {string|Buffer} Diffie Hellman parameters, required for + [Perfect Forward Secrecy][]. Use `openssl dhparam` to create the parameters. + The key length must be greater than or equal to 1024 bits, otherwise an + error will be thrown. It is strongly recommended to use 2048 bits or larger + for stronger security. If omitted or invalid, the parameters are silently + discarded and DHE ciphers will not be available. + * `ecdhCurve` {string} A string describing a named curve or a colon separated + list of curve NIDs or names, for example `P-521:P-384:P-256`, to use for + ECDH key agreement. Set to `auto` to select the + curve automatically. Use [`crypto.getCurves()`][] to obtain a list of + available curve names. On recent releases, `openssl ecparam -list_curves` + will also display the name and description of each available elliptic curve. + **Default:** [`tls.DEFAULT_ECDH_CURVE`][]. + * `highWaterMark` {number} Total number of bytes that the `QuicStream` may + buffer internally before the `quicstream.write()` function starts returning + `false`. Default: `16384`. + * `honorCipherOrder` {boolean} Attempt to use the server's cipher suite + preferences instead of the client's. When `true`, causes + `SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see + [OpenSSL Options][] for more information. + * `idleTimeout` {number} + * `ipv6Only` {boolean} + * `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format. + PEM allows the option of private keys being encrypted. Encrypted keys will + be decrypted with `options.passphrase`. Multiple keys using different + algorithms can be provided either as an array of unencrypted key strings or + buffers, or an array of objects in the form `{pem: [, + passphrase: ]}`. The object form can only occur in an array. + `object.passphrase` is optional. Encrypted keys will be decrypted with + `object.passphrase` if provided, or `options.passphrase` if it is not. + * `activeConnectionIdLimit` {number} Must be a value between `2` and `8` + (inclusive). Default: `2`. + * `maxAckDelay` {number} + * `maxData` {number} + * `maxPacketSize` {number} + * `maxStreamDataBidiLocal` {number} + * `maxStreamDataBidiRemote` {number} + * `maxStreamDataUni` {number} + * `maxStreamsBidi` {number} + * `maxStreamsUni` {number} + * `h3` {Object} HTTP/3 Specific Configuration Options + * `qpackMaxTableCapacity` {number} + * `qpackBlockedStreams` {number} + * `maxHeaderListSize` {number} + * `maxPushes` {number} + * `passphrase` {string} Shared passphrase used for a single private key and/or + a PFX. + * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded + private key and certificate chain. `pfx` is an alternative to providing + `key` and `cert` individually. PFX is usually encrypted, if it is, + `passphrase` will be used to decrypt it. Multiple PFX can be provided either + as an array of unencrypted PFX buffers, or an array of objects in the form + `{buf: [, passphrase: ]}`. The object form can only + occur in an array. `object.passphrase` is optional. Encrypted PFX will be + decrypted with `object.passphrase` if provided, or `options.passphrase` if + it is not. + * `port` {number} The IP port of the remote QUIC server. + * `preferredAddressPolicy` {string} `'accept'` or `'reject'`. When `'accept'`, + indicates that the client will automatically use the preferred address + advertised by the server. + * `remoteTransportParams` {Buffer|TypedArray|DataView} The serialized remote + transport parameters from a previously established session. These would + have been provided as part of the `'sessionTicket'` event on a previous + `QuicClientSession` object. + * `qlog` {boolean} Whether to emit ['qlog'][] events for this session. + Default: `false`. + * `requestOCSP` {boolean} If `true`, specifies that the OCSP status request + extension will be added to the client hello and an `'OCSPResponse'` event + will be emitted before establishing a secure communication. + * `secureOptions` {number} Optionally affect the OpenSSL protocol behavior, + which is not usually necessary. This should be used carefully if at all! + Value is a numeric bitmask of the `SSL_OP_*` options from + [OpenSSL Options][]. + * `servername` {string} The SNI servername. + * `sessionTicket`: {Buffer|TypedArray|DataView} The serialized TLS Session + Ticket from a previously established session. These would have been + provided as part of the `'sessionTicket`' event on a previous + `QuicClientSession` object. + * `type`: {string} Identifies the type of UDP socket. The value must either + be `'udp4'`, indicating UDP over IPv4, or `'udp6'`, indicating UDP over + IPv6. Defaults to `'udp4'`. + +Create a new `QuicClientSession`. This function can be called multiple times +to create sessions associated with different endpoints on the same +client endpoint. + +#### quicsocket.destroy(\[error\]) + + +* `error` {any} + +Destroys the `QuicSocket` then emits the `'close'` event when done. The `'error'` +event will be emitted after `'close'` if the `error` is not `undefined`. + +#### quicsocket.destroyed + + +* Type: {boolean} + +Will be `true` if the `QuicSocket` has been destroyed. + +#### quicsocket.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been active, + +#### quicsocket.endpoints + + +* Type: {QuicEndpoint[]} + +An array of `QuicEndpoint` instances associated with the `QuicSocket`. + +#### quicsocket.listen(\[options\]\[, callback\]) + + +* `options` {Object} + * `alpn` {string} A required ALPN protocol identifier. + * `ca` {string|string[]|Buffer|Buffer[]} Optionally override the trusted CA + certificates. Default is to trust the well-known CAs curated by Mozilla. + Mozilla's CAs are completely replaced when CAs are explicitly specified + using this option. The value can be a string or `Buffer`, or an `Array` of + strings and/or `Buffer`s. Any string or `Buffer` can contain multiple PEM + CAs concatenated together. The peer's certificate must be chainable to a CA + trusted by the server for the connection to be authenticated. When using + certificates that are not chainable to a well-known CA, the certificate's CA + must be explicitly specified as a trusted or the connection will fail to + authenticate. + If the peer uses a certificate that doesn't match or chain to one of the + default CAs, use the `ca` option to provide a CA certificate that the peer's + certificate can match or chain to. + For self-signed certificates, the certificate is its own CA, and must be + provided. + For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE", + "X509 CERTIFICATE", and "CERTIFICATE". + * `cert` {string|string[]|Buffer|Buffer[]} Cert chains in PEM format. One cert + chain should be provided per private key. Each cert chain should consist of + the PEM formatted certificate for a provided private `key`, followed by the + PEM formatted intermediate certificates (if any), in order, and not + including the root CA (the root CA must be pre-known to the peer, see `ca`). + When providing multiple cert chains, they do not have to be in the same + order as their private keys in `key`. If the intermediate certificates are + not provided, the peer will not be able to validate the certificate, and the + handshake will fail. + * `ciphers` {string} Cipher suite specification, replacing the default. For + more information, see [modifying the default cipher suite][]. Permitted + ciphers can be obtained via [`tls.getCiphers()`][]. Cipher names must be + uppercased in order for OpenSSL to accept them. + * `clientCertEngine` {string} Name of an OpenSSL engine which can provide the + client certificate. + * `crl` {string|string[]|Buffer|Buffer[]} PEM formatted CRLs (Certificate + Revocation Lists). + * `defaultEncoding` {string} The default encoding that is used when no + encoding is specified as an argument to `quicstream.write()`. Default: + `'utf8'`. + * `dhparam` {string|Buffer} Diffie Hellman parameters, required for + [Perfect Forward Secrecy][]. Use `openssl dhparam` to create the parameters. + The key length must be greater than or equal to 1024 bits, otherwise an + error will be thrown. It is strongly recommended to use 2048 bits or larger + for stronger security. If omitted or invalid, the parameters are silently + discarded and DHE ciphers will not be available. + * `earlyData` {boolean} Set to `false` to disable 0RTT early data. + Default: `true`. + * `ecdhCurve` {string} A string describing a named curve or a colon separated + list of curve NIDs or names, for example `P-521:P-384:P-256`, to use for + ECDH key agreement. Set to `auto` to select the + curve automatically. Use [`crypto.getCurves()`][] to obtain a list of + available curve names. On recent releases, `openssl ecparam -list_curves` + will also display the name and description of each available elliptic curve. + **Default:** [`tls.DEFAULT_ECDH_CURVE`][]. + * `highWaterMark` {number} Total number of bytes that `QuicStream` instances + may buffer internally before the `quicstream.write()` function starts + returning `false`. Default: `16384`. + * `honorCipherOrder` {boolean} Attempt to use the server's cipher suite + references instead of the client's. When `true`, causes + `SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see + [OpenSSL Options][] for more information. + * `idleTimeout` {number} + * `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format. + PEM allows the option of private keys being encrypted. Encrypted keys will + be decrypted with `options.passphrase`. Multiple keys using different + algorithms can be provided either as an array of unencrypted key strings or + buffers, or an array of objects in the form `{pem: [, + passphrase: ]}`. The object form can only occur in an array. + `object.passphrase` is optional. Encrypted keys will be decrypted with + `object.passphrase` if provided, or `options.passphrase` if it is not. + * `activeConnectionIdLimit` {number} + * `maxAckDelay` {number} + * `maxData` {number} + * `maxPacketSize` {number} + * `maxStreamsBidi` {number} + * `maxStreamsUni` {number} + * `maxStreamDataBidiLocal` {number} + * `maxStreamDataBidiRemote` {number} + * `maxStreamDataUni` {number} + * `passphrase` {string} Shared passphrase used for a single private key + and/or a PFX. + * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded + private key and certificate chain. `pfx` is an alternative to providing + `key` and `cert` individually. PFX is usually encrypted, if it is, + `passphrase` will be used to decrypt it. Multiple PFX can be provided either + as an array of unencrypted PFX buffers, or an array of objects in the form + `{buf: [, passphrase: ]}`. The object form can only + occur in an array. `object.passphrase` is optional. Encrypted PFX will be + decrypted with `object.passphrase` if provided, or `options.passphrase` if + it is not. + * `preferredAddress` {Object} + * `address` {string} + * `port` {number} + * `type` {string} `'udp4'` or `'udp6'`. + * `requestCert` {boolean} Request a certificate used to authenticate the + client. + * `rejectUnauthorized` {boolean} If not `false` the server will reject any + connection which is not authorized with the list of supplied CAs. This + option only has an effect if `requestCert` is `true`. Default: `true`. + * `secureOptions` {number} Optionally affect the OpenSSL protocol behavior, + which is not usually necessary. This should be used carefully if at all! + Value is a numeric bitmask of the `SSL_OP_*` options from + [OpenSSL Options][]. + * `sessionIdContext` {string} Opaque identifier used by servers to ensure + session state is not shared between applications. Unused by clients. + +* `callback` {Function} + +Listen for new peer-initiated sessions. + +If a `callback` is given, it is registered as a handler for the +`'session'` event. + +#### quicsocket.listenDuration + + +* Type: {BigInt} + +A `BigInt` representing the length of time this `QuicSocket` has been listening +for connections. + +#### quicsocket.listening + + +* Type: {boolean} + +Set to `true` if the `QuicSocket` is listening for new connections. + +#### quicsocket.packetsIgnored + + +* Type: {BigInt} + +A `BigInt` representing the number of packets received by this `QuicSocket` that +have been ignored. + +#### quicsocket.packetsReceived + + +* Type: {BigInt} + +A `BigInt` representing the number of packets successfully received by this +`QuicSocket`. + +#### quicsocket.packetsSent + + +* Type: {BigInt} + +A `BigInt` representing the number of packets sent by this `QuicSocket`. + +#### quicsocket.pending + + +* Type: {boolean} + +Set to `true` if the socket is not yet bound to the local UDP port. + +#### quicsocket.ref() + + +#### quicsocket.serverBusyCount + + +* Type: {BigInt} + +A `BigInt` representing the number of `QuicSession` instances rejected +due to server busy status. + +#### quicsocket.serverSessions + + +* Type: {BigInt} + +A `BigInt` representing the number of server `QuicSession` instances that +have been associated with this `QuicSocket`. + +#### quicsocket.setDiagnosticPacketLoss(options) + + +* `options` {Object} + * `rx` {number} A value in the range `0.0` to `1.0` that specifies the + probability of received packet loss. + * `tx` {number} A value in the range `0.0` to `1.0` that specifies the + probability of transmitted packet loss. + +The `quicsocket.setDiagnosticPacketLoss()` method is a diagnostic only tool +that can be used to *simulate* packet loss conditions for this `QuicSocket` +by artificially dropping received or transmitted packets. + +This method is *not* to be used in production applications. + +#### quicsocket.setServerBusy(\[on\]) + + +* `on` {boolean} When `true`, the `QuicSocket` will reject new connections. + **Defaults**: `true`. + +Calling `setServerBusy()` or `setServerBusy(true)` will tell the `QuicSocket` +to reject all new incoming connection requests using the `SERVER_BUSY` QUIC +error code. To begin receiving connections again, disable busy mode by calling +`setServerBusy(false)`. + +#### quicsocket.statelessResetCount + + +* Type: {BigInt} + +A `BigInt` that represents the number of stateless resets that have been sent. + +#### quicsocket.toggleStatelessReset() + + +* Returns {boolean} `true` if stateless reset processing is enabled; `false` + if disabled. + +By default, a listening `QuicSocket` will generate stateless reset tokens when +appropriate. The `disableStatelessReset` option may be set when the +`QuicSocket` is created to disable generation of stateless resets. The +`toggleStatelessReset()` function allows stateless reset to be turned on and +off dynamically through the lifetime of the `QuicSocket`. + +#### quicsocket.unref(); + + +### Class: QuicStream extends stream.Duplex + + +* Extends: {stream.Duplex} + +#### Event: `'blocked'` + + +Emitted when the `QuicStream` has been prevented from sending queued data for +the `QuicStream` due to congestion control. + +#### Event: `'close'` + + +Emitted when the `QuicStream` has is completely closed and the underlying +resources have been freed. + +#### Event: `'data'` + + +#### Event: `'end'` + + +#### Event: `'error'` + + +#### Event: `'informationalHeaders'` + + +Emitted when the `QuicStream` has received a block of informational headers. + +Support for headers depends entirely on the QUIC Application used as identified +by the `alpn` configuration option. In QUIC Applications that support headers, +informational header blocks typically come before initial headers. + +The event handler is invoked with a single argument representing the block of +Headers as an object. + +```js +stream('informationalHeaders', (headers) => { + // Use headers +}); +``` + +#### Event: `'initialHeaders'` + + +Emitted when the `QuicStream` has received a block of initial headers. + +Support for headers depends entirely on the QUIC Application used as identified +by the `alpn` configuration option. HTTP/3, for instance, supports two kinds of +initial headers: request headers for HTTP request messages and response headers +for HTTP response messages. For HTTP/3 QUIC streams, request and response +headers are each emitted using the `'initialHeaders'` event. + +The event handler is invoked with a single argument representing the block of +Headers as an object. + +```js +stream('initialHeaders', (headers) => { + // Use headers +}); +``` + +#### Event: `'ready'` + + +Emitted when the underlying `QuicSession` has emitted its `secure` event +this stream has received its id, which is accessible as `stream.id` once this +event is emitted. + +#### Event: `'trailingHeaders'` + + +Emitted when the `QuicStream` has received a block of trailing headers. + +Support for headers depends entirely on the QUIC Application used as identified +by the `alpn` configuration option. Trailing headers typically follow any data +transmitted on the `QuicStream`, and therefore typically emit sometime after the +last `'data'` event but before the `'close'` event. The precise timing may +vary from one QUIC application to another. + +The event handler is invoked with a single argument representing the block of +Headers as an object. + +```js +stream('trailingHeaders', (headers) => { + // Use headers +}); +``` + +#### Event: `'readable'` + + +#### quicstream.aborted + +* Type: {boolean} + +True if dataflow on the `QuicStream` was prematurely terminated. + +#### quicstream.bidirectional + + +* Type: {boolean} + +Set to `true` if the `QuicStream` is bidirectional. + +#### quicstream.bytesReceived + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes received for this +`QuicStream`. + +#### quicstream.bytesSent + + +* Type: {BigInt} + +A `BigInt` representing the total number of bytes sent by this +`QuicStream`. + +#### quicstream.clientInitiated + + +* Type: {boolean} + +Set to `true` if the `QuicStream` was initiated by a `QuicClientSession` +instance. + +#### quicstream.close(code) + + +* `code` {number} + +Closes the `QuicStream`. + +#### quicstream.dataAckHistogram + + +TBD + +#### quicstream.dataRateHistogram + + +TBD + +#### quicstream.dataSizeHistogram + +TBD + +#### quicstream.duration + + +* Type: {BigInt} + +A `BigInt` representing the length of time the `QuicStream` has been active. + +#### quicstream.finalSize + + +* Type: {BigInt} + +A `BigInt` specifying the total number of bytes successfully received by the +`QuicStream`. + +#### quicstream.id + + +* Type: {number} + +The numeric identifier of the `QuicStream`. + +#### quicstream.maxAcknowledgedOffset + + +* Type: {BigInt} + +A `BigInt` representing the highest acknowledged data offset received +for this `QuicStream`. + +#### quicstream.maxExtendedOffset + + +* Type: {BigInt} + +A `BigInt` representing the maximum extended data offset that has been +reported to the connected peer. + +#### quicstream.maxReceivedOffset + + +* Type: {BigInt} + +A `BigInt` representing the maximum received offset for this `QuicStream`. + +#### quicstream.pending + + +* {boolean} + +This property is `true` if the underlying session is not finished yet, +i.e. before the `'ready'` event is emitted. + +#### quicstream.pushStream(headers\[, options\]) + + +* `headers` {Object} An object representing a block of headers to be + transmitted with the push promise. +* `options` {Object} + * `highWaterMark` {number} Total number of bytes that the `QuicStream` may + buffer internally before the `quicstream.write()` function starts returning + `false`. Default: `16384`. + * `defaultEncoding` {string} The default encoding that is used when no + encoding is specified as an argument to `quicstream.write()`. Default: + `'utf8'`. + +* Returns: {QuicStream} + +If the selected QUIC application protocol supports push streams, then the +`pushStream()` method will initiate a new push promise and create a new +unidirectional `QuicStream` object used to fulfill that push. + +Currently only HTTP/3 supports the use of `pushStream()`. + +If the selected QUIC application protocol does not support push streams, an +error will be thrown. + +#### quicstream.serverInitiated + + +* Type: {boolean} + +Set to `true` if the `QuicStream` was initiated by a `QuicServerSession` +instance. + +#### quicstream.session + + +* Type: {QuicSession} + +The `QuicServerSession` or `QuicClientSession`. + +#### quicstream.sendFD(fd\[, options\]) + + +* `fd` {number|FileHandle} A readable file descriptor. +* `options` {Object} + * `offset` {number} The offset position at which to begin reading. + Default: `-1`. + * `length` {number} The amount of data from the fd to send. + Default: `-1`. + +Instead of using a `Quicstream` as a writable stream, send data from a given +file descriptor. + +If `offset` is set to a non-negative number, reading starts from that position +and the file offset will not be advanced. +If `length` is set to a non-negative number, it gives the maximum number of +bytes that are read from the file. + +The file descriptor or `FileHandle` is not closed when the stream is closed, +so it will need to be closed manually once it is no longer needed. +Using the same file descriptor concurrently for multiple streams +is not supported and may result in data loss. Re-using a file descriptor +after a stream has finished is supported. + +#### quicstream.sendFile(path\[, options\]) + + +* `path` {string|Buffer|URL} +* `options` {Object} + * `onError` {Function} Callback function invoked in the case of an + error before send. + * `offset` {number} The offset position at which to begin reading. + Default: `-1`. + * `length` {number} The amount of data from the fd to send. + Default: `-1`. + +Instead of using a `QuicStream` as a writable stream, send data from a given +file path. + +The `options.onError` callback will be called if the file could not be opened. +If `offset` is set to a non-negative number, reading starts from that position. +If `length` is set to a non-negative number, it gives the maximum number of +bytes that are read from the file. + +#### quicstream.submitInformationalHeaders(headers) + +* `headers` {Object} + +TBD + +#### quicstream.submitInitialHeaders(headers) + +* `headers` {Object} + +TBD + +#### quicstream.submitTrailingHeaders(headers) + +* `headers` {Object} + +TBD + +#### quicstream.unidirectional + + +* Type: {boolean} + +Set to `true` if the `QuicStream` is unidirectional. + +[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves +[`tls.DEFAULT_ECDH_CURVE`]: #tls_tls_default_ecdh_curve +[`tls.getCiphers()`]: tls.html#tls_tls_getciphers +[ALPN]: https://tools.ietf.org/html/rfc7301 +[RFC 4007]: https://tools.ietf.org/html/rfc4007 +[Certificate Object]: https://nodejs.org/dist/latest-v12.x/docs/api/tls.html#tls_certificate_object +[modifying the default cipher suite]: tls.html#tls_modifying_the_default_tls_cipher_suite +[OpenSSL Options]: crypto.html#crypto_openssl_options +[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy +['qlog']: #quic_event_qlog +[qlog standard]: https://tools.ietf.org/id/draft-marx-qlog-event-definitions-quic-h3-00.html diff --git a/lib/_http_client.js b/lib/_http_client.js index c447b695c8b45b..c075daf8507ad0 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -184,10 +184,11 @@ function ClientRequest(input, options, cb) { method = this.method = 'GET'; } - const maxHeaderSize = options.maxHeaderSize; - if (maxHeaderSize !== undefined) - validateInteger(maxHeaderSize, 'maxHeaderSize', 0); - this.maxHeaderSize = maxHeaderSize; + validateInteger( + options.maxHeaderSize, + 'maxHeaderSize', + { min: 0, allowUndefined: true }); + this.maxHeaderSize = options.maxHeaderSize; const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined && diff --git a/lib/_http_server.js b/lib/_http_server.js index 3f9c98e8380363..88b9a1015e77e8 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -330,10 +330,11 @@ function Server(options, requestListener) { this[kIncomingMessage] = options.IncomingMessage || IncomingMessage; this[kServerResponse] = options.ServerResponse || ServerResponse; - const maxHeaderSize = options.maxHeaderSize; - if (maxHeaderSize !== undefined) - validateInteger(maxHeaderSize, 'maxHeaderSize', 0); - this.maxHeaderSize = maxHeaderSize; + validateInteger( + options.maxHeaderSize, + 'maxHeaderSize', + { min: 0, allowUndefined: true }); + this.maxHeaderSize = options.maxHeaderSize; const insecureHTTPParser = options.insecureHTTPParser; if (insecureHTTPParser !== undefined && diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 561bb1f2d55581..3722ba52a6619b 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -70,11 +70,14 @@ const { ERR_TLS_INVALID_STATE } = codes; const { onpskexchange: kOnPskExchange } = internalBinding('symbols'); -const { getOptionValue } = require('internal/options'); +const { + getAllowUnauthorized, + getOptionValue, +} = require('internal/options'); const { validateString, validateBuffer, - validateUint32 + validateUint32, } = require('internal/validators'); const traceTls = getOptionValue('--trace-tls'); const tlsKeylog = getOptionValue('--tls-keylog'); @@ -1540,25 +1543,14 @@ function onConnectEnd() { } } -let warnOnAllowUnauthorized = true; - // Arguments: [port,] [host,] [options,] [cb] exports.connect = function connect(...args) { args = normalizeConnectArgs(args); let options = args[0]; const cb = args[1]; - const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; - - if (allowUnauthorized && warnOnAllowUnauthorized) { - warnOnAllowUnauthorized = false; - process.emitWarning('Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + - 'environment variable to \'0\' makes TLS connections ' + - 'and HTTPS requests insecure by disabling ' + - 'certificate verification.'); - } options = { - rejectUnauthorized: !allowUnauthorized, + rejectUnauthorized: !getAllowUnauthorized(), ciphers: tls.DEFAULT_CIPHERS, checkServerIdentity: tls.checkServerIdentity, minDHSize: 1024, diff --git a/lib/internal/crypto/scrypt.js b/lib/internal/crypto/scrypt.js index cd18f671b18b73..d8c9d67a10d822 100644 --- a/lib/internal/crypto/scrypt.js +++ b/lib/internal/crypto/scrypt.js @@ -108,7 +108,7 @@ function check(password, salt, keylen, options) { } if (options.maxmem !== undefined) { maxmem = options.maxmem; - validateInteger(maxmem, 'maxmem', 0); + validateInteger(maxmem, 'maxmem', { min: 0 }); } if (N === 0) N = defaults.N; if (r === 0) r = defaults.r; diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 9f9a0a66f2866e..4475a26bafabfb 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1253,6 +1253,61 @@ E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath) => { pkgPath}${sep}package.json`; } }, Error); +E('ERR_QUICCLIENTSESSION_FAILED', + 'Failed to create a new QuicClientSession: %s', Error); +E('ERR_QUICCLIENTSESSION_FAILED_SETSOCKET', + 'Failed to set the QuicSocket', Error); +E('ERR_QUICSESSION_DESTROYED', + 'Cannot call %s after a QuicSession has been destroyed', Error); +E('ERR_QUICSESSION_INVALID_DCID', 'Invalid DCID value: %s', Error); +E('ERR_QUICSESSION_UPDATEKEY', 'Unable to update QuicSession keys', Error); +E('ERR_QUICSESSION_VERSION_NEGOTIATION', + (version, requestedVersions, supportedVersions) => { + return 'QUIC session received version negotiation from server. ' + + `Version: ${version}. Requested: ${requestedVersions.join(', ')} ` + + `Supported: ${supportedVersions.join(', ')}`; + }, + Error); +E('ERR_QUICSOCKET_DESTROYED', + 'Cannot call %s after a QuicSocket has been destroyed', Error); +E('ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH', + 'The stateResetToken must be exactly 16-bytes in length', + Error); +E('ERR_QUICSOCKET_LISTENING', + 'This QuicSocket is already listening', Error); +E('ERR_QUICSOCKET_UNBOUND', + 'Cannot call %s before a QuicSocket has been bound', Error); +E('ERR_QUICSTREAM_DESTROYED', + 'Cannot call %s after a QuicStream has been destroyed', Error); +E('ERR_QUICSTREAM_INVALID_PUSH', + 'Push streams are only supported on client-initiated, bidirectional streams', + Error); +E('ERR_QUICSTREAM_OPEN_FAILED', 'Opening a new QuicStream failed', Error); +E('ERR_QUICSTREAM_UNSUPPORTED_PUSH', + 'Push streams are not supported on this QuicSession', Error); +E('ERR_QUIC_ERROR', function(code, family) { + const { + constants: { + QUIC_ERROR_APPLICATION, + QUIC_ERROR_CRYPTO, + QUIC_ERROR_SESSION, + } + } = internalBinding('quic'); + let familyType = 'unknown'; + switch (family) { + case QUIC_ERROR_APPLICATION: + familyType = 'application'; + break; + case QUIC_ERROR_CRYPTO: + familyType = 'crypto'; + break; + case QUIC_ERROR_SESSION: + familyType = 'session'; + break; + } + return `QUIC session closed with ${familyType} error code ${code}`; +}, Error); +E('ERR_QUIC_TLS13_REQUIRED', 'QUIC requires TLS version 1.3', Error); E('ERR_REQUIRE_ESM', (filename, parentPath = null, packageJsonPath = null) => { let msg = `Must use import to load ES Module: ${filename}`; diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index 2d73219a77772c..cd5a284010b2d3 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -110,11 +110,40 @@ function stripBOM(content) { } const builtinLibs = [ - 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto', - 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net', - 'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl', - 'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util', - 'v8', 'vm', 'worker_threads', 'zlib' + 'assert', + 'async_hooks', + 'buffer', + 'child_process', + 'cluster', + 'crypto', + 'dgram', + 'dns', + 'domain', + 'events', + 'fs', + 'http', + 'http2', + 'https', + 'net', + 'os', + 'path', + 'perf_hooks', + 'punycode', + 'quic', + 'querystring', + 'readline', + 'repl', + 'stream', + 'string_decoder', + 'tls', + 'trace_events', + 'tty', + 'url', + 'util', + 'v8', + 'vm', + 'worker_threads', + 'zlib', ]; if (internalBinding('config').experimentalWasi) { diff --git a/lib/internal/options.js b/lib/internal/options.js index e494787b96c088..03586f9dae6d76 100644 --- a/lib/internal/options.js +++ b/lib/internal/options.js @@ -3,6 +3,8 @@ const { getOptions } = internalBinding('options'); const { options, aliases } = getOptions(); +let warnOnAllowUnauthorized = true; + function getOptionValue(option) { const result = options.get(option); if (!result) { @@ -11,8 +13,23 @@ function getOptionValue(option) { return result.value; } +function getAllowUnauthorized() { + const allowUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0'; + + if (allowUnauthorized && warnOnAllowUnauthorized) { + warnOnAllowUnauthorized = false; + process.emitWarning( + 'Setting the NODE_TLS_REJECT_UNAUTHORIZED ' + + 'environment variable to \'0\' makes TLS connections ' + + 'and HTTPS requests insecure by disabling ' + + 'certificate verification.'); + } + return allowUnauthorized; +} + module.exports = { options, aliases, - getOptionValue + getOptionValue, + getAllowUnauthorized, }; diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js new file mode 100644 index 00000000000000..18f60820c596fb --- /dev/null +++ b/lib/internal/quic/core.js @@ -0,0 +1,3339 @@ +'use strict'; + +/* eslint-disable no-use-before-define */ + +const { + assertCrypto, + customInspectSymbol: kInspect, +} = require('internal/util'); + +assertCrypto(); + +const { + Array, + BigInt64Array, + Boolean, + Error, + Map, + RegExp, + Set, + Symbol, +} = primordials; + +const { Buffer } = require('buffer'); +const { isArrayBufferView } = require('internal/util/types'); +const { + getAllowUnauthorized, + getSocketType, + lookup4, + lookup6, + setTransportParams, + toggleListeners, + validateCloseCode, + validateTransportParams, + validateQuicClientSessionOptions, + validateQuicSocketOptions, + validateQuicStreamOptions, + validateQuicSocketListenOptions, + validateQuicEndpointOptions, + validateCreateSecureContextOptions, + validateQuicSocketConnectOptions, +} = require('internal/quic/util'); +const util = require('util'); +const assert = require('internal/assert'); +const EventEmitter = require('events'); +const fs = require('fs'); +const fsPromisesInternal = require('internal/fs/promises'); +const { Duplex } = require('stream'); +const { + createSecureContext: _createSecureContext +} = require('tls'); +const { + translatePeerCertificate +} = require('_tls_common'); +const { + defaultTriggerAsyncIdScope, + symbols: { + async_id_symbol, + owner_symbol, + }, +} = require('internal/async_hooks'); +const dgram = require('dgram'); +const internalDgram = require('internal/dgram'); +const { + assertValidPseudoHeader, + assertValidPseudoHeaderResponse, + assertValidPseudoHeaderTrailer, + mapToHeaders, +} = require('internal/http2/util'); + +const { + constants: { + UV_UDP_IPV6ONLY, + UV_UDP_REUSEADDR, + } +} = internalBinding('udp_wrap'); + +const { + writeGeneric, + writevGeneric, + onStreamRead, + kAfterAsyncWrite, + kMaybeDestroy, + kUpdateTimer, + kHandle, + setStreamTimeout // eslint-disable-line no-unused-vars +} = require('internal/stream_base_commons'); + +const { + ShutdownWrap, + kReadBytesOrError, + streamBaseState +} = internalBinding('stream_wrap'); + +const { + codes: { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_CALLBACK, + ERR_QUIC_ERROR, + ERR_QUICSESSION_DESTROYED, + ERR_QUICSESSION_VERSION_NEGOTIATION, + ERR_QUICSOCKET_DESTROYED, + ERR_QUICSOCKET_LISTENING, + ERR_QUICCLIENTSESSION_FAILED, + ERR_QUICCLIENTSESSION_FAILED_SETSOCKET, + ERR_QUICSESSION_UPDATEKEY, + ERR_QUICSTREAM_DESTROYED, + ERR_QUICSTREAM_INVALID_PUSH, + ERR_QUICSTREAM_UNSUPPORTED_PUSH, + ERR_QUICSTREAM_OPEN_FAILED, + ERR_TLS_DH_PARAM_SIZE, + }, + errnoException, + exceptionWithHostPort +} = require('internal/errors'); + +const { FileHandle } = internalBinding('fs'); +const { StreamPipe } = internalBinding('stream_pipe'); +const { UV_EOF } = internalBinding('uv'); + +const { + QuicSocket: QuicSocketHandle, + QuicEndpoint: QuicEndpointHandle, + initSecureContext, + initSecureContextClient, + createClientSession: _createClientSession, + openBidirectionalStream: _openBidirectionalStream, + openUnidirectionalStream: _openUnidirectionalStream, + setCallbacks, + constants: { + AF_INET, + AF_INET6, + IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, + IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, + IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, + IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, + IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, + IDX_QUIC_SESSION_STATS_CREATED_AT, + IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT, + IDX_QUIC_SESSION_STATS_BYTES_RECEIVED, + IDX_QUIC_SESSION_STATS_BYTES_SENT, + IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT, + IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT, + IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT, + IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT, + IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT, + IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT, + IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT, + IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT, + IDX_QUIC_SESSION_STATS_BLOCK_COUNT, + IDX_QUIC_SESSION_STATS_MIN_RTT, + IDX_QUIC_SESSION_STATS_SMOOTHED_RTT, + IDX_QUIC_SESSION_STATS_LATEST_RTT, + IDX_QUIC_STREAM_STATS_CREATED_AT, + IDX_QUIC_STREAM_STATS_BYTES_RECEIVED, + IDX_QUIC_STREAM_STATS_BYTES_SENT, + IDX_QUIC_STREAM_STATS_MAX_OFFSET, + IDX_QUIC_STREAM_STATS_FINAL_SIZE, + IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK, + IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV, + IDX_QUIC_SOCKET_STATS_CREATED_AT, + IDX_QUIC_SOCKET_STATS_BOUND_AT, + IDX_QUIC_SOCKET_STATS_LISTEN_AT, + IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED, + IDX_QUIC_SOCKET_STATS_BYTES_SENT, + IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED, + IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED, + IDX_QUIC_SOCKET_STATS_PACKETS_SENT, + IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS, + IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS, + IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT, + IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT, + ERR_FAILED_TO_CREATE_SESSION, + ERR_INVALID_REMOTE_TRANSPORT_PARAMS, + ERR_INVALID_TLS_SESSION_TICKET, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE, + NGTCP2_NO_ERROR, + QUIC_ERROR_APPLICATION, + QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED, + QUICSERVERSESSION_OPTION_REQUEST_CERT, + QUICCLIENTSESSION_OPTION_REQUEST_OCSP, + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY, + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS, + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU, + QUICSTREAM_HEADERS_KIND_NONE, + QUICSTREAM_HEADERS_KIND_INFORMATIONAL, + QUICSTREAM_HEADERS_KIND_INITIAL, + QUICSTREAM_HEADERS_KIND_TRAILING, + QUICSTREAM_HEADERS_KIND_PUSH, + QUICSTREAM_HEADER_FLAGS_NONE, + QUICSTREAM_HEADER_FLAGS_TERMINAL, + } +} = internalBinding('quic'); + +const { + Histogram, + kDestroy: kDestroyHistogram +} = require('internal/histogram'); + +const { + validateBoolean, + validateInteger, + validateNumber, + validateObject, + validateString, +} = require('internal/validators'); + +const emit = EventEmitter.prototype.emit; + +const kAddSession = Symbol('kAddSession'); +const kAddStream = Symbol('kAddStream'); +const kClose = Symbol('kClose'); +const kCert = Symbol('kCert'); +const kClientHello = Symbol('kClientHello'); +const kContinueBind = Symbol('kContinueBind'); +const kContinueConnect = Symbol('kContinueConnect'); +const kDestroy = Symbol('kDestroy'); +const kEndpointBound = Symbol('kEndpointBound'); +const kEndpointClose = Symbol('kEndpointClose'); +const kGetStreamOptions = Symbol('kGetStreamOptions'); +const kHandshake = Symbol('kHandshake'); +const kHandshakePost = Symbol('kHandshakePost'); +const kHeaders = Symbol('kHeaders'); +const kMaybeBind = Symbol('kMaybeBind'); +const kSocketReady = Symbol('kSocketReady'); +const kRemoveSession = Symbol('kRemove'); +const kRemoveStream = Symbol('kRemoveStream'); +const kServerBusy = Symbol('kServerBusy'); +const kSetHandle = Symbol('kSetHandle'); +const kSetSocket = Symbol('kSetSocket'); +const kStreamClose = Symbol('kStreamClose'); +const kStreamReset = Symbol('kStreamReset'); +const kTrackWriteState = Symbol('kTrackWriteState'); +const kUDPHandleForTesting = Symbol('kUDPHandleForTesting'); +const kUsePreferredAddress = Symbol('kUsePreferredAddress'); +const kVersionNegotiation = Symbol('kVersionNegotiation'); + +const kSocketUnbound = 0; +const kSocketPending = 1; +const kSocketBound = 2; +const kSocketClosing = 3; +const kSocketDestroyed = 4; + +let diagnosticPacketLossWarned = false; +let warnedVerifyHostnameIdentity = false; + +// Called by the C++ internals when the socket is closed. +// When this is called, the only thing left to do is destroy +// the QuicSocket instance. +function onSocketClose() { + this[owner_symbol].destroy(); +} + +// Called by the C++ internals when an error occurs on the socket. +// When this is called, the only thing left to do is destroy +// the QuicSocket instance with an error. +// TODO(@jasnell): Should consolidate this with onSocketClose +function onSocketError(err) { + this[owner_symbol].destroy(errnoException(err)); +} + +// Called by the C++ internals when the server busy state of +// the QuicSocket has been changed. +function onSocketServerBusy(on) { + this[owner_symbol][kServerBusy](!!on); +} + +// Called by the C++ internals when a new server QuicSession has been created. +function onSessionReady(handle) { + const socket = this[owner_symbol]; + const session = + new QuicServerSession( + socket, + handle, + socket[kGetStreamOptions]()); + process.nextTick(emit.bind(socket, 'session', session)); +} + +// During an immediate close, all currently open QuicStreams are +// abruptly closed. If they are still writable or readable, an abort +// event will be emitted, and RESET_STREAM and STOP_SENDING frames +// will be transmitted as necessary. Once streams have been +// shutdown, a CONNECTION_CLOSE frame will be sent and the +// session will enter the closing period, after which it will +// be destroyed either when the idle timeout expires, the +// QuicSession is silently closed, or destroy is called. +function onSessionClose(code, family) { + if (this[owner_symbol]) { + this[owner_symbol][kClose](family, code); + } else { + // When there's no owner_symbol, the session was closed + // before it could be fully set up. Just immediately + // close everything down on the native side. + this.destroy(code, family); + } +} + +// Called by the C++ internals when a QuicSession has been destroyed. +// When this is called, the QuicSession is no longer usable. Removing +// the handle and emitting close is the only action. +// TODO(@jasnell): In the future, this will need to act differently +// for QuicClientSessions when autoResume is enabled. +function onSessionDestroyed() { + const session = this[owner_symbol]; + this[owner_symbol] = undefined; + + if (session) { + session[kSetHandle](); + process.nextTick(emit.bind(session, 'close')); + } +} + +// Used only within the onSessionClientHello function. Invoked +// to complete the client hello process. +function clientHelloCallback(err, ...args) { + if (err) { + this[owner_symbol].destroy(err); + return; + } + try { + this.onClientHelloDone(...args); + } catch (err) { + this[owner_symbol].destroy(err); + } +} + +// This callback is invoked at the start of the TLS handshake to provide +// some basic information about the ALPN, SNI, and Ciphers that are +// being requested. It is only called if the 'clientHello' event is +// listened for. +function onSessionClientHello(alpn, servername, ciphers) { + this[owner_symbol][kClientHello]( + alpn, + servername, + ciphers, + clientHelloCallback.bind(this)); +} + +// Used only within the onSessionCert function. Invoked +// to complete the session cert process. +function sessionCertCallback(err, context, ocspResponse) { + if (err) { + this[owner_symbol].destroy(err); + return; + } + if (context != null && !context.context) { + this[owner_symbol].destroy( + new ERR_INVALID_ARG_TYPE( + 'context', + 'SecureContext', + context)); + } + if (ocspResponse != null) { + if (typeof ocspResponse === 'string') + ocspResponse = Buffer.from(ocspResponse); + if (!isArrayBufferView(ocspResponse)) { + this[owner_symbol].destroy( + new ERR_INVALID_ARG_TYPE( + 'ocspResponse', + ['string', 'Buffer', 'TypedArray', 'DataView'], + ocspResponse)); + } + } + try { + this.onCertDone(context ? context.context : undefined, ocspResponse); + } catch (err) { + this[owner_symbol].destroy(err); + } +} + +// This callback is only ever invoked for QuicServerSession instances, +// and is used to trigger OCSP request processing when needed. The +// user callback must invoke .onCertDone() in order for the +// TLS handshake to continue. +function onSessionCert(servername) { + this[owner_symbol][kCert](servername, sessionCertCallback.bind(this)); +} + +// This callback is only ever invoked for QuicClientSession instances, +// and is used to deliver the OCSP response as provided by the server. +// If the requestOCSP configuration option is false, this will never +// be called. +function onSessionStatus(response) { + this[owner_symbol][kCert](response); +} + +// Called by the C++ internals when the TLS handshake is completed. +function onSessionHandshake( + servername, + alpn, + cipher, + cipherVersion, + maxPacketLength, + verifyErrorReason, + verifyErrorCode, + earlyData) { + this[owner_symbol][kHandshake]( + servername, + alpn, + cipher, + cipherVersion, + maxPacketLength, + verifyErrorReason, + verifyErrorCode, + earlyData); +} + +// Called by the C++ internals when TLS session ticket data is +// available. This is generally most useful on the client side +// where the session ticket needs to be persisted for session +// resumption and 0RTT. +function onSessionTicket(sessionTicket, transportParams) { + if (this[owner_symbol]) { + process.nextTick( + emit.bind( + this[owner_symbol], + 'sessionTicket', + sessionTicket, + transportParams)); + } +} + +// Called by the C++ internals when path validation is completed. +// This is a purely informational event that is emitted only when +// there is a listener present for the pathValidation event. +function onSessionPathValidation(res, local, remote) { + const session = this[owner_symbol]; + if (session) { + process.nextTick( + emit.bind( + session, + 'pathValidation', + res === NGTCP2_PATH_VALIDATION_RESULT_FAILURE ? 'failure' : 'success', + local, + remote)); + } +} + +function onSessionUsePreferredAddress(address, port, family) { + const session = this[owner_symbol]; + session[kUsePreferredAddress]({ + address, + port, + type: family === AF_INET6 ? 'udp6' : 'udp4' + }); +} + +// Called by the C++ internals to emit a QLog record. +function onSessionQlog(str) { + if (this.qlogBuffer === undefined) this.qlogBuffer = ''; + + const session = this[owner_symbol]; + + if (session && session.listenerCount('qlog') > 0) { + // Emit this chunk along with any previously buffered data. + str = this.qlogBuffer + str; + this.qlogBuffer = ''; + if (str === '') return; + session.emit('qlog', str); + } else { + // Buffer the data until both the JS session object and a listener + // become available. + this.qlogBuffer += str; + + if (!session || this.waitingForQlogListener) return; + this.waitingForQlogListener = true; + + function onNewListener(ev) { + if (ev === 'qlog') { + session.removeListener('newListener', onNewListener); + process.nextTick(() => { + onSessionQlog.call(this, ''); + }); + } + } + + session.on('newListener', onNewListener); + } +} + +// Called when an error occurs in a QuicSession. When this happens, +// the only remedy is to destroy the session. +function onSessionError(error) { + if (this[owner_symbol]) { + this[owner_symbol].destroy(error); + } +} + +// Called by the C++ internals when a client QuicSession receives +// a version negotiation response from the server. +function onSessionVersionNegotiation( + version, + requestedVersions, + supportedVersions) { + if (this[owner_symbol]) { + this[owner_symbol][kVersionNegotiation]( + version, + requestedVersions, + supportedVersions); + } +} + +// Called by the C++ internals to emit keylogging details for a +// QuicSession. +function onSessionKeylog(line) { + if (this[owner_symbol]) { + this[owner_symbol].emit('keylog', line); + } +} + +// Called by the C++ internals when a new QuicStream has been created. +function onStreamReady(streamHandle, id, push_id) { + const session = this[owner_symbol]; + + // onStreamReady should never be called if the stream is in a closing + // state because new streams should not have been accepted at the C++ + // level. + assert(!session.closing); + + // TODO(@jasnell): Get default options from session + const uni = id & 0b10; + const { + highWaterMark, + defaultEncoding, + } = session[kGetStreamOptions](); + const stream = new QuicStream({ + writable: !uni, + highWaterMark, + defaultEncoding, + }, session, push_id); + stream[kSetHandle](streamHandle); + if (uni) + stream.end(); + session[kAddStream](id, stream); + process.nextTick(emit.bind(session, 'stream', stream)); +} + +// Called by the C++ internals when a stream is closed and +// needs to be destroyed on the JavaScript side. +function onStreamClose(id, appErrorCode) { + this[owner_symbol][kStreamClose](id, appErrorCode); +} + +// Called by the C++ internals when a stream has been reset +function onStreamReset(id, appErrorCode) { + this[owner_symbol][kStreamReset](id, appErrorCode); +} + +// Called when an error occurs in a QuicStream +function onStreamError(streamHandle, error) { + streamHandle[owner_symbol].destroy(error); +} + +// Called when a block of headers has been fully +// received for the stream. Not all QuicStreams +// will support headers. The headers argument +// here is an Array of name-value pairs. +function onStreamHeaders(id, headers, kind, push_id) { + this[owner_symbol][kHeaders](id, headers, kind, push_id); +} + +// During a silent close, all currently open QuicStreams are abruptly +// closed. If they are still writable or readable, an abort event will be +// emitted, otherwise the stream is just destroyed. No RESET_STREAM or +// STOP_SENDING is transmitted to the peer. +function onSessionSilentClose(statelessReset, code, family) { + this[owner_symbol][kDestroy](statelessReset, family, code); +} + +// When a stream is flow control blocked, causes a blocked event +// to be emitted. This is a purely informational event. +function onStreamBlocked() { + process.nextTick(emit.bind(this[owner_symbol], 'blocked')); +} + +// Register the callbacks with the QUIC internal binding. +setCallbacks({ + onSocketClose, + onSocketError, + onSocketServerBusy, + onSessionReady, + onSessionCert, + onSessionClientHello, + onSessionClose, + onSessionDestroyed, + onSessionError, + onSessionHandshake, + onSessionKeylog, + onSessionQlog, + onSessionSilentClose, + onSessionStatus, + onSessionTicket, + onSessionVersionNegotiation, + onStreamReady, + onStreamClose, + onStreamError, + onStreamReset, + onSessionPathValidation, + onSessionUsePreferredAddress, + onStreamHeaders, + onStreamBlocked, +}); + +// connectAfterLookup is invoked when the QuicSocket connect() +// method has been invoked. The first step is to resolve the given +// remote hostname into an ip address. Once resolution is complete, +// the resolved ip address needs to be passed on to the [kContinueConnect] +// function or the QuicClientSession needs to be destroyed. +function connectAfterLookup(type, err, ip) { + if (err) { + this.destroy(err); + return; + } + this[kContinueConnect](type, ip); +} + +// Creates the SecureContext used by QuicSocket instances that are listening +// for new connections. +function createSecureContext(options, init_cb) { + const sc_options = validateCreateSecureContextOptions(options); + const { groups, earlyData } = sc_options; + const sc = _createSecureContext(sc_options); + // TODO(@jasnell): Determine if it's really necessary to pass in groups here. + init_cb(sc.context, groups, earlyData); + return sc; +} + +function onNewListener(event) { + toggleListeners(this[kHandle], event, true); +} + +function onRemoveListener(event) { + toggleListeners(this[kHandle], event, false); +} + +// QuicEndpoint wraps a UDP socket and is owned +// by a QuicSocket. It does not exist independently +// of the QuicSocket. +class QuicEndpoint { + #state = kSocketUnbound; + #socket = undefined; + #udpSocket = undefined; + #address = undefined; + #ipv6Only = undefined; + #lookup = undefined; + #port = undefined; + #reuseAddr = undefined; + #type = undefined; + #fd = undefined; + + constructor(socket, options) { + const { + address, + ipv6Only, + lookup, + port = 0, + reuseAddr, + type, + preferred, + } = validateQuicEndpointOptions(options); + this.#socket = socket; + this.#address = address || (type === AF_INET6 ? '::' : '0.0.0.0'); + this.#ipv6Only = !!ipv6Only; + this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + this.#port = port; + this.#reuseAddr = !!reuseAddr; + this.#udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4'); + + // kUDPHandleForTesting is only used in the Node.js test suite to + // artificially test the endpoint. This code path should never be + // used in user code. + if (typeof options[kUDPHandleForTesting] === 'object') { + this.#udpSocket.bind(options[kUDPHandleForTesting]); + this.#state = kSocketBound; + this.#socket[kEndpointBound](this); + } + const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + const handle = new QuicEndpointHandle(socket[kHandle], udpHandle); + handle[owner_symbol] = this; + this[kHandle] = handle; + socket[kHandle].addEndpoint(handle, !!preferred); + } + + [kInspect]() { + const obj = { + address: this.address, + fd: this.#fd, + type: this.#type + }; + return `QuicEndpoint ${util.format(obj)}`; + } + + // afterLookup is invoked when binding a QuicEndpoint. The first + // step to binding is to resolve the given hostname into an ip + // address. Once resolution is complete, the ip address needs to + // be passed on to the [kContinueBind] function or the QuicEndpoint + // needs to be destroyed. + static #afterLookup = function(err, ip) { + if (err) { + this.destroy(err); + return; + } + this[kContinueBind](ip); + }; + + // kMaybeBind binds the endpoint on-demand if it is not already + // bound. If it is bound, we return immediately, otherwise put + // the endpoint into the pending state and initiate the binding + // process by calling the lookup to resolve the IP address. + [kMaybeBind]() { + if (this.#state !== kSocketUnbound) + return; + this.#state = kSocketPending; + this.#lookup(this.#address, QuicEndpoint.#afterLookup.bind(this)); + } + + // IP address resolution is completed and we're ready to finish + // binding to the local port. + [kContinueBind](ip) { + const udpHandle = this.#udpSocket[internalDgram.kStateSymbol].handle; + if (udpHandle == null) { + // TODO(@jasnell): We may need to throw an error here. Under + // what conditions does this happen? + return; + } + const flags = + (this.#reuseAddr ? UV_UDP_REUSEADDR : 0) | + (this.#ipv6Only ? UV_UDP_IPV6ONLY : 0); + + const ret = udpHandle.bind(ip, this.#port, flags); + if (ret) { + this.destroy(exceptionWithHostPort(ret, 'bind', ip, this.#port || 0)); + return; + } + + // On Windows, the fd will be meaningless, but we always record it. + this.#fd = udpHandle.fd; + this.#state = kSocketBound; + + // Notify the owning socket that the QuicEndpoint has been successfully + // bound to the local UDP port. + this.#socket[kEndpointBound](this); + } + + [kDestroy](error) { + const handle = this[kHandle]; + if (handle !== undefined) { + this[kHandle] = undefined; + handle[owner_symbol] = undefined; + handle.ondone = () => { + this.#udpSocket.close((err) => { + if (err) error = err; + this.#socket[kEndpointClose](this, error); + }); + }; + handle.waitForPendingCallbacks(); + } + } + + // If the QuicEndpoint is bound, returns an object detailing + // the local IP address, port, and address type to which it + // is bound. Otherwise, returns an empty object. + get address() { + if (this.#state !== kSocketDestroyed) { + try { + return this.#udpSocket.address(); + } catch (err) { + if (err.code === 'EBADF') { + // If there is an EBADF error, the socket is not bound. + // Return empty object. Else, rethrow the error because + // something else bad happened. + return {}; + } + throw err; + } + } + return {}; + } + + get fd() { + return this.#fd; + } + + // True if the QuicEndpoint has been destroyed and is + // no longer usable. + get destroyed() { + return this.#state === kSocketDestroyed; + } + + // True if binding has been initiated and is in progress. + get pending() { + return this.#state === kSocketPending; + } + + // True if the QuicEndpoint has been bound to the local + // UDP port. + get bound() { + return this.#state === kSocketBound; + } + + setTTL(ttl) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setTTL'); + this.#udpSocket.setTTL(ttl); + return this; + } + + setMulticastTTL(ttl) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastTTL'); + this.#udpSocket.setMulticastTTL(ttl); + return this; + } + + setBroadcast(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setBroadcast'); + this.#udpSocket.setBroadcast(on); + return this; + } + + setMulticastLoopback(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastLoopback'); + this.#udpSocket.setMulticastLoopback(on); + return this; + } + + setMulticastInterface(iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setMulticastInterface'); + this.#udpSocket.setMulticastInterface(iface); + return this; + } + + addMembership(address, iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('addMembership'); + this.#udpSocket.addMembership(address, iface); + return this; + } + + dropMembership(address, iface) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('dropMembership'); + this.#udpSocket.dropMembership(address, iface); + return this; + } + + ref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('ref'); + this.#udpSocket.ref(); + return this; + } + + unref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('unref'); + this.#udpSocket.unref(); + return this; + } + + destroy(error) { + // If the QuicEndpoint is already destroyed, do nothing + if (this.#state === kSocketDestroyed) + return; + + // Mark the QuicEndpoint as being destroyed. + this.#state = kSocketDestroyed; + + this[kDestroy](error); + } +} + +// QuicSocket wraps a UDP socket plus the associated TLS context and QUIC +// Protocol state. There may be *multiple* QUIC connections (QuicSession) +// associated with a single QuicSocket. +class QuicSocket extends EventEmitter { + #alpn = undefined; + #autoClose = undefined; + #client = undefined; + #defaultEncoding = undefined; + #endpoints = new Set(); + #highWaterMark = undefined; + #lookup = undefined; + #server = undefined; + #serverBusy = false; + #serverListening = false; + #serverSecureContext = undefined; + #sessions = new Set(); + #state = kSocketUnbound; + #stats = undefined; + + constructor(options) { + const { + endpoint, + + // True if the QuicSocket should automatically enter a graceful shutdown + // if it is not listening as a server and the last QuicClientSession + // closes + autoClose, + + // Default configuration for QuicClientSessions + client, + + // The maximum number of connections + maxConnections, + + // The maximum number of connections per host + maxConnectionsPerHost, + + // The maximum number of stateless resets per host + maxStatelessResetsPerHost, + + // The maximum number of seconds for retry token + retryTokenTimeout, + + // The DNS lookup function + lookup, + + // Default configuration for QuicServerSessions + server, + + // UDP type + type, + + // True if address verification should be used. + validateAddress, + + // True if an LRU should be used for add validation + validateAddressLRU, + + // Whether qlog should be enabled for sessions + qlog, + + // Stateless reset token secret (16 byte buffer) + statelessResetSecret, + + // When true, stateless resets will not be sent (default false) + disableStatelessReset, + } = validateQuicSocketOptions(options); + super({ captureRejections: true }); + + this.#autoClose = autoClose; + this.#client = client; + this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4); + this.#server = server; + + const socketOptions = + (validateAddress ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS : 0) | + (validateAddressLRU ? QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU : 0); + + this[kSetHandle]( + new QuicSocketHandle( + socketOptions, + retryTokenTimeout, + maxConnections, + maxConnectionsPerHost, + maxStatelessResetsPerHost, + qlog, + statelessResetSecret, + disableStatelessReset)); + + this.addEndpoint({ + lookup: this.#lookup, + // Keep the lookup and ...endpoint in this order + // to allow the passed in endpoint options to + // override the lookup specifically for that endpoint + ...endpoint, + preferred: true + }); + } + + // Returns the default QuicStream options for peer-initiated + // streams. These are passed on to new client and server + // QuicSession instances when they are created. + [kGetStreamOptions]() { + return { + highWaterMark: this.#highWaterMark, + defaultEncoding: this.#defaultEncoding, + }; + } + + [kSetHandle](handle) { + this[kHandle] = handle; + if (handle !== undefined) { + handle[owner_symbol] = this; + this[async_id_symbol] = handle.getAsyncId(); + } + } + + [kInspect]() { + const obj = { + endpoints: this.endpoints, + sessions: this.#sessions, + }; + return `QuicSocket ${util.format(obj)}`; + } + + [kAddSession](session) { + this.#sessions.add(session); + } + + [kRemoveSession](session) { + this.#sessions.delete(session); + this[kMaybeDestroy](); + } + + // Bind all QuicEndpoints on-demand, only if they haven't already been bound. + // Function is a non-op if the socket is already bound or in the process of + // being bound, and will call the callback once the socket is ready. + [kMaybeBind](callback = () => {}) { + if (this.#state === kSocketBound) + return process.nextTick(callback); + + this.once('ready', callback); + + if (this.#state === kSocketPending) + return; + this.#state = kSocketPending; + + for (const endpoint of this.#endpoints) + endpoint[kMaybeBind](); + } + + [kEndpointBound](endpoint) { + if (this.#state === kSocketBound) + return; + this.#state = kSocketBound; + + // Once the QuicSocket has been bound, we notify all currently + // existing QuicSessions. QuicSessions created after this + // point will automatically be notified that the QuicSocket + // is ready. + for (const session of this.#sessions) + session[kSocketReady](); + + // The ready event indicates that the QuicSocket is ready to be + // used to either listen or connect. No QuicServerSession should + // exist before this event, and all QuicClientSession will remain + // in Initial states until ready is invoked. + process.nextTick(emit.bind(this, 'ready')); + } + + // Called when a QuicEndpoint closes + [kEndpointClose](endpoint, error) { + this.#endpoints.delete(endpoint); + process.nextTick(emit.bind(this, 'endpointClose', endpoint, error)); + + // If there are no more QuicEndpoints, the QuicSocket is no + // longer usable. + if (this.#endpoints.size === 0) { + // Ensure that there are absolutely no additional sessions + for (const session of this.#sessions) + session.destroy(error); + + if (error) process.nextTick(emit.bind(this, 'error', error)); + process.nextTick(emit.bind(this, 'close')); + } + } + + // kDestroy is called to actually free the QuicSocket resources and + // cause the error and close events to be emitted. + [kDestroy](error) { + // The QuicSocket will be destroyed once all QuicEndpoints + // are destroyed. See [kEndpointClose]. + for (const endpoint of this.#endpoints) + endpoint.destroy(error); + } + + // kMaybeDestroy is called one or more times after the close() method + // is called. The QuicSocket will be destroyed if there are no remaining + // open sessions. + [kMaybeDestroy]() { + if (this.closing && this.#sessions.size === 0) { + this.destroy(); + return true; + } + return false; + } + + // Called by the C++ internals to notify when server busy status is toggled. + [kServerBusy](on) { + this.#serverBusy = on; + process.nextTick(emit.bind(this, 'busy', on)); + } + + addEndpoint(options = {}) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('listen'); + + // TODO(@jasnell): Also forbid adding an endpoint if + // the QuicSocket is closing. + + const endpoint = new QuicEndpoint(this, options); + this.#endpoints.add(endpoint); + + // If the QuicSocket is already bound at this point, + // also bind the newly created QuicEndpoint. + if (this.#state !== kSocketUnbound) + endpoint[kMaybeBind](); + + return endpoint; + } + + // Used only from within the #continueListen function. When a preferred + // address has been provided, the hostname given must be resolved into an + // IP address, which must be passed on to #completeListen or the QuicSocket + // needs to be destroyed. + static #afterPreferredAddressLookup = function( + transportParams, + port, + type, + err, + address) { + if (err) { + this.destroy(err); + return; + } + this.#completeListen(transportParams, { address, port, type }); + }; + + // The #completeListen function is called after all of the necessary + // DNS lookups have been performed and we're ready to let the C++ + // internals begin listening for new QuicServerSession instances. + #completeListen = function(transportParams, preferredAddress) { + const { + address, + port, + type = AF_INET, + } = { ...preferredAddress }; + + const { + rejectUnauthorized = !getAllowUnauthorized(), + requestCert = false, + } = transportParams; + + // Transport Parameters are passed to the C++ side using a shared array. + // These are the transport parameters that will be used when a new + // server QuicSession is established. They are transmitted to the client + // as part of the server's initial TLS handshake. Once they are set, they + // cannot be modified. + setTransportParams(transportParams); + + const options = + (rejectUnauthorized ? QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED : 0) | + (requestCert ? QUICSERVERSESSION_OPTION_REQUEST_CERT : 0); + + // When the handle is told to listen, it will begin acting as a QUIC + // server and will emit session events whenever a new QuicServerSession + // is created. + this[kHandle].listen( + this.#serverSecureContext.context, + address, + type, + port, + this.#alpn, + options); + process.nextTick(emit.bind(this, 'listening')); + }; + + // When the QuicSocket listen() function is called, the first step is to bind + // the underlying QuicEndpoint's. Once at least one endpoint has been bound, + // the continueListen function is invoked to continue the process of starting + // to listen. + // + // The preferredAddress is a preferred network endpoint that the server wishes + // connecting clients to use. If specified, this will be communicate to the + // client as part of the tranport parameters exchanged during the TLS + // handshake. + #continueListen = function(transportParams, lookup) { + const { preferredAddress } = transportParams; + + // TODO(@jasnell): Currently, we wait to start resolving the + // preferred address until after we know the QuicSocket is + // bound. That's not strictly necessary as we can resolve the + // preferred address in parallel, which out to be faster but + // is a slightly more complicated to coordinate. In the future, + // we should do those things in parallel. + if (preferredAddress && typeof preferredAddress === 'object') { + const { + address, + port, + type = 'udp4', + } = { ...preferredAddress }; + const typeVal = getSocketType(type); + // If preferred address is set, we need to perform a lookup on it + // to get the IP address. Only after that lookup completes can we + // continue with the listen operation, passing in the resolved + // preferred address. + lookup( + address || (typeVal === AF_INET6 ? '::' : '0.0.0.0'), + QuicSocket.#afterPreferredAddressLookup.bind( + this, + transportParams, + port, + typeVal)); + return; + } + // If preferred address is not set, we can skip directly to the listen + this.#completeListen(transportParams); + }; + + // Begin listening for server connections. The callback that may be + // passed to this function is registered as a handler for the + // on('session') event. Errors may be thrown synchronously by this + // function. + listen(options, callback) { + if (this.#serverListening) + throw new ERR_QUICSOCKET_LISTENING(); + + if (this.#state === kSocketDestroyed || + this.#state === kSocketClosing) { + throw new ERR_QUICSOCKET_DESTROYED('listen'); + } + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (callback !== undefined && typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(callback); + + options = { + secureProtocol: 'TLSv1_3_server_method', + ...this.#server, + ...options + }; + + // The ALPN protocol identifier is strictly required. + const { + alpn, + defaultEncoding, + highWaterMark, + transportParams, + } = validateQuicSocketListenOptions(options); + + // Store the secure context so that it is not garbage collected + // while we still need to make use of it. + this.#serverSecureContext = + createSecureContext( + options, + initSecureContext); + + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + this.#serverListening = true; + this.#alpn = alpn; + + // If the callback function is provided, it is registered as a + // handler for the on('session') event and will be called whenever + // there is a new QuicServerSession instance created. + if (callback) + this.on('session', callback); + + // Bind the QuicSocket to the local port if it hasn't been bound already. + this[kMaybeBind](this.#continueListen.bind( + this, + transportParams, + this.#lookup)); + } + + // When the QuicSocket connect() function is called, the first step is to bind + // the underlying QuicEndpoint's. Once at least one endpoint has been bound, + // the connectAfterBind function is invoked to continue the connection + // process. + // + // The immediate next step is to resolve the address into an ip address. + #continueConnect = function(session, lookup, address, type) { + // TODO(@jasnell): Currently, we perform the DNS resolution after + // the QuicSocket has been bound. We don't have to. We could do + // it in parallel while we're waitint to be bound but doing so + // is slightly more complicated. + + // Notice here that connectAfterLookup is bound to the QuicSession + // that was created... + lookup( + address || (type === AF_INET6 ? '::' : '0.0.0.0'), + connectAfterLookup.bind(session, type)); + }; + + // Creates and returns a new QuicClientSession. + connect(options, callback) { + if (this.#state === kSocketDestroyed || + this.#state === kSocketClosing) { + throw new ERR_QUICSOCKET_DESTROYED('connect'); + } + + if (typeof options === 'function') { + callback = options; + options = undefined; + } + + options = { + ...this.#client, + ...options + }; + + const { + type, + address, + } = validateQuicSocketConnectOptions(options); + + const session = new QuicClientSession(this, options); + + // TODO(@jasnell): This likely should listen for the secure event + // rather than the ready event + if (typeof callback === 'function') + session.once('ready', callback); + + this[kMaybeBind](this.#continueConnect.bind( + this, + session, + this.#lookup, + address, + type)); + + return session; + } + + // Initiate a Graceful Close of the QuicSocket. + // Existing QuicClientSession and QuicServerSession instances will be + // permitted to close naturally and gracefully on their own. + // The QuicSocket will be immediately closed and freed as soon as there + // are no additional session instances remaining. If there are no + // QuicClientSession or QuicServerSession instances, the QuicSocket + // will be immediately closed. + // + // If specified, the callback will be registered for once('close'). + // + // No additional QuicServerSession instances will be accepted from + // remote peers, and calls to connect() to create QuicClientSession + // instances will fail. The QuicSocket will be otherwise usable in + // every other way. + // + // Subsequent calls to close(callback) will register the close callback + // if one is defined but will otherwise do nothing. + // + // Once initiated, a graceful close cannot be canceled. The graceful + // close can be interupted, however, by abruptly destroying the + // QuicSocket using the destroy() method. + // + // If close() is called before the QuicSocket has been bound (before + // either connect() or listen() have been called, or the QuicSocket + // is still in the pending state, the callback is registered for the + // once('close') event (if specified) and the QuicSocket is destroyed + // immediately. + close(callback) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('close'); + + // If a callback function is specified, it is registered as a + // handler for the once('close') event. If the close occurs + // immediately, the close event will be emitted as soon as the + // process.nextTick queue is processed. Otherwise, the close + // event will occur at some unspecified time in the near future. + if (callback) { + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + this.once('close', callback); + } + + // If we are already closing, do nothing else and wait + // for the close event to be invoked. + if (this.#state === kSocketClosing) + return; + + // If the QuicSocket is otherwise not bound to the local + // port, destroy the QuicSocket immediately. + if (this.#state !== kSocketBound) { + this.destroy(); + } + + // Mark the QuicSocket as closing to prevent re-entry + this.#state = kSocketClosing; + + // Otherwise, gracefully close each QuicSession, with + // [kMaybeDestroy]() being called after each closes. + const maybeDestroy = this[kMaybeDestroy].bind(this); + + // Tell the underlying QuicSocket C++ object to stop + // listening for new QuicServerSession connections. + // New initial connection packets for currently unknown + // DCID's will be ignored. + if (this[kHandle]) { + this[kHandle].stopListening(); + } + this.#serverListening = false; + + // If there are no sessions, calling maybeDestroy + // will immediately and synchronously destroy the + // QuicSocket. + if (maybeDestroy()) + return; + + // If we got this far, there a QuicClientSession and + // QuicServerSession instances still, we need to trigger + // a graceful close for each of them. As each closes, + // they will call the kMaybeDestroy function. When there + // are no remaining session instances, the QuicSocket + // will be closed and destroyed. + for (const session of this.#sessions) + session.close(maybeDestroy); + } + + // Initiate an abrupt close and destruction of the QuicSocket. + // Existing QuicClientSession and QuicServerSession instances will be + // immediately closed. If error is specified, it will be forwarded + // to each of the session instances. + // + // When the session instances are closed, an attempt to send a final + // CONNECTION_CLOSE will be made. + // + // The JavaScript QuicSocket object will be marked destroyed and will + // become unusable. As soon as all pending outbound UDP packets are + // flushed from the QuicSocket's queue, the QuicSocket C++ instance + // will be destroyed and freed from memory. + destroy(error) { + // If the QuicSocket is already destroyed, do nothing + if (this.#state === kSocketDestroyed) + return; + + // Mark the QuicSocket as being destroyed. + this.#state = kSocketDestroyed; + + // Immediately close any sessions that may be remaining. + // If the udp socket is in a state where it is able to do so, + // a final attempt to send CONNECTION_CLOSE frames for each + // closed session will be made. + for (const session of this.#sessions) + session.destroy(error); + + this[kDestroy](error); + } + + ref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('ref'); + for (const endpoint of this.#endpoints) + endpoint.ref(); + return this; + } + + unref() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('unref'); + for (const endpoint of this.#endpoints) + endpoint.unref(); + return this; + } + + get endpoints() { + return Array.from(this.#endpoints); + } + + // The sever secure context is the SecureContext specified when calling + // listen. It is the context that will be used with all new server + // QuicSession instances. + get serverSecureContext() { + return this.#serverSecureContext; + } + + // True if at least one associated QuicEndpoint has been successfully + // bound to a local UDP port. + get bound() { + return this.#state === kSocketBound; + } + + // True if graceful close has been initiated by calling close() + get closing() { + return this.#state === kSocketClosing; + } + + // True if the QuicSocket has been destroyed and is no longer usable + get destroyed() { + return this.#state === kSocketDestroyed; + } + + // True if listen() has been called successfully + get listening() { + return this.#serverListening; + } + + // True if the QuicSocket is currently waiting on at least one + // QuicEndpoint to succesfully bind.g + get pending() { + return this.#state === kSocketPending; + } + + // Marking a server as busy will cause all new + // connection attempts to fail with a SERVER_BUSY CONNECTION_CLOSE. + setServerBusy(on = true) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setServerBusy'); + validateBoolean(on, 'on'); + if (this.#serverBusy !== on) + this[kHandle].setServerBusy(on); + } + + get duration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_CREATED_AT]; + } + + get boundDuration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_BOUND_AT]; + } + + get listenDuration() { + // TODO(@jasnell): If the object is destroyed, it should + // use a fixed duration rather than calculating from now + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SOCKET_STATS_LISTEN_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_BYTES_SENT]; + } + + get packetsReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_RECEIVED]; + } + + get packetsSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_SENT]; + } + + get packetsIgnored() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_PACKETS_IGNORED]; + } + + get serverBusy() { + return this.#serverBusy; + } + + get serverSessions() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_SERVER_SESSIONS]; + } + + get clientSessions() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_CLIENT_SESSIONS]; + } + + get statelessResetCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_STATELESS_RESET_COUNT]; + } + + get serverBusyCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SOCKET_STATS_SERVER_BUSY_COUNT]; + } + + // Diagnostic packet loss is a testing mechanism that allows simulating + // pseudo-random packet loss for rx or tx. The value specified for each + // option is a number between 0 and 1 that identifies the possibility of + // packet loss in the given direction. + setDiagnosticPacketLoss(options) { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('setDiagnosticPacketLoss'); + const { + rx = 0.0, + tx = 0.0 + } = { ...options }; + validateNumber(rx, 'options.rx', { min: 0.0, max: 1.0 }); + validateNumber(tx, 'options.tx', { min: 0.0, max: 1.0 }); + if ((rx > 0.0 || tx > 0.0) && !diagnosticPacketLossWarned) { + diagnosticPacketLossWarned = true; + process.emitWarning( + 'QuicSocket diagnostic packet loss is enabled. Received or ' + + 'transmitted packets will be randomly ignored to simulate ' + + 'network packet loss.'); + } + this[kHandle].setDiagnosticPacketLoss(rx, tx); + } + + // Toggles stateless reset on/off. By default, stateless reset tokens + // are generated when necessary. The disableStatelessReset option may + // be used when the QuicSocket is created to disable generation of + // stateless resets. The toggleStatelessReset method allows the setting + // to be switched on/off dynamically through the lifetime of the + // socket. + toggleStatelessReset() { + if (this.#state === kSocketDestroyed) + throw new ERR_QUICSOCKET_DESTROYED('toggleStatelessReset'); + return this[kHandle].toggleStatelessReset(); + } +} + +class QuicSession extends EventEmitter { + #alpn = undefined; + #cipher = undefined; + #cipherVersion = undefined; + #closeCode = NGTCP2_NO_ERROR; + #closeFamily = QUIC_ERROR_APPLICATION; + #closing = false; + #destroyed = false; + #earlyData = false; + #handshakeComplete = false; + #idleTimeout = false; + #maxPacketLength = IDX_QUIC_SESSION_MAX_PACKET_SIZE_DEFAULT; + #servername = undefined; + #socket = undefined; + #statelessReset = false; + #stats = undefined; + #pendingStreams = new Set(); + #streams = new Map(); + #verifyErrorReason = undefined; + #verifyErrorCode = undefined; + #handshakeAckHistogram = undefined; + #handshakeContinuationHistogram = undefined; + #highWaterMark = undefined; + #defaultEncoding = undefined; + + constructor(socket, options) { + const { + alpn, + servername, + highWaterMark, + defaultEncoding, + } = options; + super({ captureRejections: true }); + this.on('newListener', onNewListener); + this.on('removeListener', onRemoveListener); + this.#socket = socket; + this.#servername = servername; + this.#alpn = alpn; + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + socket[kAddSession](this); + } + + // kGetStreamOptions is called to get the configured options + // for peer initiated QuicStream instances. + [kGetStreamOptions]() { + return { + highWaterMark: this.#highWaterMark, + defaultEncoding: this.#defaultEncoding, + }; + } + + // Sets the internal handle for the QuicSession instance. For + // server QuicSessions, this is called immediately as the + // handle is created before the QuicServerSession JS object. + // For client QuicSession instances, the connect() method + // must first perform DNS resolution on the provided address + // before the underlying QuicSession handle can be created. + [kSetHandle](handle) { + this[kHandle] = handle; + if (handle !== undefined) { + handle[owner_symbol] = this; + this.#handshakeAckHistogram = new Histogram(handle.ack); + this.#handshakeContinuationHistogram = new Histogram(handle.rate); + } else { + if (this.#handshakeAckHistogram) + this.#handshakeAckHistogram[kDestroyHistogram](); + if (this.#handshakeContinuationHistogram) + this.#handshakeContinuationHistogram[kDestroyHistogram](); + } + } + + // Called when a client QuicSession instance receives a version + // negotiation packet from the server peer. The client QuicSession + // is destroyed immediately. This is not called at all for server + // QuicSessions. + [kVersionNegotiation](version, requestedVersions, supportedVersions) { + const err = + new ERR_QUICSESSION_VERSION_NEGOTIATION( + version, + requestedVersions, + supportedVersions); + err.detail = { + version, + requestedVersions, + supportedVersions, + }; + this.destroy(err); + } + + // Causes the QuicSession to be immediately destroyed, but with + // additional metadata set. + [kDestroy](statelessReset, family, code) { + this.#statelessReset = !!statelessReset; + this.#closeCode = code; + this.#closeFamily = family; + this.destroy(); + } + + // Immediate close has been initiated for the session. Any + // still open QuicStreams must be abandoned and shutdown + // with RESET_STREAM and STOP_SENDING frames transmitted + // as appropriate. Once the streams have been shutdown, a + // CONNECTION_CLOSE will be generated and sent, switching + // the session into the closing period. + [kClose](family, code) { + // Do nothing if the QuicSession has already been destroyed. + if (this.#destroyed) + return; + + // Set the close code and family so we can keep track. + this.#closeCode = code; + this.#closeFamily = family; + + // Shutdown all pending streams. These are Streams that + // have been created but do not yet have a handle assigned. + for (const stream of this.#pendingStreams) + stream[kClose](family, code); + + // Shutdown all of the remaining streams + for (const stream of this.#streams.values()) + stream[kClose](family, code); + + // By this point, all necessary RESET_STREAM and + // STOP_SENDING frames ought to have been sent, + // so now we just trigger sending of the + // CONNECTION_CLOSE frame. + this[kHandle].close(family, code); + } + + // Closes the specified stream with the given code. The + // QuicStream object will be destroyed. + [kStreamClose](id, code) { + const stream = this.#streams.get(id); + if (stream === undefined) + return; + + stream.destroy(); + } + + // Delivers a block of headers to the appropriate QuicStream + // instance. This will only be called if the ALPN selected + // is known to support headers. + [kHeaders](id, headers, kind, push_id) { + const stream = this.#streams.get(id); + if (stream === undefined) + return; + + stream[kHeaders](headers, kind, push_id); + } + + [kStreamReset](id, code) { + const stream = this.#streams.get(id); + if (stream === undefined) + return; + + stream[kStreamReset](code); + } + + [kInspect]() { + const obj = { + alpn: this.#alpn, + cipher: this.cipher, + closing: this.closing, + closeCode: this.closeCode, + destroyed: this.destroyed, + earlyData: this.#earlyData, + maxStreams: this.maxStreams, + servername: this.servername, + streams: this.#streams.size, + stats: { + handshakeAck: this.handshakeAckHistogram, + handshakeContinuation: this.handshakeContinuationHistogram, + } + }; + return `${this.constructor.name} ${util.format(obj)}`; + } + + [kSetSocket](socket) { + this.#socket = socket; + } + + // Called at the completion of the TLS handshake for the local peer + [kHandshake]( + servername, + alpn, + cipher, + cipherVersion, + maxPacketLength, + verifyErrorReason, + verifyErrorCode, + earlyData) { + this.#handshakeComplete = true; + this.#servername = servername; + this.#alpn = alpn; + this.#cipher = cipher; + this.#cipherVersion = cipherVersion; + this.#maxPacketLength = maxPacketLength; + this.#verifyErrorReason = verifyErrorReason; + this.#verifyErrorCode = verifyErrorCode; + this.#earlyData = earlyData; + if (!this[kHandshakePost]()) + return; + + process.nextTick( + emit.bind(this, 'secure', servername, alpn, this.cipher)); + } + + // Non-op for the default case. QuicClientSession + // overrides this with some client-side specific + // checks + [kHandshakePost]() { + return true; + } + + [kRemoveStream](stream) { + this.#streams.delete(stream.id); + } + + [kAddStream](id, stream) { + stream.once('close', this[kMaybeDestroy].bind(this)); + this.#streams.set(id, stream); + } + + // The QuicSession will be destroyed if closing has been + // called and there are no remaining streams + [kMaybeDestroy]() { + if (this.#closing && this.#streams.size === 0) + this.destroy(); + } + + // Called when a client QuicSession has opted to use the + // server provided preferred address. This is a purely + // informationational notification. It is not called on + // server QuicSession instances. + [kUsePreferredAddress](address) { + process.nextTick( + emit.bind(this, 'usePreferredAddress', address)); + } + + // Closing allows any existing QuicStream's to complete + // normally but disallows any new QuicStreams from being + // opened. Calls to openStream() will fail, and new streams + // from the peer will be rejected/ignored. + close(callback) { + if (this.#destroyed) + throw new ERR_QUICSESSION_DESTROYED('close'); + + if (callback) { + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + this.once('close', callback); + } + + // If we're already closing, do nothing else. + // Callback will be invoked once the session + // has been destroyed + if (this.#closing) + return; + + this.#closing = true; + this[kHandle].gracefulClose(); + + // See if we can close immediately. + this[kMaybeDestroy](); + } + + // Destroying synchronously shuts down and frees the + // QuicSession immediately, even if there are still open + // streams. + // + // A CONNECTION_CLOSE packet is sent to the + // connected peer and the session is immediately + // destroyed. + // + // If destroy is called with an error argument, the + // 'error' event is emitted on next tick. + // + // Once destroyed, and after the 'error' event (if any), + // the close event is emitted on next tick. + destroy(error) { + // Destroy can only be called once. Multiple calls will be ignored + if (this.#destroyed) + return; + this.#destroyed = true; + this.#closing = false; + + if (typeof error === 'number' || + (error != null && + typeof error === 'object' && + !(error instanceof Error))) { + const { + closeCode, + closeFamily + } = validateCloseCode(error); + this.#closeCode = closeCode; + this.#closeFamily = closeFamily; + error = new ERR_QUIC_ERROR(closeCode, closeFamily); + } + + // Destroy any pending streams immediately. These + // are streams that have been created but have not + // yet been assigned an internal handle. + for (const stream of this.#pendingStreams) + stream.destroy(error); + + // Destroy any remaining streams immediately. + for (const stream of this.#streams.values()) + stream.destroy(error); + + this.removeListener('newListener', onNewListener); + this.removeListener('removeListener', onRemoveListener); + + if (error) process.nextTick(emit.bind(this, 'error', error)); + + const handle = this[kHandle]; + if (handle !== undefined) { + // Copy the stats for use after destruction + this.#stats = new BigInt64Array(handle.stats); + this.#idleTimeout = !!handle.state[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT]; + // Calling destroy will cause a CONNECTION_CLOSE to be + // sent to the peer and will destroy the QuicSession + // handler immediately. + handle.destroy(this.#closeCode, this.#closeFamily); + } else { + process.nextTick(emit.bind(this, 'close')); + } + + // Remove the QuicSession JavaScript object from the + // associated QuicSocket. + this.#socket[kRemoveSession](this); + this.#socket = undefined; + } + + // For server QuicSession instances, true if earlyData is + // enabled. For client QuicSessions, true only if session + // resumption is used and early data was accepted during + // the TLS handshake. The value is set only after the + // TLS handshake is completed (immeditely before the + // secure event is emitted) + get usingEarlyData() { + return this.#earlyData; + } + + get maxStreams() { + let bidi = 0; + let uni = 0; + if (this[kHandle]) { + bidi = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI]; + uni = this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI]; + } + return { bidi, uni }; + } + + get address() { + return this.#socket ? this.#socket.address : {}; + } + + get maxDataLeft() { + return this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] : 0; + } + + get bytesInFlight() { + return this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] : 0; + } + + get blockCount() { + return this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATS_BLOCK_COUNT] : 0; + } + + get authenticated() { + // Specifically check for null. Undefined means the check has not + // been performed yet, another other value other than null means + // there was an error + return this.#verifyErrorReason == null; + } + + get authenticationError() { + if (this.authenticated) + return undefined; + // eslint-disable-next-line no-restricted-syntax + const err = new Error(this.#verifyErrorReason); + const code = 'ERR_QUIC_VERIFY_' + this.#verifyErrorCode; + err.name = `Error [${code}]`; + err.code = code; + return err; + } + + get remoteAddress() { + const out = {}; + if (this[kHandle]) + this[kHandle].getRemoteAddress(out); + return out; + } + + get handshakeComplete() { + return this.#handshakeComplete; + } + + get handshakeConfirmed() { + return Boolean(this[kHandle] ? + this[kHandle].state[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] : 0); + } + + get idleTimeout() { + return this.#idleTimeout; + } + + get alpnProtocol() { + return this.#alpn; + } + + get cipher() { + const name = this.#cipher; + const version = this.#cipherVersion; + return this.handshakeComplete ? { name, version } : {}; + } + + getCertificate() { + return this[kHandle] ? + translatePeerCertificate(this[kHandle].getCertificate() || {}) : {}; + } + + getPeerCertificate(detailed = false) { + return this[kHandle] ? + translatePeerCertificate( + this[kHandle].getPeerCertificate(detailed) || {}) : {}; + } + + ping() { + if (!this[kHandle]) + throw new ERR_QUICSESSION_DESTROYED('ping'); + this[kHandle].ping(); + } + + get servername() { + return this.#servername; + } + + get destroyed() { + return this.#destroyed; + } + + get closing() { + return this.#closing; + } + + get closeCode() { + return { + code: this.#closeCode, + family: this.#closeFamily + }; + } + + get socket() { + return this.#socket; + } + + get statelessReset() { + return this.#statelessReset; + } + + openStream(options) { + if (this.#destroyed || this.#closing) + throw new ERR_QUICSESSION_DESTROYED('openStream'); + const { + halfOpen, // Unidirectional or Bidirectional + highWaterMark, + defaultEncoding, + } = validateQuicStreamOptions(options); + + const stream = new QuicStream({ + highWaterMark, + defaultEncoding, + readable: !halfOpen + }, this); + + // TODO(@jasnell): This really shouldn't be necessary + if (halfOpen) { + stream.push(null); + stream.read(); + } + + this.#pendingStreams.add(stream); + + // If early data is being used, we can create the internal QuicStream on the + // ready event, that is immediately after the internal QuicSession handle + // has been created. Otherwise, we have to wait until the secure event + // signaling the completion of the TLS handshake. + const makeStream = QuicSession.#makeStream.bind(this, stream, halfOpen); + let deferred = false; + if (this.allowEarlyData && !this.ready) { + deferred = true; + this.once('ready', makeStream); + } else if (!this.handshakeComplete) { + deferred = true; + this.once('secure', makeStream); + } + + if (!deferred) + makeStream(stream, halfOpen); + + return stream; + } + + static #makeStream = function(stream, halfOpen) { + this.#pendingStreams.delete(stream); + const handle = + halfOpen ? + _openUnidirectionalStream(this[kHandle]) : + _openBidirectionalStream(this[kHandle]); + + if (handle === undefined) { + stream.destroy(new ERR_QUICSTREAM_OPEN_FAILED()); + return; + } + + stream[kSetHandle](handle); + this[kAddStream](stream.id, stream); + }; + + get duration() { + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_SESSION_STATS_CREATED_AT]; + } + + get handshakeDuration() { + const stats = this.#stats || this[kHandle].stats; + const end = + this.handshakeComplete ? + stats[4] : process.hrtime.bigint(); + return end - stats[IDX_QUIC_SESSION_STATS_HANDSHAKE_START_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BYTES_SENT]; + } + + get bidiStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_BIDI_STREAM_COUNT]; + } + + get uniStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_UNI_STREAM_COUNT]; + } + + get maxInFlightBytes() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_MAX_BYTES_IN_FLIGHT]; + } + + get lossRetransmitCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_LOSS_RETRANSMIT_COUNT]; + } + + get ackDelayRetransmitCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_ACK_DELAY_RETRANSMIT_COUNT]; + } + + get peerInitiatedStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_STREAMS_IN_COUNT]; + } + + get selfInitiatedStreamCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_STREAMS_OUT_COUNT]; + } + + get keyUpdateCount() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_KEYUPDATE_COUNT]; + } + + get minRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_MIN_RTT]; + } + + get latestRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_LATEST_RTT]; + } + + get smoothedRTT() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_SESSION_STATS_SMOOTHED_RTT]; + } + + updateKey() { + // Initiates a key update for the connection. + if (this.#destroyed || this.#closing) + throw new ERR_QUICSESSION_DESTROYED('updateKey'); + if (!this.handshakeConfirmed) + throw new ERR_QUICSESSION_UPDATEKEY(); + return this[kHandle].updateKey(); + } + + get handshakeAckHistogram() { + return this.#handshakeAckHistogram; + } + + get handshakeContinuationHistogram() { + return this.#handshakeContinuationHistogram; + } + + // TODO(addaleax): This is a temporary solution for testing and should be + // removed later. + removeFromSocket() { + return this[kHandle].removeFromSocket(); + } +} + +class QuicServerSession extends QuicSession { + #contexts = []; + + constructor(socket, handle, options) { + const { + highWaterMark, + defaultEncoding, + } = options; + super(socket, { highWaterMark, defaultEncoding }); + this[kSetHandle](handle); + + // Both the handle and socket are immediately usable + // at this point so trigger the ready event. + this[kSocketReady](); + } + + // Called only when a clientHello event handler is registered. + // Allows user code an opportunity to interject into the start + // of the TLS handshake. + [kClientHello](alpn, servername, ciphers, callback) { + this.emit( + 'clientHello', + alpn, + servername, + ciphers, + callback.bind(this[kHandle])); + } + + // Called only when an OCSPRequest event handler is registered. + // Allows user code the ability to answer the OCSP query. + [kCert](servername, callback) { + const { serverSecureContext } = this.socket; + let { context } = serverSecureContext; + + for (var i = 0; i < this.#contexts.length; i++) { + const elem = this.#contexts[i]; + if (elem[0].test(servername)) { + context = elem[1]; + break; + } + } + + this.emit( + 'OCSPRequest', + servername, + context, + callback.bind(this[kHandle])); + } + + [kSocketReady]() { + process.nextTick(emit.bind(this, 'ready')); + } + + get ready() { return true; } + + get allowEarlyData() { return false; } + + addContext(servername, context = {}) { + validateString(servername, 'servername'); + validateObject(context, 'context'); + + // TODO(@jasnell): Consider unrolling regex + const re = new RegExp('^' + + servername.replace(/([.^$+?\-\\[\]{}])/g, '\\$1') + .replace(/\*/g, '[^.]*') + + '$'); + this.#contexts.push([re, _createSecureContext(context)]); + } +} + +class QuicClientSession extends QuicSession { + #allowEarlyData = false; + #autoStart = true; + #dcid = undefined; + #handshakeStarted = false; + #ipv6Only = undefined; + #minDHSize = undefined; + #port = undefined; + #ready = 0; + #remoteTransportParams = undefined; + #requestOCSP = undefined; + #secureContext = undefined; + #sessionTicket = undefined; + #transportParams = undefined; + #preferredAddressPolicy; + #verifyHostnameIdentity = true; + #qlogEnabled = false; + + constructor(socket, options) { + const sc_options = { + secureProtocol: 'TLSv1_3_client_method', + ...options + }; + const { + autoStart, + alpn, + dcid, + ipv6Only, + minDHSize, + port, + preferredAddressPolicy, + remoteTransportParams, + requestOCSP, + servername, + sessionTicket, + verifyHostnameIdentity, + qlog, + highWaterMark, + defaultEncoding, + } = validateQuicClientSessionOptions(options); + + if (!verifyHostnameIdentity && !warnedVerifyHostnameIdentity) { + warnedVerifyHostnameIdentity = true; + process.emitWarning( + 'QUIC hostname identity verification is disabled. This violates QUIC ' + + 'specification requirements and reduces security. Hostname identity ' + + 'verification should only be disabled for debugging purposes.' + ); + } + + super(socket, { servername, alpn, highWaterMark, defaultEncoding }); + this.#autoStart = autoStart; + this.#handshakeStarted = autoStart; + this.#dcid = dcid; + this.#ipv6Only = ipv6Only; + this.#minDHSize = minDHSize; + this.#port = port || 0; + this.#preferredAddressPolicy = preferredAddressPolicy; + this.#requestOCSP = requestOCSP; + this.#secureContext = + createSecureContext( + sc_options, + initSecureContextClient); + this.#transportParams = validateTransportParams(options); + this.#verifyHostnameIdentity = verifyHostnameIdentity; + this.#qlogEnabled = qlog; + + // If provided, indicates that the client is attempting to + // resume a prior session. Early data would be enabled. + this.#remoteTransportParams = remoteTransportParams; + this.#sessionTicket = sessionTicket; + this.#allowEarlyData = + remoteTransportParams !== undefined && + sessionTicket !== undefined; + + if (socket.bound) + this[kSocketReady](); + } + + [kHandshakePost]() { + const { type, size } = this.ephemeralKeyInfo; + if (type === 'DH' && size < this.#minDHSize) { + this.destroy(new ERR_TLS_DH_PARAM_SIZE(size)); + return false; + } + return true; + } + + [kCert](response) { + this.emit('OCSPResponse', response); + } + + [kContinueConnect](type, ip) { + setTransportParams(this.#transportParams); + + const options = + (this.#verifyHostnameIdentity ? + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY : 0) | + (this.#requestOCSP ? + QUICCLIENTSESSION_OPTION_REQUEST_OCSP : 0); + + const handle = + _createClientSession( + this.socket[kHandle], + type, + ip, + this.#port, + this.#secureContext.context, + this.servername || ip, + this.#remoteTransportParams, + this.#sessionTicket, + this.#dcid, + this.#preferredAddressPolicy, + this.alpnProtocol, + options, + this.#qlogEnabled, + this.#autoStart); + + // We no longer need these, unset them so + // memory can be garbage collected. + this.#remoteTransportParams = undefined; + this.#sessionTicket = undefined; + this.#dcid = undefined; + + // If handle is a number, creating the session failed. + if (typeof handle === 'number') { + let reason; + switch (handle) { + case ERR_FAILED_TO_CREATE_SESSION: + reason = 'QuicSession bootstrap failed'; + break; + case ERR_INVALID_REMOTE_TRANSPORT_PARAMS: + reason = 'Invalid Remote Transport Params'; + break; + case ERR_INVALID_TLS_SESSION_TICKET: + reason = 'Invalid TLS Session Ticket'; + break; + default: + reason = `${handle}`; + } + this.destroy(new ERR_QUICCLIENTSESSION_FAILED(reason)); + return; + } + + this[kSetHandle](handle); + + // Listeners may have been added before the handle was created. + // Ensure that we toggle those listeners in the handle state. + + if (this.listenerCount('keylog') > 0) + toggleListeners(handle, 'keylog', true); + + if (this.listenerCount('pathValidation') > 0) + toggleListeners(handle, 'pathValidation', true); + + if (this.listenerCount('usePreferredAddress') > 0) + toggleListeners(handle, 'usePreferredAddress', true); + + this.#maybeReady(0x2); + } + + [kSocketReady]() { + this.#maybeReady(0x1); + } + + // The QuicClientSession is ready for use only after + // (a) The QuicSocket has been bound and + // (b) The internal handle has been created + #maybeReady = function(flag) { + this.#ready |= flag; + if (this.ready) + process.nextTick(emit.bind(this, 'ready')); + }; + + get allowEarlyData() { + return this.#allowEarlyData; + } + + get ready() { + return this.#ready === 0x3; + } + + get handshakeStarted() { + return this.#handshakeStarted; + } + + startHandshake() { + if (this.destroyed) + throw new ERR_QUICSESSION_DESTROYED('startHandshake'); + if (this.#handshakeStarted) + return; + this.#handshakeStarted = true; + if (!this.ready) { + this.once('ready', () => this[kHandle].startHandshake()); + } else { + this[kHandle].startHandshake(); + } + } + + get ephemeralKeyInfo() { + return this[kHandle] !== undefined ? + this[kHandle].getEphemeralKeyInfo() : + {}; + } + + #setSocketAfterBind = function(socket, callback) { + if (socket.destroyed) { + callback(new ERR_QUICSOCKET_DESTROYED('setSocket')); + return; + } + + if (!this[kHandle].setSocket(socket[kHandle])) { + callback(new ERR_QUICCLIENTSESSION_FAILED_SETSOCKET()); + return; + } + + if (this.socket) { + this.socket[kRemoveSession](this); + this[kSetSocket](undefined); + } + socket[kAddSession](this); + this[kSetSocket](socket); + + callback(); + }; + + setSocket(socket, callback) { + if (!(socket instanceof QuicSocket)) + throw new ERR_INVALID_ARG_TYPE('socket', 'QuicSocket', socket); + + if (typeof callback !== 'function') + throw new ERR_INVALID_CALLBACK(); + + socket[kMaybeBind](() => this.#setSocketAfterBind(socket, callback)); + } +} + +function streamOnResume() { + if (!this.destroyed) + this[kHandle].readStart(); +} + +function streamOnPause() { + if (!this.destroyed /* && !this.pending */) + this[kHandle].readStop(); +} + +class QuicStream extends Duplex { + #closed = false; + #aborted = false; + #defaultEncoding = undefined; + #didRead = false; + #id = undefined; + #highWaterMark = undefined; + #push_id = undefined; + #resetCode = undefined; + #session = undefined; + #dataRateHistogram = undefined; + #dataSizeHistogram = undefined; + #dataAckHistogram = undefined; + #stats = undefined; + + constructor(options, session, push_id) { + const { + highWaterMark, + defaultEncoding, + } = options; + super({ + highWaterMark, + defaultEncoding, + allowHalfOpen: true, + decodeStrings: true, + emitClose: true, + autoDestroy: false, + captureRejections: true, + }); + this.#highWaterMark = highWaterMark; + this.#defaultEncoding = defaultEncoding; + this.#session = session; + this.#push_id = push_id; + this._readableState.readingMore = true; + this.on('pause', streamOnPause); + + // See src/node_quic_stream.h for an explanation + // of the initial states for unidirectional streams. + if (this.unidirectional) { + if (session instanceof QuicServerSession) { + if (this.serverInitiated) { + // Close the readable side + this.push(null); + this.read(); + } else { + // Close the writable side + this.end(); + } + } else if (this.serverInitiated) { + // Close the writable side + this.end(); + } else { + this.push(null); + this.read(); + } + } + + // The QuicStream writes are corked until kSetHandle + // is set, ensuring that writes are buffered in JavaScript + // until we have somewhere to send them. + this.cork(); + } + + // Set handle is called once the QuicSession has been able + // to complete creation of the internal QuicStream handle. + // This will happen only after the QuicSession's own + // internal handle has been created. The QuicStream object + // is still minimally usable before this but any data + // written will be buffered until kSetHandle is called. + [kSetHandle](handle) { + this[kHandle] = handle; + if (handle !== undefined) { + handle.onread = onStreamRead; + handle[owner_symbol] = this; + this[async_id_symbol] = handle.getAsyncId(); + this.#id = handle.id(); + this.#dataRateHistogram = new Histogram(handle.rate); + this.#dataSizeHistogram = new Histogram(handle.size); + this.#dataAckHistogram = new Histogram(handle.ack); + this.uncork(); + this.emit('ready'); + } else { + if (this.#dataRateHistogram) + this.#dataRateHistogram[kDestroyHistogram](); + if (this.#dataSizeHistogram) + this.#dataSizeHistogram[kDestroyHistogram](); + if (this.#dataAckHistogram) + this.#dataAckHistogram[kDestroyHistogram](); + } + } + + [kStreamReset](code) { + this.#resetCode = code | 0; + this.push(null); + this.read(); + } + + [kHeaders](headers, kind, push_id) { + // TODO(@jasnell): Convert the headers into a proper object + let name; + switch (kind) { + case QUICSTREAM_HEADERS_KIND_NONE: + // Fall through + case QUICSTREAM_HEADERS_KIND_INITIAL: + name = 'initialHeaders'; + break; + case QUICSTREAM_HEADERS_KIND_INFORMATIONAL: + name = 'informationalHeaders'; + break; + case QUICSTREAM_HEADERS_KIND_TRAILING: + name = 'trailingHeaders'; + break; + case QUICSTREAM_HEADERS_KIND_PUSH: + name = 'pushHeaders'; + break; + default: + assert.fail('Invalid headers kind'); + } + process.nextTick(emit.bind(this, name, headers, push_id)); + } + + [kClose](family, code) { + // Trigger the abrupt shutdown of the stream. If the stream is + // already no-longer readable or writable, this does nothing. If + // the stream is readable or writable, then the abort event will + // be emitted immediately after triggering the send of the + // RESET_STREAM and STOP_SENDING frames. The stream will no longer + // be readable or writable, but will not be immediately destroyed + // as we need to wait until ngtcp2 recognizes the stream as + // having been closed to be destroyed. + + // Do nothing if we've already been destroyed + if (this.destroyed || this.#closed) + return; + + if (this.pending) + return this.once('ready', () => this[kClose](family, code)); + + this.#closed = true; + + this.#aborted = this.readable || this.writable; + + // Trigger scheduling of the RESET_STREAM and STOP_SENDING frames + // as appropriate. Notify ngtcp2 that the stream is to be shutdown. + // Once sent, the stream will be closed and destroyed as soon as + // the shutdown is acknowledged by the peer. + this[kHandle].resetStream(code, family); + + // Close down the readable side of the stream + if (this.readable) { + this.push(null); + this.read(); + } + + // It is important to call shutdown on the handle before shutting + // down the writable side of the stream in order to prevent an + // empty STREAM frame with fin set to be sent to the peer. + if (this.writable) + this.end(); + } + + [kAfterAsyncWrite]({ bytes }) { + // TODO(@jasnell): Implement this + } + + [kInspect]() { + const direction = this.bidirectional ? 'bidirectional' : 'unidirectional'; + const initiated = this.serverInitiated ? 'server' : 'client'; + const obj = { + id: this.#id, + direction, + initiated, + writableState: this._writableState, + readableState: this._readableState, + stats: { + dataRate: this.dataRateHistogram, + dataSize: this.dataSizeHistogram, + dataAck: this.dataAckHistogram, + } + }; + return `QuicStream ${util.format(obj)}`; + } + + [kTrackWriteState](stream, bytes) { + // TODO(@jasnell): Not yet sure what we want to do with these + // this.#writeQueueSize += bytes; + // this.#writeQueueSize += bytes; + // this[kHandle].chunksSentSinceLastWrite = 0; + } + + [kUpdateTimer]() { + // TODO(@jasnell): Implement this later + } + + get pending() { + // The id is set in the kSetHandle function + return this.#id === undefined; + } + + get aborted() { + return this.#aborted; + } + + get serverInitiated() { + return !!(this.#id & 0b01); + } + + get clientInitiated() { + return !this.serverInitiated; + } + + get unidirectional() { + return !!(this.#id & 0b10); + } + + get bidirectional() { + return !this.unidirectional; + } + + #writeGeneric = function(writev, data, encoding, cb) { + if (this.destroyed) + return; // TODO(addaleax): Can this happen? + + // The stream should be corked while still pending + // but just in case uncork + // was called early, defer the actual write until the + // ready event is emitted. + if (this.pending) { + return this.once('ready', () => { + this.#writeGeneric(writev, data, encoding, cb); + }); + } + + this[kUpdateTimer](); + const req = (writev) ? + writevGeneric(this, data, cb) : + writeGeneric(this, data, encoding, cb); + + this[kTrackWriteState](this, req.bytes); + }; + + _write(data, encoding, cb) { + this.#writeGeneric(false, data, encoding, cb); + } + + _writev(data, cb) { + this.#writeGeneric(true, data, '', cb); + } + + // Called when the last chunk of data has been + // acknowledged by the peer and end has been + // called. By calling shutdown, we're telling + // the native side that no more data will be + // coming so that a fin stream packet can be + // sent. + _final(cb) { + // The QuicStream should be corked while pending + // so this shouldn't be called, but just in case + // the stream was prematurely uncorked, defer the + // operation until the ready event is emitted. + if (this.pending) + return this.once('ready', () => this._final(cb)); + + const handle = this[kHandle]; + if (handle === undefined) { + cb(); + return; + } + + const req = new ShutdownWrap(); + req.oncomplete = () => cb(); + req.handle = handle; + const err = handle.shutdown(req); + if (err === 1) + return cb(); + } + + _read(nread) { + if (this.pending) + return this.once('ready', () => this._read(nread)); + + if (this.destroyed) { // TODO(addaleax): Can this happen? + this.push(null); + return; + } + if (!this.#didRead) { + this._readableState.readingMore = false; + this.#didRead = true; + } + + streamOnResume.call(this); + } + + sendFile(path, options = {}) { + fs.open(path, 'r', QuicStream.#onFileOpened.bind(this, options)); + } + + static #onFileOpened = function(options, err, fd) { + const onError = options.onError; + if (err) { + if (onError) { + this.close(); + onError(err); + } else { + this.destroy(err); + } + return; + } + + if (this.destroyed || this.closed) { + fs.close(fd, (err) => { if (err) throw err; }); + return; + } + + this.sendFD(fd, options, true); + }; + + sendFD(fd, { offset = -1, length = -1 } = {}, ownsFd = false) { + if (this.destroyed || this.#closed) + return; + + validateInteger(offset, 'options.offset', { min: -1 }); + validateInteger(length, 'options.length', { min: -1 }); + + if (fd instanceof fsPromisesInternal.FileHandle) + fd = fd.fd; + else if (typeof fd !== 'number') + throw new ERR_INVALID_ARG_TYPE('fd', ['number', 'FileHandle'], fd); + + if (this.pending) { + return this.once('ready', () => { + this.sendFD(fd, { offset, length }, ownsFd); + }); + } + + this[kUpdateTimer](); + this.ownsFd = ownsFd; + + // Close the writable side of the stream, but only as far as the writable + // stream implementation is concerned. + this._final = null; + this.end(); + + defaultTriggerAsyncIdScope(this[async_id_symbol], + QuicStream.#startFilePipe, + this, fd, offset, length); + } + + static #startFilePipe = (stream, fd, offset, length) => { + const handle = new FileHandle(fd, offset, length); + handle.onread = QuicStream.#onPipedFileHandleRead; + handle.stream = stream; + + const pipe = new StreamPipe(handle, stream[kHandle]); + pipe.onunpipe = QuicStream.#onFileUnpipe; + pipe.start(); + + // Exact length of the file doesn't matter here, since the + // stream is closing anyway - just use 1 to signify that + // a write does exist + stream[kTrackWriteState](stream, 1); + } + + static #onFileUnpipe = function() { // Called on the StreamPipe instance. + const stream = this.sink[owner_symbol]; + if (stream.ownsFd) + this.source.close().catch(stream.destroy.bind(stream)); + else + this.source.releaseFD(); + }; + + static #onPipedFileHandleRead = function() { + const err = streamBaseState[kReadBytesOrError]; + if (err < 0 && err !== UV_EOF) { + this.stream.destroy(errnoException(err, 'sendFD')); + } + }; + + get resetReceived() { + return (this.#resetCode !== undefined) ? + { code: this.#resetCode | 0 } : + undefined; + } + + get bufferSize() { + // TODO(@jasnell): Implement this + return undefined; + } + + get id() { + return this.#id; + } + + get push_id() { + return this.#push_id; + } + + close(code) { + this[kClose](QUIC_ERROR_APPLICATION, code); + } + + get session() { + return this.#session; + } + + _destroy(error, callback) { + this.#session[kRemoveStream](this); + const handle = this[kHandle]; + // Do not use handle after this point as the underlying C++ + // object has been destroyed. Any attempt to use the object + // will segfault and crash the process. + if (handle !== undefined) { + this.#stats = new BigInt64Array(handle.stats); + handle.destroy(); + } + // The destroy callback must be invoked in a nextTick + process.nextTick(() => callback(error)); + } + + _onTimeout() { + // TODO(@jasnell): Implement this + } + + get dataRateHistogram() { + return this.#dataRateHistogram; + } + + get dataSizeHistogram() { + return this.#dataSizeHistogram; + } + + get dataAckHistogram() { + return this.#dataAckHistogram; + } + + pushStream(headers = {}, options = {}) { + if (this.destroyed) + throw new ERR_QUICSTREAM_DESTROYED('push'); + + const { + highWaterMark = this.#highWaterMark, + defaultEncoding = this.#defaultEncoding, + } = validateQuicStreamOptions(options); + + validateObject(headers, 'headers'); + + // Push streams are only supported on QUIC servers, and + // only if the original stream is bidirectional. + // TODO(@jasnell): This is really an http/3 specific + // requirement so if we end up later with another + // QUIC application protocol that has a similar + // notion of push streams without this restriction, + // then we'll need to check alpn value here also. + if (!this.clientInitiated && !this.bidirectional) + throw new ERR_QUICSTREAM_INVALID_PUSH(); + + // TODO(@jasnell): The assertValidPseudoHeader validator + // here is HTTP/3 specific. If we end up with another + // QUIC application protocol that supports push streams, + // we will need to select a validator based on the + // alpn value. + const handle = this[kHandle].submitPush( + mapToHeaders(headers, assertValidPseudoHeader)); + + // If undefined is returned, it either means that push + // streams are not supported by the underlying application, + // or push streams are currently disabled/blocked. This + // will typically be the case with HTTP/3 when the client + // peer has disabled push. + if (handle === undefined) + throw new ERR_QUICSTREAM_UNSUPPORTED_PUSH(); + + const stream = new QuicStream({ + readable: false, + highWaterMark, + defaultEncoding, + }, this.session); + + // TODO(@jasnell): The null push and subsequent read shouldn't be necessary + stream.push(null); + stream.read(); + + stream[kSetHandle](handle); + this.session[kAddStream](stream.id, stream); + return stream; + } + + submitInformationalHeaders(headers = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + validateObject(headers, 'headers'); + + // TODO(@jasnell): The validators here are specific to the QUIC + // protocol. In the case below, these are the http/3 validators + // (which are identical to the rules for http/2). We need to + // find a way for this to be easily abstracted based on the + // selected alpn. + + let validator; + if (this.session instanceof QuicServerSession) { + validator = + this.clientInitiated ? + assertValidPseudoHeaderResponse : + assertValidPseudoHeader; + } else { // QuicClientSession + validator = + this.clientInitiated ? + assertValidPseudoHeader : + assertValidPseudoHeaderResponse; + } + + return this[kHandle].submitInformationalHeaders( + mapToHeaders(headers, validator)); + } + + submitInitialHeaders(headers = {}, options = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + const { terminal } = { ...options }; + + validateBoolean(terminal, 'options.terminal', { allowUndefined: true }); + validateObject(headers, 'headers'); + + // TODO(@jasnell): The validators here are specific to the QUIC + // protocol. In the case below, these are the http/3 validators + // (which are identical to the rules for http/2). We need to + // find a way for this to be easily abstracted based on the + // selected alpn. + + let validator; + if (this.session instanceof QuicServerSession) { + validator = + this.clientInitiated ? + assertValidPseudoHeaderResponse : + assertValidPseudoHeader; + } else { // QuicClientSession + validator = + this.clientInitiated ? + assertValidPseudoHeader : + assertValidPseudoHeaderResponse; + } + + return this[kHandle].submitHeaders( + mapToHeaders(headers, validator), + terminal ? + QUICSTREAM_HEADER_FLAGS_TERMINAL : + QUICSTREAM_HEADER_FLAGS_NONE); + } + + submitTrailingHeaders(headers = {}) { + // TODO(@jasnell): Likely better to throw here instead of return false + if (this.destroyed) + return false; + + validateObject(headers, 'headers'); + + // TODO(@jasnell): The validators here are specific to the QUIC + // protocol. In the case below, these are the http/3 validators + // (which are identical to the rules for http/2). We need to + // find a way for this to be easily abstracted based on the + // selected alpn. + + return this[kHandle].submitTrailers( + mapToHeaders(headers, assertValidPseudoHeaderTrailer)); + } + + get duration() { + const now = process.hrtime.bigint(); + const stats = this.#stats || this[kHandle].stats; + return now - stats[IDX_QUIC_STREAM_STATS_CREATED_AT]; + } + + get bytesReceived() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_BYTES_RECEIVED]; + } + + get bytesSent() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_BYTES_SENT]; + } + + get maxExtendedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET]; + } + + get finalSize() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_FINAL_SIZE]; + } + + get maxAcknowledgedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_ACK]; + } + + get maxReceivedOffset() { + const stats = this.#stats || this[kHandle].stats; + return stats[IDX_QUIC_STREAM_STATS_MAX_OFFSET_RECV]; + } +} + +function createSocket(options) { + return new QuicSocket(options); +} + +module.exports = { + createSocket, + kUDPHandleForTesting +}; + +/* eslint-enable no-use-before-define */ + +// A single QuicSocket may act as both a Server and a Client. +// There are two kinds of sessions: +// * QuicServerSession +// * QuicClientSession +// +// It is important to understand that QUIC sessions are +// independent of the QuicSocket. A default configuration +// for QuicServerSession and QuicClientSessions may be +// set when the QuicSocket is created, but the actual +// configuration for a particular QuicSession instance is +// not set until the session itself is created. +// +// QuicSockets and QuicSession instances have distinct +// configuration options that apply independently: +// +// QuicSocket Options: +// * `lookup` {Function} A function used to resolve DNS names. +// * `type` {string} Either `'udp4'` or `'udp6'`, defaults to +// `'udp4'`. +// * `port` {number} The local IP port the QuicSocket will +// bind to. +// * `address` {string} The local IP address or hostname that +// the QuicSocket will bind to. If a hostname is given, the +// `lookup` function will be invoked to resolve an IP address. +// * `ipv6Only` +// * `reuseAddr` +// +// Keep in mind that while all QUIC network traffic is encrypted +// using TLS 1.3, every QuicSession maintains it's own SecureContext +// that is completely independent of the QuicSocket. Every +// QuicServerSession and QuicClientSession could, in theory, +// use a completely different TLS 1.3 configuration. To keep it +// simple, however, we use the same SecureContext for all QuicServerSession +// instances, but that may be something we want to revisit later. +// +// Every QuicSession has two sets of configuration parameters: +// * Options +// * Transport Parameters +// +// Options establish implementation specific operation parameters, +// such as the default highwatermark for new QuicStreams. Transport +// Parameters are QUIC specific and are passed to the peer as part +// of the TLS handshake. +// +// Every QuicSession may have separate options and transport +// parameters, even within the same QuicSocket, so the configuration +// must be established when the session is created. +// +// When creating a QuicSocket, it is possible to set a default +// configuration for both QuicServerSession and QuicClientSession +// options. +// +// const soc = createSocket({ +// type: 'udp4', +// port: 0, +// server: { +// // QuicServerSession configuration defaults +// }, +// client: { +// // QuicClientSession configuration defaults +// } +// }); +// +// When calling listen() on the created QuicSocket, the server +// specific configuration that will be used for all new +// QuicServerSession instances will be given, with the values +// provided to createSocket() using the server option used +// as a default. +// +// When calling connect(), the client specific configuration +// will be given, with the values provided to the createSocket() +// using the client option used as a default. + + +// Some lifecycle documentation for the various objects: +// +// QuicSocket +// Close +// * Close all existing Sessions +// * Do not allow any new Sessions (inbound or outbound) +// * Destroy once there are no more sessions + +// Destroy +// * Destroy all remaining sessions +// * Destroy and free the QuicSocket handle immediately +// * If Error, emit Error event +// * Emit Close event + +// QuicClientSession +// Close +// * Allow existing Streams to complete normally +// * Do not allow any new Streams (inbound or outbound) +// * Destroy once there are no more streams + +// Destroy +// * Send CONNECTION_CLOSE +// * Destroy all remaining Streams +// * Remove Session from Parent Socket +// * Destroy and free the QuicSession handle immediately +// * If Error, emit Error event +// * Emit Close event + +// QuicServerSession +// Close +// * Allow existing Streams to complete normally +// * Do not allow any new Streams (inbound or outbound) +// * Destroy once there are no more streams +// Destroy +// * Send CONNECTION_CLOSE +// * Destroy all remaining Streams +// * Remove Session from Parent Socket +// * Destroy and free the QuicSession handle immediately +// * If Error, emit Error event +// * Emit Close event + +// QuicStream +// Destroy +// * Remove Stream From Parent Session +// * Destroy and free the QuicStream handle immediately +// * If Error, emit Error event +// * Emit Close event + +// QuicEndpoint States: +// Initial -- Created, Endpoint not yet bound to local UDP +// port. +// Pending -- Binding to local UDP port has been initialized. +// Bound -- Binding to local UDP port has completed. +// Closed -- Endpoint has been unbound from the local UDP +// port. +// Error -- An error has been encountered, endpoint has +// been unbound and is no longer usable. +// +// QuicSocket States: +// Initial -- Created, QuicSocket has at least one +// QuicEndpoint. All QuicEndpoints are in the +// Initial state. +// Pending -- QuicSocket has at least one QuicEndpoint in the +// Pending state. +// Bound -- QuicSocket has at least one QuicEndpoint in the +// Bound state. +// Closed -- All QuicEndpoints are in the closed state. +// Destroyed -- QuicSocket is no longer usable +// Destroyed-With-Error -- An error has been encountered, socket is no +// longer usable +// +// QuicSession States: +// Initial -- Created, QuicSocket state undetermined, +// no internal QuicSession Handle assigned. +// Ready +// QuicSocket Ready -- QuicSocket in Bound state. +// Handle Assigned -- Internal QuicSession Handle assigned. +// TLS Handshake Started -- +// TLS Handshake Completed -- +// TLS Handshake Confirmed -- +// Closed -- Graceful Close Initiated +// Destroyed -- QuicSession is no longer usable +// Destroyed-With-Error -- An error has been encountered, session is no +// longer usable +// +// QuicStream States: +// Initial Writable/Corked -- Created, QuicSession in Initial State +// Open Writable/Uncorked -- QuicSession in Ready State +// Closed -- Graceful Close Initiated +// Destroyed -- QuicStream is no longer usable +// Destroyed-With-Error -- An error has been encountered, stream is no +// longer usable diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js new file mode 100644 index 00000000000000..28119b8e2628ff --- /dev/null +++ b/lib/internal/quic/util.js @@ -0,0 +1,717 @@ +'use strict'; + +const { + codes: { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_QUICSESSION_INVALID_DCID, + ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH, + }, +} = require('internal/errors'); + +const { + isIP, +} = require('internal/net'); + +const { + getOptionValue, + getAllowUnauthorized, +} = require('internal/options'); + +const { Buffer } = require('buffer'); + +const { + sessionConfig, + http3Config, + constants: { + AF_INET, + AF_INET6, + NGTCP2_ALPN_H3, + DEFAULT_RETRYTOKEN_EXPIRATION, + DEFAULT_MAX_CONNECTIONS, + DEFAULT_MAX_CONNECTIONS_PER_HOST, + DEFAULT_MAX_STATELESS_RESETS_PER_HOST, + IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, + IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, + IDX_QUIC_SESSION_MAX_DATA, + IDX_QUIC_SESSION_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_MAX_STREAMS_UNI, + IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, + IDX_QUIC_SESSION_MAX_ACK_DELAY, + IDX_QUIC_SESSION_MAX_PACKET_SIZE, + IDX_QUIC_SESSION_CONFIG_COUNT, + IDX_QUIC_SESSION_STATE_CERT_ENABLED, + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, + IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, + IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, + IDX_HTTP3_QPACK_BLOCKED_STREAMS, + IDX_HTTP3_MAX_HEADER_LIST_SIZE, + IDX_HTTP3_MAX_PUSHES, + IDX_HTTP3_MAX_HEADER_PAIRS, + IDX_HTTP3_MAX_HEADER_LENGTH, + IDX_HTTP3_CONFIG_COUNT, + MAX_RETRYTOKEN_EXPIRATION, + MIN_RETRYTOKEN_EXPIRATION, + NGTCP2_NO_ERROR, + NGTCP2_MAX_CIDLEN, + NGTCP2_MIN_CIDLEN, + QUIC_PREFERRED_ADDRESS_IGNORE, + QUIC_PREFERRED_ADDRESS_ACCEPT, + QUIC_ERROR_APPLICATION, + } +} = internalBinding('quic'); + +const { + validateBoolean, + validateBuffer, + validateInteger, + validateNumber, + validateObject, + validatePort, + validateString, +} = require('internal/validators'); + +const kDefaultQuicCiphers = 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:' + + 'TLS_CHACHA20_POLY1305_SHA256'; +const kDefaultGroups = 'P-256:X25519:P-384:P-521'; + +let dns; + +function lazyDNS() { + if (!dns) + dns = require('dns'); + return dns; +} + +function getSocketType(type = 'udp4') { + switch (type) { + case 'udp4': return AF_INET; + case 'udp6': return AF_INET6; + } + throw new ERR_INVALID_ARG_VALUE('options.type', type); +} + +function lookup4(address, callback) { + const { lookup } = lazyDNS(); + lookup(address || '127.0.0.1', 4, callback); +} + +function lookup6(address, callback) { + const { lookup } = lazyDNS(); + lookup(address || '::1', 6, callback); +} + +function validateCloseCode(code) { + if (code != null && typeof code === 'object') { + return { + closeCode: code.code || NGTCP2_NO_ERROR, + closeFamily: code.family || QUIC_ERROR_APPLICATION, + }; + } else if (typeof code === 'number') { + return { + closeCode: code, + closeFamily: QUIC_ERROR_APPLICATION, + }; + } + throw new ERR_INVALID_ARG_TYPE('code', ['number', 'Object'], code); +} + +function validateLookup(lookup) { + if (lookup && typeof lookup !== 'function') + throw new ERR_INVALID_ARG_TYPE('options.lookup', 'Function', lookup); +} + +function validatePreferredAddress(address) { + if (address !== undefined) { + validateObject(address, 'options.preferredAddress'); + validateString(address.address, 'options.preferredAddress.address'); + if (address.port !== undefined) + validatePort(address.port, 'options.preferredAddress.port'); + getSocketType(address.type); + } + return address; +} + +// Validate known transport parameters, ignoring any that are not +// supported. Ensures that only supported parameters are passed on. +function validateTransportParams(params) { + const { + activeConnectionIdLimit, + maxStreamDataBidiLocal, + maxStreamDataBidiRemote, + maxStreamDataUni, + maxData, + maxStreamsBidi, + maxStreamsUni, + idleTimeout, + maxPacketSize, + maxAckDelay, + preferredAddress, + rejectUnauthorized, + requestCert, + h3: { + qpackMaxTableCapacity, + qpackBlockedStreams, + maxHeaderListSize, + maxPushes, + maxHeaderPairs, + maxHeaderLength = getOptionValue('--max-http-header-size'), + }, + } = { h3: {}, ...params }; + + validateInteger( + activeConnectionIdLimit, + 'options.activeConnectionIdLimit', + { min: 2, max: 8, allowUndefined: true }); + validateInteger( + maxStreamDataBidiLocal, + 'options.maxStreamDataBidiLocal', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamDataBidiRemote, + 'options.maxStreamDataBidiRemote', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamDataUni, + 'options.maxStreamDataUni', + { min: 0, allowUndefined: true }); + validateInteger( + maxData, + 'options.maxData', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamsBidi, + 'options.maxStreamsBidi', + { min: 0, allowUndefined: true }); + validateInteger( + maxStreamsUni, + 'options.maxStreamsUni', + { min: 0, allowUndefined: true }); + validateInteger( + idleTimeout, + 'options.idleTimeout', + { min: 0, allowUndefined: true }); + validateInteger( + maxPacketSize, + 'options.maxPacketSize', + { min: 0, allowUndefined: true }); + validateInteger( + maxAckDelay, + 'options.maxAckDelay', + { min: 0, allowUndefined: true }); + validateInteger( + qpackMaxTableCapacity, + 'options.h3.qpackMaxTableCapacity', + { min: 0, allowUndefined: true }); + validateInteger( + qpackBlockedStreams, + 'options.h3.qpackBlockedStreams', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderListSize, + 'options.h3.maxHeaderListSize', + { min: 0, allowUndefined: true }); + validateInteger( + maxPushes, + 'options.h3.maxPushes', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderPairs, + 'options.h3.maxHeaderPairs', + { min: 0, allowUndefined: true }); + validateInteger( + maxHeaderLength, + 'options.h3.maxHeaderLength', + { min: 0, allowUndefined: true }); + + validatePreferredAddress(preferredAddress); + + return { + activeConnectionIdLimit, + maxStreamDataBidiLocal, + maxStreamDataBidiRemote, + maxStreamDataUni, + maxData, + maxStreamsBidi, + maxStreamsUni, + idleTimeout, + maxPacketSize, + maxAckDelay, + preferredAddress, + rejectUnauthorized, + requestCert, + h3: { + qpackMaxTableCapacity, + qpackBlockedStreams, + maxHeaderListSize, + maxPushes, + maxHeaderPairs, + maxHeaderLength, + } + }; +} + +function validateQuicClientSessionOptions(options = {}) { + if (options !== null && typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); + const { + autoStart = true, + address = 'localhost', + alpn = '', + dcid: dcid_value, + ipv6Only = false, + minDHSize = 1024, + port = 0, + preferredAddressPolicy = 'ignore', + remoteTransportParams, + requestOCSP = false, + servername = (isIP(address) ? '' : address), + sessionTicket, + verifyHostnameIdentity = true, + qlog = false, + highWaterMark, + defaultEncoding, + } = options; + + validateBoolean(autoStart, 'options.autoStart'); + validateNumber(minDHSize, 'options.minDHSize'); + validatePort(port, 'options.port'); + validateString(address, 'options.address'); + validateString(alpn, 'options.alpn'); + validateString(servername, 'options.servername'); + + if (isIP(servername)) { + throw new ERR_INVALID_ARG_VALUE( + 'options.servername', + servername, + 'cannot be an IP address'); + } + + validateBuffer( + remoteTransportParams, + 'options.remoteTransportParams', + { allowUndefined: true }); + + validateBuffer( + sessionTicket, + 'options.sessionTicket', + { allowUndefined: true }); + + let dcid; + if (dcid_value !== undefined) { + if (typeof dcid_value === 'string') { + // If it's a string, it must be a hex encoded string + try { + dcid = Buffer.from(dcid_value, 'hex'); + } catch { + throw new ERR_QUICSESSION_INVALID_DCID(dcid); + } + } + + validateBuffer( + dcid_value, + 'options.dcid', + ['string', 'Buffer', 'TypedArray', 'DataView']); + + if (dcid_value.length > NGTCP2_MAX_CIDLEN || + dcid_value.length < NGTCP2_MIN_CIDLEN) { + throw new ERR_QUICSESSION_INVALID_DCID(dcid_value.toString('hex')); + } + + dcid = dcid_value; + } + + if (preferredAddressPolicy !== undefined) + validateString(preferredAddressPolicy, 'options.preferredAddressPolicy'); + + validateBoolean(ipv6Only, 'options.ipv6Only'); + validateBoolean(requestOCSP, 'options.requestOCSP'); + validateBoolean(verifyHostnameIdentity, 'options.verifyHostnameIdentity'); + validateBoolean(qlog, 'options.qlog'); + + return { + autoStart, + address, + alpn, + dcid, + ipv6Only, + minDHSize, + port, + preferredAddressPolicy: + preferredAddressPolicy === 'accept' ? + QUIC_PREFERRED_ADDRESS_ACCEPT : + QUIC_PREFERRED_ADDRESS_IGNORE, + remoteTransportParams, + requestOCSP, + servername, + sessionTicket, + verifyHostnameIdentity, + qlog, + ...validateQuicStreamOptions({ highWaterMark, defaultEncoding }) + }; +} + +function validateQuicStreamOptions(options = {}) { + validateObject(options); + const { + defaultEncoding = 'utf8', + halfOpen, + highWaterMark, + } = options; + if (!Buffer.isEncoding(defaultEncoding)) { + throw new ERR_INVALID_ARG_VALUE( + 'options.defaultEncoding', + defaultEncoding, + 'is not a valid encoding'); + } + validateBoolean(halfOpen, 'options.halfOpen', { allowUndefined: true }); + validateInteger(highWaterMark, 'options.highWaterMark', { + min: 0, + allowUndefined: true + }); + return { + defaultEncoding, + halfOpen, + highWaterMark, + }; +} + +function validateQuicEndpointOptions(options = {}, name = 'options') { + validateObject(options, name); + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); + const { + address, + ipv6Only = false, + lookup, + port = 0, + reuseAddr = false, + type = 'udp4', + preferred = false, + } = options; + validateString(address, 'options.address', { allowUndefined: true }); + validatePort(port, 'options.port'); + validateString(type, 'options.type'); + validateLookup(lookup); + validateBoolean(ipv6Only, 'options.ipv6Only'); + validateBoolean(reuseAddr, 'options.reuseAddr'); + validateBoolean(preferred, 'options.preferred'); + return { + address, + ipv6Only, + lookup, + port, + preferred, + reuseAddr, + type: getSocketType(type), + }; +} + +function validateQuicSocketOptions(options = {}) { + validateObject(options, 'options'); + + const { + autoClose = false, + client = {}, + disableStatelessReset = false, + endpoint = { port: 0, type: 'udp4' }, + lookup, + maxConnections = DEFAULT_MAX_CONNECTIONS, + maxConnectionsPerHost = DEFAULT_MAX_CONNECTIONS_PER_HOST, + maxStatelessResetsPerHost = DEFAULT_MAX_STATELESS_RESETS_PER_HOST, + qlog = false, + retryTokenTimeout = DEFAULT_RETRYTOKEN_EXPIRATION, + server = {}, + statelessResetSecret, + type = endpoint.type || 'udp4', + validateAddressLRU = false, + validateAddress = false, + } = options; + + validateQuicEndpointOptions(endpoint, 'options.endpoint'); + validateObject(client, 'options.client'); + validateObject(server, 'options.server'); + validateString(type, 'options.type'); + validateLookup(lookup); + validateBoolean(validateAddress, 'options.validateAddress'); + validateBoolean(validateAddressLRU, 'options.validateAddressLRU'); + validateBoolean(autoClose, 'options.autoClose'); + validateBoolean(qlog, 'options.qlog'); + validateBoolean(disableStatelessReset, 'options.disableStatelessReset'); + + validateInteger( + retryTokenTimeout, + 'options.retryTokenTimeout', + { + min: MIN_RETRYTOKEN_EXPIRATION, + max: MAX_RETRYTOKEN_EXPIRATION, + allowUndefined: true + }); + validateInteger( + maxConnections, + 'options.maxConnections', + { min: 1, allowUndefined: true }); + validateInteger( + maxConnectionsPerHost, + 'options.maxConnectionsPerHost', + { min: 1, allowUndefined: true }); + validateInteger( + maxStatelessResetsPerHost, + 'options.maxStatelessResetsPerHost', + { min: 1, allowUndefined: true }); + + if (statelessResetSecret !== undefined) { + validateBuffer(statelessResetSecret, 'options.statelessResetSecret'); + if (statelessResetSecret.length !== 16) + throw new ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH(); + } + + return { + endpoint, + autoClose, + client, + lookup, + maxConnections, + maxConnectionsPerHost, + maxStatelessResetsPerHost, + retryTokenTimeout, + server, + type: getSocketType(type), + validateAddress: validateAddress || validateAddressLRU, + validateAddressLRU, + qlog, + statelessResetSecret, + disableStatelessReset, + }; +} + +function validateQuicSocketListenOptions(options = {}) { + validateObject(options); + const { + alpn = NGTCP2_ALPN_H3, + defaultEncoding, + highWaterMark, + requestCert, + rejectUnauthorized, + } = options; + validateString(alpn, 'options.alpn'); + validateBoolean( + rejectUnauthorized, + 'options.rejectUnauthorized', + { allowUndefined: true }); + validateBoolean( + requestCert, + 'options.requestCert', + { allowUndefined: true }); + + const transportParams = + validateTransportParams( + options, + NGTCP2_MAX_CIDLEN, + NGTCP2_MIN_CIDLEN); + + return { + alpn, + rejectUnauthorized, + requestCert, + transportParams, + ...validateQuicStreamOptions({ highWaterMark, defaultEncoding }) + }; +} + +function validateQuicSocketConnectOptions(options = {}) { + validateObject(options); + const { + type = 'udp4', + address, + } = options; + validateString(address, 'options.address', { allowUndefined: true }); + return { + type: getSocketType(type), + address, + }; +} + +function validateCreateSecureContextOptions(options = {}) { + validateObject(options); + const { + ca, + cert, + ciphers = kDefaultQuicCiphers, + clientCertEngine, + crl, + dhparam, + ecdhCurve, + groups = kDefaultGroups, + honorCipherOrder, + key, + earlyData = true, // Early data is enabled by default + passphrase, + pfx, + sessionIdContext, + secureProtocol + } = options; + validateString(ciphers, 'option.ciphers'); + validateString(groups, 'option.groups'); + validateBoolean(earlyData, 'option.earlyData', { allowUndefined: true }); + + // Additional validation occurs within the tls + // createSecureContext function. + return { + ca, + cert, + ciphers, + clientCertEngine, + crl, + dhparam, + ecdhCurve, + groups, + honorCipherOrder, + key, + earlyData, + passphrase, + pfx, + sessionIdContext, + secureProtocol + }; +} + +function setConfigField(buffer, val, index) { + if (typeof val === 'number') { + buffer[index] = val; + return 1 << index; + } + return 0; +} + +// Extracts configuration options and updates the aliased buffer +// arrays that are used to communicate config choices to the c++ +// internals. +function setTransportParams(config) { + const { + activeConnectionIdLimit, + maxStreamDataBidiLocal, + maxStreamDataBidiRemote, + maxStreamDataUni, + maxData, + maxStreamsBidi, + maxStreamsUni, + idleTimeout, + maxPacketSize, + maxAckDelay, + h3: { + qpackMaxTableCapacity, + qpackBlockedStreams, + maxHeaderListSize, + maxPushes, + maxHeaderPairs, + maxHeaderLength, + }, + } = { h3: {}, ...config }; + + // The const flags is a bitmap that is used to communicate whether or not a + // given configuration value has been explicitly provided. + const flags = setConfigField(sessionConfig, + activeConnectionIdLimit, + IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) | + setConfigField(sessionConfig, + maxStreamDataBidiLocal, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) | + setConfigField(sessionConfig, + maxStreamDataBidiRemote, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) | + setConfigField(sessionConfig, + maxStreamDataUni, + IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI) | + setConfigField(sessionConfig, + maxData, + IDX_QUIC_SESSION_MAX_DATA) | + setConfigField(sessionConfig, + maxStreamsBidi, + IDX_QUIC_SESSION_MAX_STREAMS_BIDI) | + setConfigField(sessionConfig, + maxStreamsUni, + IDX_QUIC_SESSION_MAX_STREAMS_UNI) | + setConfigField(sessionConfig, + idleTimeout, + IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) | + setConfigField(sessionConfig, + maxAckDelay, + IDX_QUIC_SESSION_MAX_ACK_DELAY) | + setConfigField(sessionConfig, + maxPacketSize, + IDX_QUIC_SESSION_MAX_PACKET_SIZE); + + sessionConfig[IDX_QUIC_SESSION_CONFIG_COUNT] = flags; + + const h3flags = setConfigField(http3Config, + qpackMaxTableCapacity, + IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY) | + setConfigField(http3Config, + qpackBlockedStreams, + IDX_HTTP3_QPACK_BLOCKED_STREAMS) | + setConfigField(http3Config, + maxHeaderListSize, + IDX_HTTP3_MAX_HEADER_LIST_SIZE) | + setConfigField(http3Config, + maxPushes, + IDX_HTTP3_MAX_PUSHES) | + setConfigField(http3Config, + maxHeaderPairs, + IDX_HTTP3_MAX_HEADER_PAIRS) | + setConfigField(http3Config, + maxHeaderLength, + IDX_HTTP3_MAX_HEADER_LENGTH); + + http3Config[IDX_HTTP3_CONFIG_COUNT] = h3flags; +} + +// Some events that are emitted originate from the C++ internals and are +// fairly expensive and optional. An aliased array buffer is used to +// communicate that a handler has been added for the optional events +// so that the C++ internals know there is an actual listener. The event +// will not be emitted if there is no handler. +function toggleListeners(handle, event, on) { + if (handle === undefined) + return; + const val = on ? 1 : 0; + switch (event) { + case 'keylog': + handle.state[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] = val; + break; + case 'clientHello': + handle.state[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = val; + break; + case 'pathValidation': + handle.state[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] = val; + break; + case 'OCSPRequest': + handle.state[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = val; + break; + case 'usePreferredAddress': + handle.state[IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED] = on; + break; + } +} + +module.exports = { + getAllowUnauthorized, + getSocketType, + lookup4, + lookup6, + setTransportParams, + toggleListeners, + validateCloseCode, + validateTransportParams, + validateQuicClientSessionOptions, + validateQuicSocketOptions, + validateQuicStreamOptions, + validateQuicSocketListenOptions, + validateQuicEndpointOptions, + validateCreateSecureContextOptions, + validateQuicSocketConnectOptions, +}; diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js index 800ba4cefd6370..7db73141cb34bd 100644 --- a/lib/internal/stream_base_commons.js +++ b/lib/internal/stream_base_commons.js @@ -101,7 +101,7 @@ function onWriteComplete(status) { this.callback(null); } -function createWriteWrap(handle) { +function createWriteWrap(handle, callback) { const req = new WriteWrap(); req.handle = handle; @@ -109,12 +109,13 @@ function createWriteWrap(handle) { req.async = false; req.bytes = 0; req.buffer = null; + req.callback = callback; return req; } function writevGeneric(self, data, cb) { - const req = createWriteWrap(self[kHandle]); + const req = createWriteWrap(self[kHandle], cb); const allBuffers = data.allBuffers; let chunks; if (allBuffers) { @@ -134,29 +135,28 @@ function writevGeneric(self, data, cb) { // Retain chunks if (err === 0) req._chunks = chunks; - afterWriteDispatched(self, req, err, cb); + afterWriteDispatched(self, req, err); return req; } function writeGeneric(self, data, encoding, cb) { - const req = createWriteWrap(self[kHandle]); + const req = createWriteWrap(self[kHandle], cb); const err = handleWriteReq(req, data, encoding); - - afterWriteDispatched(self, req, err, cb); + afterWriteDispatched(self, req, err); return req; } -function afterWriteDispatched(self, req, err, cb) { +function afterWriteDispatched(self, req, err) { req.bytes = streamBaseState[kBytesWritten]; req.async = !!streamBaseState[kLastWriteWasAsync]; if (err !== 0) - return self.destroy(errnoException(err, 'write', req.error), cb); + return self.destroy( + errnoException(err, 'write', req.error), + req.callback); - if (!req.async) { - cb(); - } else { - req.callback = cb; + if (!req.async && typeof req.callback === 'function') { + req.callback(); } } diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 46237e54342e5b..a7e1dd9c7fae05 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -2,9 +2,11 @@ const { ArrayIsArray, + NumberINFINITY, NumberIsInteger, NumberMAX_SAFE_INTEGER, NumberMIN_SAFE_INTEGER, + NumberNEGATIVE_INFINITY, } = primordials; const { @@ -70,7 +72,12 @@ function parseFileMode(value, name, def) { } const validateInteger = hideStackFrames( - (value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => { + (value, name, + { min = NumberMIN_SAFE_INTEGER, + max = NumberMAX_SAFE_INTEGER, + allowUndefined = false } = {}) => { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value); if (!NumberIsInteger(value)) @@ -115,17 +122,25 @@ const validateUint32 = hideStackFrames((value, name, positive) => { } }); -function validateString(value, name) { +function validateString(value, name, { allowUndefined = false } = {}) { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'string') throw new ERR_INVALID_ARG_TYPE(name, 'string', value); } -function validateNumber(value, name) { +function validateNumber(value, name, + { min = NumberNEGATIVE_INFINITY, + max = NumberINFINITY } = {}) { if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value); + if (value < min || value > max) + throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); } -function validateBoolean(value, name) { +function validateBoolean(value, name, { allowUndefined = false } = {}) { + if (allowUndefined && value === undefined) + return; if (typeof value !== 'boolean') throw new ERR_INVALID_ARG_TYPE(name, 'boolean', value); } @@ -163,13 +178,16 @@ function validateSignalName(signal, name = 'signal') { } } -const validateBuffer = hideStackFrames((buffer, name = 'buffer') => { - if (!isArrayBufferView(buffer)) { - throw new ERR_INVALID_ARG_TYPE(name, - ['Buffer', 'TypedArray', 'DataView'], - buffer); - } -}); +const validateBuffer = hideStackFrames( + (buffer, name = 'buffer', { allowUndefined = false } = {}) => { + if (allowUndefined && buffer === undefined) + return; + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE(name, + ['Buffer', 'TypedArray', 'DataView'], + buffer); + } + }); function validateEncoding(data, encoding) { const normalizedEncoding = normalizeEncoding(encoding); diff --git a/lib/quic.js b/lib/quic.js new file mode 100644 index 00000000000000..ca96385abf026c --- /dev/null +++ b/lib/quic.js @@ -0,0 +1,12 @@ +'use strict'; + +const { + createSocket +} = require('internal/quic/core'); + +module.exports = { createSocket }; + +process.emitWarning( + 'QUIC protocol support is experimental and not yet ' + + 'supported for production use', + 'ExperimentalWarning'); diff --git a/node.gyp b/node.gyp index 170501477a62d1..45cd0a475635bd 100644 --- a/node.gyp +++ b/node.gyp @@ -1,5 +1,6 @@ { 'variables': { + 'experimental_quic': 'false', 'v8_use_siphash%': 0, 'v8_trace_maps%': 0, 'v8_enable_pointer_compression%': 0, @@ -19,6 +20,8 @@ 'node_shared_cares%': 'false', 'node_shared_libuv%': 'false', 'node_shared_nghttp2%': 'false', + 'node_shared_ngtcp2%': 'false', + 'node_shared_nghttp3%': 'false', 'node_use_openssl%': 'true', 'node_shared_openssl%': 'false', 'node_v8_options%': '', @@ -70,6 +73,7 @@ 'lib/process.js', 'lib/punycode.js', 'lib/querystring.js', + 'lib/quic.js', 'lib/readline.js', 'lib/repl.js', 'lib/stream.js', @@ -185,6 +189,8 @@ 'lib/internal/process/task_queues.js', 'lib/internal/querystring.js', 'lib/internal/readline/utils.js', + 'lib/internal/quic/core.js', + 'lib/internal/quic/util.js', 'lib/internal/repl.js', 'lib/internal/repl/await.js', 'lib/internal/repl/history.js', @@ -567,6 +573,7 @@ 'src/js_native_api_v8.h', 'src/js_native_api_v8_internals.h', 'src/js_stream.cc', + 'src/js_udp_wrap.cc', 'src/module_wrap.cc', 'src/node.cc', 'src/node_api.cc', @@ -657,6 +664,8 @@ 'src/node_api.h', 'src/node_api_types.h', 'src/node_binding.h', + 'src/node_bob.h', + 'src/node_bob-inl.h', 'src/node_buffer.h', 'src/node_constants.h', 'src/node_context_data.h', @@ -888,6 +897,36 @@ 'node_target_type=="executable"', { 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], }], + [ + # We can only use QUIC if using our modified, static linked + # OpenSSL because we have patched in the QUIC support. + 'node_use_openssl=="true" and node_shared_openssl=="false" and experimental_quic==1', { + 'defines': ['NODE_EXPERIMENTAL_QUIC=1'], + 'sources': [ + 'src/quic/node_quic_buffer.h', + 'src/quic/node_quic_buffer-inl.h', + 'src/quic/node_quic_crypto.h', + 'src/quic/node_quic_session.h', + 'src/quic/node_quic_session-inl.h', + 'src/quic/node_quic_socket.h', + 'src/quic/node_quic_socket-inl.h', + 'src/quic/node_quic_stream.h', + 'src/quic/node_quic_stream-inl.h', + 'src/quic/node_quic_util.h', + 'src/quic/node_quic_util-inl.h', + 'src/quic/node_quic_state.h', + 'src/quic/node_quic_default_application.h', + 'src/quic/node_quic_http3_application.h', + 'src/quic/node_quic_buffer.cc', + 'src/quic/node_quic_crypto.cc', + 'src/quic/node_quic_session.cc', + 'src/quic/node_quic_socket.cc', + 'src/quic/node_quic_stream.cc', + 'src/quic/node_quic.cc', + 'src/quic/node_quic_default_application.cc', + 'src/quic/node_quic_http3_application.cc' + ] + }], [ 'use_openssl_def==1', { # TODO(bnoordhuis) Make all platforms export the same list of symbols. # Teach mkssldef.py to generate linker maps that UNIX linkers understand. @@ -1168,6 +1207,16 @@ 'HAVE_OPENSSL=1', ], }], + [ 'node_use_openssl=="true" and experimental_quic==1', { + 'defines': [ + 'NODE_EXPERIMENTAL_QUIC=1', + ], + 'sources': [ + 'test/cctest/test_quic_buffer.cc', + 'test/cctest/test_quic_cid.cc', + 'test/cctest/test_quic_verifyhostnameidentity.cc' + ] + }], ['v8_enable_inspector==1', { 'sources': [ 'test/cctest/test_inspector_socket.cc', diff --git a/node.gypi b/node.gypi index 87bcb37fc7f3d5..d0b73a739c83ff 100644 --- a/node.gypi +++ b/node.gypi @@ -187,6 +187,24 @@ 'dependencies': [ 'deps/nghttp2/nghttp2.gyp:nghttp2' ], }], + [ + 'node_use_openssl=="true" and experimental_quic==1', { + 'conditions': [ + [ + 'node_shared_ngtcp2=="false"', { + 'dependencies': [ + 'deps/ngtcp2/ngtcp2.gyp:ngtcp2', + ]} + ], + [ + 'node_shared_nghttp3=="false"', { + 'dependencies': [ + 'deps/nghttp3/nghttp3.gyp:nghttp3' + ]} + ] + ]} + ], + [ 'node_shared_brotli=="false"', { 'dependencies': [ 'deps/brotli/brotli.gyp:brotli' ], }], diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h index 281c8fed581645..69cf2404c44632 100644 --- a/src/aliased_buffer.h +++ b/src/aliased_buffer.h @@ -32,6 +32,31 @@ template ::value>> class AliasedBufferBase { public: + /** + * Create an AliasedBufferBase over an existing buffer + */ + AliasedBufferBase( + v8::Isolate* isolate, + const size_t count, + NativeT* buffer) : + isolate_(isolate), + count_(count), + byte_offset_(0), + buffer_(buffer) { + CHECK_GT(count, 0); + const v8::HandleScope handle_scope(isolate_); + const size_t size_in_bytes = + MultiplyWithOverflowCheck(sizeof(NativeT), count); + v8::Local ab = + v8::ArrayBuffer::New( + isolate_, + buffer, + size_in_bytes); + + v8::Local js_array = V8T::New(ab, byte_offset_, count); + js_array_ = v8::Global(isolate, js_array); + } + AliasedBufferBase(v8::Isolate* isolate, const size_t count) : isolate_(isolate), count_(count), byte_offset_(0) { CHECK_GT(count, 0); diff --git a/src/async_wrap.h b/src/async_wrap.h index 33bed41a6439c5..143f8910f18100 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -51,6 +51,7 @@ namespace node { V(HTTPINCOMINGMESSAGE) \ V(HTTPCLIENTREQUEST) \ V(JSSTREAM) \ + V(JSUDPWRAP) \ V(MESSAGEPORT) \ V(PIPECONNECTWRAP) \ V(PIPESERVERWRAP) \ @@ -58,6 +59,11 @@ namespace node { V(PROCESSWRAP) \ V(PROMISE) \ V(QUERYWRAP) \ + V(QUICCLIENTSESSION) \ + V(QUICSERVERSESSION) \ + V(QUICSENDWRAP) \ + V(QUICSOCKET) \ + V(QUICSTREAM) \ V(SHUTDOWNWRAP) \ V(SIGNALWRAP) \ V(STATWATCHER) \ diff --git a/src/debug_utils.h b/src/debug_utils.h index b654159ac2a24e..528778c0a89182 100644 --- a/src/debug_utils.h +++ b/src/debug_utils.h @@ -43,6 +43,7 @@ void FWrite(FILE* file, const std::string& str); V(INSPECTOR_SERVER) \ V(INSPECTOR_PROFILER) \ V(CODE_CACHE) \ + V(NGTCP2_DEBUG) \ V(WASI) enum class DebugCategory { diff --git a/src/env-inl.h b/src/env-inl.h index 69b7316e405d41..c760134af14865 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -609,6 +609,17 @@ inline void Environment::set_http2_state( http2_state_ = std::move(buffer); } +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +inline QuicState* Environment::quic_state() const { + return quic_state_.get(); +} + +inline void Environment::set_quic_state(std::unique_ptr buffer) { + CHECK(!quic_state_); // Should be set only once. + quic_state_ = std::move(buffer); +} +#endif + inline AliasedFloat64Array* Environment::fs_stats_field_array() { return &fs_stats_field_array_; } @@ -1001,6 +1012,10 @@ inline void AllocatedBuffer::Resize(size_t len) { buffer_ = uv_buf_init(new_data, len); } +inline bool AllocatedBuffer::empty() { + return env_ == nullptr; +} + inline uv_buf_t AllocatedBuffer::release() { uv_buf_t ret = buffer_; buffer_ = uv_buf_init(nullptr, 0); diff --git a/src/env.h b/src/env.h index 13af1874050a95..fa6ffa3a3fcdc3 100644 --- a/src/env.h +++ b/src/env.h @@ -30,6 +30,9 @@ #include "inspector_profiler.h" #endif #include "debug_utils.h" +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +#include "quic/node_quic_state.h" +#endif #include "handle_wrap.h" #include "node.h" #include "node_binding.h" @@ -171,6 +174,7 @@ constexpr size_t kFsStatsBufferLength = // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. #define PER_ISOLATE_STRING_PROPERTIES(V) \ + V(ack_string, "ack") \ V(address_string, "address") \ V(aliases_string, "aliases") \ V(args_string, "args") \ @@ -332,6 +336,8 @@ constexpr size_t kFsStatsBufferLength = V(psk_string, "psk") \ V(pubkey_string, "pubkey") \ V(query_string, "query") \ + V(quic_alpn_string, "h3-27") \ + V(rate_string, "rate") \ V(raw_string, "raw") \ V(read_host_object_string, "_readHostObject") \ V(readable_string, "readable") \ @@ -359,6 +365,8 @@ constexpr size_t kFsStatsBufferLength = V(stack_string, "stack") \ V(standard_name_string, "standardName") \ V(start_time_string, "startTime") \ + V(state_string, "state") \ + V(stats_string, "stats") \ V(status_string, "status") \ V(stdio_string, "stdio") \ V(subject_string, "subject") \ @@ -398,6 +406,16 @@ constexpr size_t kFsStatsBufferLength = V(x_forwarded_string, "x-forwarded-for") \ V(zero_return_string, "ZERO_RETURN") +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) \ + V(quicclientsession_instance_template, v8::ObjectTemplate) \ + V(quicserversession_instance_template, v8::ObjectTemplate) \ + V(quicserverstream_instance_template, v8::ObjectTemplate) \ + V(quicsocketsendwrap_instance_template, v8::ObjectTemplate) +#else +# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) +#endif + #define ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) \ V(as_callback_data_template, v8::FunctionTemplate) \ V(async_wrap_ctor_template, v8::FunctionTemplate) \ @@ -425,7 +443,38 @@ constexpr size_t kFsStatsBufferLength = V(tcp_constructor_template, v8::FunctionTemplate) \ V(tty_constructor_template, v8::FunctionTemplate) \ V(write_wrap_template, v8::ObjectTemplate) \ - V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) + V(worker_heap_snapshot_taker_template, v8::ObjectTemplate) \ + QUIC_ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V) + +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) +# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ + V(quic_on_socket_close_function, v8::Function) \ + V(quic_on_socket_error_function, v8::Function) \ + V(quic_on_socket_server_busy_function, v8::Function) \ + V(quic_on_session_cert_function, v8::Function) \ + V(quic_on_session_client_hello_function, v8::Function) \ + V(quic_on_session_close_function, v8::Function) \ + V(quic_on_session_destroyed_function, v8::Function) \ + V(quic_on_session_error_function, v8::Function) \ + V(quic_on_session_handshake_function, v8::Function) \ + V(quic_on_session_keylog_function, v8::Function) \ + V(quic_on_session_path_validation_function, v8::Function) \ + V(quic_on_session_use_preferred_address_function, v8::Function) \ + V(quic_on_session_qlog_function, v8::Function) \ + V(quic_on_session_ready_function, v8::Function) \ + V(quic_on_session_silent_close_function, v8::Function) \ + V(quic_on_session_status_function, v8::Function) \ + V(quic_on_session_ticket_function, v8::Function) \ + V(quic_on_session_version_negotiation_function, v8::Function) \ + V(quic_on_stream_close_function, v8::Function) \ + V(quic_on_stream_error_function, v8::Function) \ + V(quic_on_stream_ready_function, v8::Function) \ + V(quic_on_stream_reset_function, v8::Function) \ + V(quic_on_stream_headers_function, v8::Function) \ + V(quic_on_stream_blocked_function, v8::Function) +#else +# define QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) +#endif #define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ V(as_callback_data, v8::Object) \ @@ -473,7 +522,8 @@ constexpr size_t kFsStatsBufferLength = V(tls_wrap_constructor_function, v8::Function) \ V(trace_category_state_function, v8::Function) \ V(udp_constructor_function, v8::Function) \ - V(url_constructor_function, v8::Function) + V(url_constructor_function, v8::Function) \ + QUIC_ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) class Environment; @@ -562,6 +612,7 @@ struct AllocatedBuffer { inline ~AllocatedBuffer(); inline void Resize(size_t len); + inline bool empty(); inline uv_buf_t release(); inline char* data(); inline const char* data() const; @@ -1017,6 +1068,11 @@ class Environment : public MemoryRetainer { EnabledDebugList* enabled_debug_list() { return &enabled_debug_list_; } +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) + inline QuicState* quic_state() const; + inline void set_quic_state(std::unique_ptr state); +#endif + inline AliasedFloat64Array* fs_stats_field_array(); inline AliasedBigUint64Array* fs_stats_field_bigint_array(); @@ -1374,6 +1430,9 @@ class Environment : public MemoryRetainer { char* http_parser_buffer_ = nullptr; bool http_parser_buffer_in_use_ = false; std::unique_ptr http2_state_; +#if HAVE_OPENSSL && defined(NODE_EXPERIMENTAL_QUIC) + std::unique_ptr quic_state_; +#endif EnabledDebugList enabled_debug_list_; AliasedFloat64Array fs_stats_field_array_; diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 4e039d5dbf2d37..b29cd132eed4e3 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -99,6 +99,10 @@ void HandleWrap::MarkAsUninitialized() { state_ = kClosed; } +void HandleWrap::MarkAsClosing() { + state_ = kClosing; +} + HandleWrap::HandleWrap(Environment* env, Local object, diff --git a/src/handle_wrap.h b/src/handle_wrap.h index a555da9479de93..0e2b4a715f107a 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -88,15 +88,18 @@ class HandleWrap : public AsyncWrap { void MarkAsInitialized(); void MarkAsUninitialized(); + void MarkAsClosing(); + bool IsInitialized() { return state_ == kInitialized; } inline bool IsHandleClosing() const { return state_ == kClosing || state_ == kClosed; } + static void OnClose(uv_handle_t* handle); + private: friend class Environment; friend void GetActiveHandles(const v8::FunctionCallbackInfo&); - static void OnClose(uv_handle_t* handle); // handle_wrap_queue_ needs to be at a fixed offset from the start of the // class because it is used by src/node_postmortem_metadata.cc to calculate diff --git a/src/js_udp_wrap.cc b/src/js_udp_wrap.cc new file mode 100644 index 00000000000000..d956a4009e9463 --- /dev/null +++ b/src/js_udp_wrap.cc @@ -0,0 +1,219 @@ +#include "udp_wrap.h" +#include "async_wrap-inl.h" +#include "node_errors.h" +#include "node_sockaddr-inl.h" + +namespace node { + +using errors::TryCatchScope; +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Int32; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Value; + +class JSUDPWrap final : public UDPWrapBase, public AsyncWrap { + public: + JSUDPWrap(Environment* env, Local obj); + + int RecvStart() override; + int RecvStop() override; + ssize_t Send(uv_buf_t* bufs, + size_t nbufs, + const sockaddr* addr) override; + SocketAddress GetPeerName() override; + SocketAddress GetSockName() override; + AsyncWrap* GetAsyncWrap() override { return this; } + + static void New(const FunctionCallbackInfo& args); + static void EmitReceived(const FunctionCallbackInfo& args); + static void OnSendDone(const FunctionCallbackInfo& args); + static void OnAfterBind(const FunctionCallbackInfo& args); + + static void Initialize(Local target, + Local unused, + Local context, + void* priv); + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(JSUDPWrap) + SET_SELF_SIZE(JSUDPWrap) +}; + +JSUDPWrap::JSUDPWrap(Environment* env, Local obj) + : AsyncWrap(env, obj, PROVIDER_JSUDPWRAP) { + MakeWeak(); + + obj->SetAlignedPointerInInternalField( + kUDPWrapBaseField, static_cast(this)); +} + +int JSUDPWrap::RecvStart() { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int32_t value_int = UV_EPROTO; + if (!MakeCallback(env()->onreadstart_string(), 0, nullptr).ToLocal(&value) || + !value->Int32Value(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +int JSUDPWrap::RecvStop() { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int32_t value_int = UV_EPROTO; + if (!MakeCallback(env()->onreadstop_string(), 0, nullptr).ToLocal(&value) || + !value->Int32Value(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +ssize_t JSUDPWrap::Send(uv_buf_t* bufs, + size_t nbufs, + const sockaddr* addr) { + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + TryCatchScope try_catch(env()); + Local value; + int64_t value_int = UV_EPROTO; + size_t total_len = 0; + + MaybeStackBuffer, 16> buffers(nbufs); + for (size_t i = 0; i < nbufs; i++) { + buffers[i] = Buffer::Copy(env(), bufs[i].base, bufs[i].len) + .ToLocalChecked(); + total_len += bufs[i].len; + } + + Local args[] = { + listener()->CreateSendWrap(total_len)->object(), + Array::New(env()->isolate(), buffers.out(), nbufs), + AddressToJS(env(), addr) + }; + + if (!MakeCallback(env()->onwrite_string(), arraysize(args), args) + .ToLocal(&value) || + !value->IntegerValue(env()->context()).To(&value_int)) { + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(env()->isolate(), try_catch); + } + return value_int; +} + +SocketAddress JSUDPWrap::GetPeerName() { + // TODO(jasnell): Maybe turn this into a real JS-based method. + SocketAddress ret; + CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); + return ret; +} + +SocketAddress JSUDPWrap::GetSockName() { + // TODO(jasnell): Maybe turn this into a real JS-based method. + SocketAddress ret; + CHECK(SocketAddress::New(AF_INET, "127.0.0.1", 1337, &ret)); + return ret; +} + +void JSUDPWrap::New(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + new JSUDPWrap(env, args.Holder()); +} + +void JSUDPWrap::EmitReceived(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + Environment* env = wrap->env(); + + ArrayBufferViewContents buffer(args[0]); + const char* data = buffer.data(); + int len = buffer.length(); + + CHECK(args[1]->IsInt32()); // family + CHECK(args[2]->IsString()); // address + CHECK(args[3]->IsInt32()); // port + CHECK(args[4]->IsInt32()); // flags + int family = args[1].As()->Value() == 4 ? AF_INET : AF_INET6; + Utf8Value address(env->isolate(), args[2]); + int port = args[3].As()->Value(); + int flags = args[3].As()->Value(); + + sockaddr_storage addr; + CHECK_EQ(sockaddr_for_family(family, *address, port, &addr), 0); + + // Repeatedly ask the stream's owner for memory, copy the data that we + // just read from JS into those buffers and emit them as reads. + while (len != 0) { + uv_buf_t buf = wrap->listener()->OnAlloc(len); + ssize_t avail = len; + if (static_cast(buf.len) < avail) + avail = buf.len; + + memcpy(buf.base, data, avail); + data += avail; + len -= avail; + wrap->listener()->OnRecv( + avail, buf, reinterpret_cast(&addr), flags); + } +} + +void JSUDPWrap::OnSendDone(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + + CHECK(args[0]->IsObject()); + CHECK(args[1]->IsInt32()); + ReqWrap* req_wrap; + ASSIGN_OR_RETURN_UNWRAP(&req_wrap, args[0].As()); + int status = args[1].As()->Value(); + + wrap->listener()->OnSendDone(req_wrap, status); +} + +void JSUDPWrap::OnAfterBind(const FunctionCallbackInfo& args) { + JSUDPWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + + wrap->listener()->OnAfterBind(); +} + +void JSUDPWrap::Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + Local t = env->NewFunctionTemplate(New); + Local js_udp_wrap_string = + FIXED_ONE_BYTE_STRING(env->isolate(), "JSUDPWrap"); + t->SetClassName(js_udp_wrap_string); + t->InstanceTemplate() + ->SetInternalFieldCount(UDPWrapBase::kUDPWrapBaseField + 1); + t->Inherit(AsyncWrap::GetConstructorTemplate(env)); + + UDPWrapBase::AddMethods(env, t); + env->SetProtoMethod(t, "emitReceived", EmitReceived); + env->SetProtoMethod(t, "onSendDone", OnSendDone); + env->SetProtoMethod(t, "onAfterBind", OnAfterBind); + + target->Set(env->context(), + js_udp_wrap_string, + t->GetFunction(context).ToLocalChecked()).Check(); +} + + +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(js_udp_wrap, node::JSUDPWrap::Initialize) diff --git a/src/node_binding.cc b/src/node_binding.cc index 37c64000650085..fdb2e52ac34635 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -7,8 +7,14 @@ #if HAVE_OPENSSL #define NODE_BUILTIN_OPENSSL_MODULES(V) V(crypto) V(tls_wrap) +#if defined(NODE_EXPERIMENTAL_QUIC) +#define NODE_BUILTIN_QUIC_MODULES(V) V(quic) +#else +#define NODE_BUILTIN_QUIC_MODULES(V) +#endif #else #define NODE_BUILTIN_OPENSSL_MODULES(V) +#define NODE_BUILTIN_QUIC_MODULES(V) #endif #if NODE_HAVE_I18N_SUPPORT @@ -58,6 +64,7 @@ V(http_parser) \ V(inspector) \ V(js_stream) \ + V(js_udp_wrap) \ V(messaging) \ V(module_wrap) \ V(native_module) \ @@ -93,6 +100,7 @@ #define NODE_BUILTIN_MODULES(V) \ NODE_BUILTIN_STANDARD_MODULES(V) \ NODE_BUILTIN_OPENSSL_MODULES(V) \ + NODE_BUILTIN_QUIC_MODULES(V) \ NODE_BUILTIN_ICU_MODULES(V) \ NODE_BUILTIN_REPORT_MODULES(V) \ NODE_BUILTIN_PROFILER_MODULES(V) \ diff --git a/src/node_bob-inl.h b/src/node_bob-inl.h new file mode 100644 index 00000000000000..578e45e9ff6149 --- /dev/null +++ b/src/node_bob-inl.h @@ -0,0 +1,37 @@ +#ifndef SRC_NODE_BOB_INL_H_ +#define SRC_NODE_BOB_INL_H_ + +#include "node_bob.h" + +#include + +namespace node { +namespace bob { + +template +int SourceImpl::Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint) { + + int status; + if (eos_) { + status = bob::Status::STATUS_EOS; + std::move(next)(status, nullptr, 0, [](size_t len) {}); + return status; + } + + status = DoPull(std::move(next), options, data, count, max_count_hint); + + if (status == bob::Status::STATUS_END) + eos_ = true; + + return status; +} + +} // namespace bob +} // namespace node + +#endif // SRC_NODE_BOB_INL_H_ diff --git a/src/node_bob.h b/src/node_bob.h new file mode 100644 index 00000000000000..74571608f3ae56 --- /dev/null +++ b/src/node_bob.h @@ -0,0 +1,111 @@ +#ifndef SRC_NODE_BOB_H_ +#define SRC_NODE_BOB_H_ + +#include + +namespace node { +namespace bob { + +constexpr size_t kMaxCountHint = 16; + +// Negative status codes indicate error conditions. +enum Status : int { + // Indicates that an attempt was made to pull after end. + STATUS_EOS = -1, + + // Indicates the end of the stream. No additional + // data will be available and the consumer should stop + // pulling. + STATUS_END = 0, + + // Indicates that there is additional data available + // and the consumer may continue to pull. + STATUS_CONTINUE = 1, + + // Indicates that there is no additional data available + // but the stream has not ended. The consumer should not + // continue to pull but may resume pulling later when + // data is available. + STATUS_BLOCK = 2, + + // Indicates that there is no additional data available + // but the stream has not ended and the source will call + // next again later when data is available. STATUS_WAIT + // must not be used with the OPTIONS_SYNC option. + STATUS_WAIT = 3, +}; + +enum Options : int { + OPTIONS_NONE = 0, + + // Indicates that the consumer is requesting the end + // of the stream. + OPTIONS_END = 1, + + // Indicates that the consumer requires the source to + // invoke Next synchronously. If the source is + // unable to provide data immediately but the + // stream has not yet ended, it should call Next + // using STATUS_BLOCK. When not set, the source + // may call Next asynchronously. + OPTIONS_SYNC = 2 +}; + +// There are Sources and there are Consumers. +// +// Consumers get data by calling Source::Pull, +// optionally passing along a status and allocated +// buffer space for the source to fill, and a Next +// function the Source will call when data is +// available. +// +// The source calls Next to deliver the data. It can +// choose to either use the allocated buffer space +// provided by the consumer or it can allocate its own +// buffers and push those instead. If it allocates +// its own, it can send a Done function that the +// Sink will call when it is done consuming the data. +using Done = std::function; +template +using Next = std::function; + +template +class Source { + public: + virtual int Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint = kMaxCountHint) = 0; +}; + + +template +class SourceImpl : public Source { + public: + int Pull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint = kMaxCountHint) override; + + bool is_eos() const { return eos_; } + + protected: + virtual int DoPull( + Next next, + int options, + T* data, + size_t count, + size_t max_count_hint) = 0; + + private: + bool eos_ = false; +}; + +} // namespace bob +} // namespace node + +#endif // SRC_NODE_BOB_H_ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index fdaf91acdc0a1c..0e3f840088ce8f 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -621,6 +621,17 @@ void SecureContext::Init(const FunctionCallbackInfo& args) { min_version = TLS1_2_VERSION; max_version = TLS1_2_VERSION; method = TLS_client_method(); + } else if (strcmp(*sslmethod, "TLSv1_3_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + } else if (strcmp(*sslmethod, "TLSv1_3_server_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + method = TLS_server_method(); + } else if (strcmp(*sslmethod, "TLSv1_3_client_method") == 0) { + min_version = TLS1_3_VERSION; + max_version = TLS1_3_VERSION; + method = TLS_client_method(); } else { const std::string msg("Unknown method: "); THROW_ERR_TLS_INVALID_PROTOCOL_METHOD(env, (msg + * sslmethod).c_str()); @@ -2482,7 +2493,7 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { // Store the SNI context for later use. w->sni_context_ = BaseObjectPtr(sc); - if (UseSNIContext(w->ssl_, sc) && !w->SetCACerts(sc)) { + if (UseSNIContext(w->ssl_, w->sni_context_) && !w->SetCACerts(sc)) { // Not clear why sometimes we throw error, and sometimes we call // onerror(). Both cause .destroy(), but onerror does a bit more. unsigned long err = ERR_get_error(); // NOLINT(runtime/int) diff --git a/src/node_crypto_common.cc b/src/node_crypto_common.cc index 197bc5cd5913a4..d890ca5dd998ee 100644 --- a/src/node_crypto_common.cc +++ b/src/node_crypto_common.cc @@ -1,4 +1,5 @@ #include "env-inl.h" +#include "base_object-inl.h" #include "node_buffer.h" #include "node_crypto.h" #include "node_crypto_common.h" @@ -223,7 +224,7 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) return err; } -int UseSNIContext(const SSLPointer& ssl, SecureContext* context) { +int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context) { SSL_CTX* ctx = context->ctx_.get(); X509* x509 = SSL_CTX_get0_certificate(ctx); EVP_PKEY* pkey = SSL_CTX_get0_privatekey(ctx); @@ -329,11 +330,15 @@ const char* X509ErrorCode(long err) { // NOLINT(runtime/int) } MaybeLocal GetValidationErrorReason(Environment* env, int err) { + if (err == 0) + return Undefined(env->isolate()); const char* reason = X509_verify_cert_error_string(err); return OneByteString(env->isolate(), reason); } MaybeLocal GetValidationErrorCode(Environment* env, int err) { + if (err == 0) + return Undefined(env->isolate()); return OneByteString(env->isolate(), X509ErrorCode(err)); } diff --git a/src/node_crypto_common.h b/src/node_crypto_common.h index e42e249ef2ba2e..b6017d5300a104 100644 --- a/src/node_crypto_common.h +++ b/src/node_crypto_common.h @@ -72,7 +72,7 @@ long VerifyPeerCertificate( // NOLINT(runtime/int) const SSLPointer& ssl, long def = X509_V_ERR_UNSPECIFIED); // NOLINT(runtime/int) -int UseSNIContext(const SSLPointer& ssl, SecureContext* context); +int UseSNIContext(const SSLPointer& ssl, BaseObjectPtr context); const char* GetClientHelloALPN(const SSLPointer& ssl); diff --git a/src/node_errors.h b/src/node_errors.h index 960cb725323e92..9792d293711505 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -63,6 +63,8 @@ void OnFatalError(const char* location, const char* message); V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \ V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, Error) \ V(ERR_VM_MODULE_CACHED_DATA_REJECTED, Error) \ + V(ERR_QUIC_CANNOT_SET_GROUPS, Error) \ + V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, Error) #define V(code, type) \ inline v8::Local code(v8::Isolate* isolate, \ @@ -110,7 +112,9 @@ void OnFatalError(const char* location, const char* message); "Script execution was interrupted by `SIGINT`") \ V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \ "Cannot serialize externalized SharedArrayBuffer") \ - V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") + V(ERR_TLS_PSK_SET_IDENTIY_HINT_FAILED, "Failed to set PSK identity hint") \ + V(ERR_QUIC_CANNOT_SET_GROUPS, "Cannot set groups") \ + V(ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT, "Failure setting SNI context") #define V(code, message) \ inline v8::Local code(v8::Isolate* isolate) { \ diff --git a/src/node_http_common-inl.h b/src/node_http_common-inl.h index d63cdf79a4b468..dccc78bdf3b099 100644 --- a/src/node_http_common-inl.h +++ b/src/node_http_common-inl.h @@ -76,6 +76,14 @@ size_t GetServerMaxHeaderPairs(size_t max_header_pairs) { return std::max(max_header_pairs, min_header_pairs); } +template +std::string NgHeaderImpl::ToString() const { + std::string ret = name(); + ret += " = "; + ret += value(); + return ret; +} + template bool NgHeader::IsZeroLength( NgHeader::rcbuf_t* name, @@ -121,6 +129,12 @@ NgHeader::NgHeader( value_.reset(value); } +template +void NgHeader::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("name", name_); + tracker->TrackField("value", value_); +} + template NgHeader::NgHeader(NgHeader&& other) noexcept : name_(std::move(other.name_)), diff --git a/src/node_http_common.h b/src/node_http_common.h index 41b5f419d94338..5c6c1f4de1e5c5 100644 --- a/src/node_http_common.h +++ b/src/node_http_common.h @@ -453,6 +453,16 @@ class NgRcBufPointer : public MemoryRetainer { bool internalizable_ = false; }; +template +struct NgHeaderImpl : public MemoryRetainer { + virtual v8::MaybeLocal GetName(allocator_t* allocator) const = 0; + virtual v8::MaybeLocal GetValue(allocator_t* allocator) const = 0; + virtual std::string name() const = 0; + virtual std::string value() const = 0; + virtual size_t length() const = 0; + virtual std::string ToString() const; +}; + // The ng libraries use nearly identical structs to represent // received http headers. The NgHeader class wraps those in a // consistent way and allows converting the name and value to @@ -461,7 +471,7 @@ class NgRcBufPointer : public MemoryRetainer { // memory tracking, and implementation of static utility functions. // See Http2HeaderTraits in node_http2.h for an example. template -class NgHeader : public MemoryRetainer { +class NgHeader : public NgHeaderImpl { public: typedef typename T::rcbufferpointer_t rcbufferpointer_t; typedef typename T::rcbufferpointer_t::rcbuf_t rcbuf_t; @@ -487,28 +497,19 @@ class NgHeader : public MemoryRetainer { // object to the v8 string. Once the v8 string is garbage collected, // the reference counter will be decremented. - inline v8::MaybeLocal GetName(allocator_t* allocator) const; - inline v8::MaybeLocal GetValue(allocator_t* allocator) const; + inline v8::MaybeLocal GetName( + allocator_t* allocator) const override; + inline v8::MaybeLocal GetValue( + allocator_t* allocator) const override; + inline std::string name() const override; + inline std::string value() const override; + inline size_t length() const override; - inline std::string name() const; - inline std::string value() const; - inline size_t length() const; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("name", name_); - tracker->TrackField("value", value_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(NgHeader) SET_SELF_SIZE(NgHeader) - std::string ToString() const { - std::string ret = name(); - ret += " = "; - ret += value(); - return ret; - } - private: Environment* env_; rcbufferpointer_t name_; diff --git a/src/node_mem.h b/src/node_mem.h index 0d3388ad4766bb..f8cdc20848f82e 100644 --- a/src/node_mem.h +++ b/src/node_mem.h @@ -13,8 +13,12 @@ namespace mem { // use different struct names. To allow for code re-use, // the NgLibMemoryManager template class can be used for both. +struct NgLibMemoryManagerBase { + virtual void StopTrackingMemory(void* ptr) = 0; +}; + template -class NgLibMemoryManager { +class NgLibMemoryManager : public NgLibMemoryManagerBase { public: // Class needs to provide these methods: // void CheckAllocatedSize(size_t previous_size) const; @@ -24,7 +28,7 @@ class NgLibMemoryManager { AllocatorStructName MakeAllocator(); - void StopTrackingMemory(void* ptr); + void StopTrackingMemory(void* ptr) override; private: static void* ReallocImpl(void* ptr, size_t size, void* user_data); diff --git a/src/node_metadata.cc b/src/node_metadata.cc index 8d0a725de45421..4841163f2a8673 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -11,6 +11,10 @@ #if HAVE_OPENSSL #include +#if defined(NODE_EXPERIMENTAL_QUIC) +#include +#include +#endif #endif // HAVE_OPENSSL #ifdef NODE_HAVE_I18N_SUPPORT @@ -89,6 +93,10 @@ Metadata::Versions::Versions() { #if HAVE_OPENSSL openssl = GetOpenSSLVersion(); +#if defined(NODE_EXPERIMENTAL_QUIC) + ngtcp2 = NGTCP2_VERSION; + nghttp3 = NGHTTP3_VERSION; +#endif #endif #ifdef NODE_HAVE_I18N_SUPPORT diff --git a/src/node_metadata.h b/src/node_metadata.h index bf7e5d3ff4e811..7e9f522d21a9e5 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -34,8 +34,14 @@ namespace node { #if HAVE_OPENSSL #define NODE_VERSIONS_KEY_CRYPTO(V) V(openssl) +#if defined(NODE_EXPERIMENTAL_QUIC) +#define NODE_VERSIONS_KEY_QUIC(V) V(ngtcp2) V(nghttp3) +#else +#define NODE_VERSIONS_KEY_QUIC(V) +#endif #else #define NODE_VERSIONS_KEY_CRYPTO(V) +#define NODE_VERSIONS_KEY_QUIC(V) #endif #ifdef NODE_HAVE_I18N_SUPPORT @@ -51,6 +57,7 @@ namespace node { #define NODE_VERSIONS_KEYS(V) \ NODE_VERSIONS_KEYS_BASE(V) \ NODE_VERSIONS_KEY_CRYPTO(V) \ + NODE_VERSIONS_KEY_QUIC(V) \ NODE_VERSIONS_KEY_INTL(V) class Metadata { diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 680c585d0fb3df..4ffc666600738d 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -87,11 +87,18 @@ void NativeModuleLoader::InitializeModuleCategories() { "crypto", "https", "http2", +#if !defined(NODE_EXPERIMENTAL_QUIC) + "quic", +#endif "tls", "_tls_common", "_tls_wrap", "internal/http2/core", "internal/http2/compat", +#if !defined(NODE_EXPERIMENTAL_QUIC) + "internal/quic/core", + "internal/quic/util", +#endif "internal/policy/manifest", "internal/process/policy", "internal/streams/lazy_transform", diff --git a/src/quic/node_quic.cc b/src/quic/node_quic.cc new file mode 100644 index 00000000000000..c868eaeaa8bc3a --- /dev/null +++ b/src/quic/node_quic.cc @@ -0,0 +1,244 @@ +#include "debug_utils-inl.h" +#include "node.h" +#include "env-inl.h" +#include "node_crypto.h" // SecureContext +#include "node_crypto_common.h" +#include "node_errors.h" +#include "node_process.h" +#include "node_quic_crypto.h" +#include "node_quic_session-inl.h" +#include "node_quic_socket-inl.h" +#include "node_quic_stream-inl.h" +#include "node_quic_state.h" +#include "node_quic_util-inl.h" +#include "node_sockaddr-inl.h" + +#include +#include + +namespace node { + +using crypto::SecureContext; +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::Value; + +namespace quic { + +namespace { +// Register the JavaScript callbacks the internal binding will use to report +// status and updates. This is called only once when the quic module is loaded. +void QuicSetCallbacks(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsObject()); + Local obj = args[0].As(); + +#define SETFUNCTION(name, callback) \ + do { \ + Local fn; \ + CHECK(obj->Get(env->context(), \ + FIXED_ONE_BYTE_STRING(env->isolate(), name)).ToLocal(&fn));\ + CHECK(fn->IsFunction()); \ + env->set_quic_on_##callback##_function(fn.As()); \ + } while (0) + + SETFUNCTION("onSocketClose", socket_close); + SETFUNCTION("onSocketError", socket_error); + SETFUNCTION("onSessionReady", session_ready); + SETFUNCTION("onSessionCert", session_cert); + SETFUNCTION("onSessionClientHello", session_client_hello); + SETFUNCTION("onSessionClose", session_close); + SETFUNCTION("onSessionDestroyed", session_destroyed); + SETFUNCTION("onSessionError", session_error); + SETFUNCTION("onSessionHandshake", session_handshake); + SETFUNCTION("onSessionKeylog", session_keylog); + SETFUNCTION("onSessionUsePreferredAddress", session_use_preferred_address); + SETFUNCTION("onSessionPathValidation", session_path_validation); + SETFUNCTION("onSessionQlog", session_qlog); + SETFUNCTION("onSessionSilentClose", session_silent_close); + SETFUNCTION("onSessionStatus", session_status); + SETFUNCTION("onSessionTicket", session_ticket); + SETFUNCTION("onSessionVersionNegotiation", session_version_negotiation); + SETFUNCTION("onStreamReady", stream_ready); + SETFUNCTION("onStreamClose", stream_close); + SETFUNCTION("onStreamError", stream_error); + SETFUNCTION("onStreamReset", stream_reset); + SETFUNCTION("onSocketServerBusy", socket_server_busy); + SETFUNCTION("onStreamHeaders", stream_headers); + SETFUNCTION("onStreamBlocked", stream_blocked); + +#undef SETFUNCTION +} + +// Sets QUIC specific configuration options for the SecureContext. +// It's entirely likely that there's a better way to do this, but +// for now this works. +template +void QuicInitSecureContext(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsObject()); // Secure Context + CHECK(args[1]->IsString()); // groups + CHECK(args[2]->IsBoolean()); // early data + + SecureContext* sc; + ASSIGN_OR_RETURN_UNWRAP(&sc, args[0].As(), + args.GetReturnValue().Set(UV_EBADF)); + const node::Utf8Value groups(env->isolate(), args[1]); + + bool early_data = args[2]->BooleanValue(env->isolate()); + + InitializeSecureContext( + BaseObjectPtr(sc), + early_data, + side); + + if (!crypto::SetGroups(sc, *groups)) + THROW_ERR_QUIC_CANNOT_SET_GROUPS(env); +} +} // namespace + +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + Isolate* isolate = env->isolate(); + HandleScope scope(isolate); + + HistogramBase::Initialize(env); + + std::unique_ptr state = std::make_unique(isolate); + +#define SET_STATE_TYPEDARRAY(name, field) \ + target->Set(context, \ + FIXED_ONE_BYTE_STRING(isolate, (name)), \ + (field.GetJSArray())).FromJust() + SET_STATE_TYPEDARRAY("sessionConfig", state->quicsessionconfig_buffer); + SET_STATE_TYPEDARRAY("http3Config", state->http3config_buffer); +#undef SET_STATE_TYPEDARRAY + + env->set_quic_state(std::move(state)); + + QuicSocket::Initialize(env, target, context); + QuicEndpoint::Initialize(env, target, context); + QuicSession::Initialize(env, target, context); + QuicStream::Initialize(env, target, context); + + env->SetMethod(target, + "setCallbacks", + QuicSetCallbacks); + env->SetMethod(target, + "initSecureContext", + QuicInitSecureContext); + env->SetMethod(target, + "initSecureContextClient", + QuicInitSecureContext); + + Local constants = Object::New(env->isolate()); + +// TODO(@jasnell): Audit which constants are actually being used in JS +#define QUIC_CONSTANTS(V) \ + V(DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL) \ + V(DEFAULT_RETRYTOKEN_EXPIRATION) \ + V(DEFAULT_MAX_CONNECTIONS) \ + V(DEFAULT_MAX_CONNECTIONS_PER_HOST) \ + V(DEFAULT_MAX_STATELESS_RESETS_PER_HOST) \ + V(IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY) \ + V(IDX_HTTP3_QPACK_BLOCKED_STREAMS) \ + V(IDX_HTTP3_MAX_HEADER_LIST_SIZE) \ + V(IDX_HTTP3_MAX_PUSHES) \ + V(IDX_HTTP3_MAX_HEADER_PAIRS) \ + V(IDX_HTTP3_MAX_HEADER_LENGTH) \ + V(IDX_HTTP3_CONFIG_COUNT) \ + V(IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT) \ + V(IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT) \ + V(IDX_QUIC_SESSION_MAX_DATA) \ + V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL) \ + V(IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE) \ + V(IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI) \ + V(IDX_QUIC_SESSION_MAX_STREAMS_BIDI) \ + V(IDX_QUIC_SESSION_MAX_STREAMS_UNI) \ + V(IDX_QUIC_SESSION_MAX_PACKET_SIZE) \ + V(IDX_QUIC_SESSION_ACK_DELAY_EXPONENT) \ + V(IDX_QUIC_SESSION_DISABLE_MIGRATION) \ + V(IDX_QUIC_SESSION_MAX_ACK_DELAY) \ + V(IDX_QUIC_SESSION_CONFIG_COUNT) \ + V(IDX_QUIC_SESSION_STATE_CERT_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED) \ + V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI) \ + V(IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI) \ + V(IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT) \ + V(IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT) \ + V(IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED) \ + V(IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT) \ + V(MAX_RETRYTOKEN_EXPIRATION) \ + V(MIN_RETRYTOKEN_EXPIRATION) \ + V(NGTCP2_APP_NOERROR) \ + V(NGTCP2_PATH_VALIDATION_RESULT_FAILURE) \ + V(NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) \ + V(QUIC_ERROR_APPLICATION) \ + V(QUIC_ERROR_CRYPTO) \ + V(QUIC_ERROR_SESSION) \ + V(QUIC_PREFERRED_ADDRESS_ACCEPT) \ + V(QUIC_PREFERRED_ADDRESS_IGNORE) \ + V(QUICCLIENTSESSION_OPTION_REQUEST_OCSP) \ + V(QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY) \ + V(QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED) \ + V(QUICSERVERSESSION_OPTION_REQUEST_CERT) \ + V(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS) \ + V(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU) \ + V(QUICSTREAM_HEADER_FLAGS_NONE) \ + V(QUICSTREAM_HEADER_FLAGS_TERMINAL) \ + V(QUICSTREAM_HEADERS_KIND_NONE) \ + V(QUICSTREAM_HEADERS_KIND_INFORMATIONAL) \ + V(QUICSTREAM_HEADERS_KIND_PUSH) \ + V(QUICSTREAM_HEADERS_KIND_INITIAL) \ + V(QUICSTREAM_HEADERS_KIND_TRAILING) \ + V(ERR_FAILED_TO_CREATE_SESSION) \ + V(UV_EBADF) + +#define V(name, _, __) \ + NODE_DEFINE_CONSTANT(constants, IDX_QUIC_SESSION_STATS_##name); + SESSION_STATS(V) +#undef V + +#define V(name, _, __) \ + NODE_DEFINE_CONSTANT(constants, IDX_QUIC_SOCKET_STATS_##name); + SOCKET_STATS(V) +#undef V + +#define V(name, _, __) \ + NODE_DEFINE_CONSTANT(constants, IDX_QUIC_STREAM_STATS_##name); + STREAM_STATS(V) +#undef V + +#define V(name) NODE_DEFINE_CONSTANT(constants, name); + QUIC_CONSTANTS(V) +#undef V + + NODE_DEFINE_CONSTANT(constants, NGTCP2_PROTO_VER); + NODE_DEFINE_CONSTANT(constants, NGTCP2_DEFAULT_MAX_ACK_DELAY); + NODE_DEFINE_CONSTANT(constants, NGTCP2_MAX_CIDLEN); + NODE_DEFINE_CONSTANT(constants, NGTCP2_MIN_CIDLEN); + NODE_DEFINE_CONSTANT(constants, NGTCP2_NO_ERROR); + NODE_DEFINE_CONSTANT(constants, AF_INET); + NODE_DEFINE_CONSTANT(constants, AF_INET6); + NODE_DEFINE_STRING_CONSTANT(constants, + NODE_STRINGIFY_HELPER(NGTCP2_ALPN_H3), + NGTCP2_ALPN_H3); + + target->Set(context, env->constants_string(), constants).FromJust(); +} + +} // namespace quic +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(quic, node::quic::Initialize) diff --git a/src/quic/node_quic_buffer-inl.h b/src/quic/node_quic_buffer-inl.h new file mode 100644 index 00000000000000..e03378a331154b --- /dev/null +++ b/src/quic/node_quic_buffer-inl.h @@ -0,0 +1,99 @@ +#ifndef SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ +#define SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_quic_buffer.h" +#include "node_bob-inl.h" +#include "util-inl.h" +#include "uv.h" + +#include + +namespace node { + +namespace quic { + +QuicBufferChunk::QuicBufferChunk(size_t len) + : data_buf_(len, 0), + buf_(uv_buf_init(reinterpret_cast(data_buf_.data()), len)), + length_(len), + done_called_(true) {} + +QuicBufferChunk::QuicBufferChunk(uv_buf_t buf, DoneCB done) + : buf_(buf), + length_(buf.len) { + if (done != nullptr) + done_ = std::move(done); +} + +QuicBufferChunk::~QuicBufferChunk() { + CHECK(done_called_); +} + +size_t QuicBufferChunk::Seek(size_t amount) { + amount = std::min(amount, remaining()); + buf_.base += amount; + buf_.len -= amount; + return amount; +} + +size_t QuicBufferChunk::Consume(size_t amount) { + amount = std::min(amount, length_); + length_ -= amount; + return amount; +} + +void QuicBufferChunk::Done(int status) { + if (done_called_) return; + done_called_ = true; + if (done_ != nullptr) + std::move(done_)(status); +} + +QuicBuffer::QuicBuffer(QuicBuffer&& src) noexcept + : head_(src.head_), + tail_(src.tail_), + ended_(src.ended_), + length_(src.length_) { + root_ = std::move(src.root_); + src.head_ = nullptr; + src.tail_ = nullptr; + src.length_ = 0; + src.remaining_ = 0; + src.ended_ = false; +} + + +QuicBuffer& QuicBuffer::operator=(QuicBuffer&& src) noexcept { + if (this == &src) return *this; + this->~QuicBuffer(); + return *new(this) QuicBuffer(std::move(src)); +} + +bool QuicBuffer::is_empty(uv_buf_t buf) { + DCHECK_IMPLIES(buf.base == nullptr, buf.len == 0); + return buf.len == 0; +} + +size_t QuicBuffer::Consume(size_t amount) { + return Consume(0, amount); +} + +size_t QuicBuffer::Cancel(int status) { + if (canceled_) return 0; + canceled_ = true; + size_t t = Consume(status, length()); + return t; +} + +void QuicBuffer::Push(uv_buf_t buf, DoneCB done) { + std::unique_ptr chunk = + std::make_unique(buf, done); + Push(std::move(chunk)); +} +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS +#endif // SRC_QUIC_NODE_QUIC_BUFFER_INL_H_ diff --git a/src/quic/node_quic_buffer.cc b/src/quic/node_quic_buffer.cc new file mode 100644 index 00000000000000..dc924da8a3190e --- /dev/null +++ b/src/quic/node_quic_buffer.cc @@ -0,0 +1,160 @@ +#include "node_quic_buffer-inl.h" // NOLINT(build/include) +#include "node_bob-inl.h" +#include "util.h" +#include "uv.h" + +#include +#include +#include +#include + +namespace node { +namespace quic { + +void QuicBufferChunk::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("buf", data_buf_); + tracker->TrackField("next", next_); +} + +size_t QuicBuffer::Push(uv_buf_t* bufs, size_t nbufs, DoneCB done) { + size_t len = 0; + if (nbufs == 0 || bufs == nullptr || is_empty(bufs[0])) { + done(0); + return 0; + } + size_t n = 0; + while (nbufs > 1) { + if (!is_empty(bufs[n])) { + Push(bufs[n]); + len += bufs[n].len; + } + n++; + nbufs--; + } + len += bufs[n].len; + Push(bufs[n], done); + return len; +} + +void QuicBuffer::Push(std::unique_ptr chunk) { + CHECK(!ended_); + length_ += chunk->remaining(); + remaining_ += chunk->remaining(); + if (!tail_) { + root_ = std::move(chunk); + head_ = tail_ = root_.get(); + } else { + tail_->next_ = std::move(chunk); + tail_ = tail_->next_.get(); + if (!head_) + head_ = tail_; + } +} + +size_t QuicBuffer::Seek(size_t amount) { + size_t len = 0; + while (head_ && amount > 0) { + size_t amt = head_->Seek(amount); + amount -= amt; + len += amt; + remaining_ -= amt; + if (head_->remaining()) + break; + head_ = head_->next_.get(); + } + return len; +} + +bool QuicBuffer::Pop(int status) { + if (!root_) + return false; + std::unique_ptr root(std::move(root_)); + root_ = std::move(root.get()->next_); + + if (head_ == root.get()) + head_ = root_.get(); + if (tail_ == root.get()) + tail_ = root_.get(); + + root->Done(status); + return true; +} + +size_t QuicBuffer::Consume(int status, size_t amount) { + size_t amt = std::min(amount, length_); + size_t len = 0; + while (root_ && amt > 0) { + auto root = root_.get(); + size_t consumed = root->Consume(amt); + len += consumed; + length_ -= consumed; + amt -= consumed; + if (root->length() > 0) + break; + Pop(status); + } + return len; +} + +void QuicBuffer::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("root", root_); +} + +int QuicBuffer::DoPull( + bob::Next next, + int options, + ngtcp2_vec* data, + size_t count, + size_t max_count_hint) { + size_t len = 0; + size_t numbytes = 0; + int status = bob::Status::STATUS_CONTINUE; + + // There's no data to read. + if (!remaining() || head_ == nullptr) { + status = is_ended() ? + bob::Status::STATUS_END : + bob::Status::STATUS_BLOCK; + std::move(next)(status, nullptr, 0, [](size_t len) {}); + return status; + } + + // Ensure that there's storage space. + MaybeStackBuffer vec; + if (data == nullptr || count == 0) { + vec.AllocateSufficientStorage(max_count_hint); + data = vec.out(); + } else { + max_count_hint = std::min(count, max_count_hint); + } + + // Build the list of buffers. + QuicBufferChunk* pos = head_; + while (pos != nullptr && len < max_count_hint) { + data[len].base = reinterpret_cast(pos->buf().base); + data[len].len = pos->buf().len; + numbytes += data[len].len; + len++; + pos = pos->next_.get(); + } + + // If the buffer is ended, and the number of bytes + // matches the total remaining and OPTIONS_END is + // used, set the status to STATUS_END. + if (is_ended() && + numbytes == remaining() && + options & bob::OPTIONS_END) + status = bob::Status::STATUS_END; + + // Pass the data back out to the caller. + std::move(next)( + status, + data, + len, + std::move([this](size_t len) { Seek(len); })); + + return status; +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_buffer.h b/src/quic/node_quic_buffer.h new file mode 100644 index 00000000000000..17f59a7e759161 --- /dev/null +++ b/src/quic/node_quic_buffer.h @@ -0,0 +1,239 @@ +#ifndef SRC_QUIC_NODE_QUIC_BUFFER_H_ +#define SRC_QUIC_NODE_QUIC_BUFFER_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "memory_tracker-inl.h" +#include "ngtcp2/ngtcp2.h" +#include "node.h" +#include "node_bob.h" +#include "node_internals.h" +#include "util.h" +#include "uv.h" + +#include + +namespace node { +namespace quic { + +class QuicBuffer; + +constexpr size_t kMaxVectorCount = 16; + +using DoneCB = std::function; + +// When data is sent over QUIC, we are required to retain it in memory +// until we receive an acknowledgement that it has been successfully +// acknowledged. The QuicBuffer object is what we use to handle that +// and track until it is acknowledged. To understand the QuicBuffer +// object itself, it is important to understand how ngtcp2 and nghttp3 +// handle data that is given to it to serialize into QUIC packets. +// +// An individual QUIC packet may contain multiple QUIC frames. Whenever +// we create a QUIC packet, we really have no idea what frames are going +// to be encoded or how much buffered handshake or stream data is going +// to be included within that QuicPacket. If there is buffered data +// available for a stream, we provide an array of pointers to that data +// and an indication about how much data is available, then we leave it +// entirely up to ngtcp2 and nghttp3 to determine how much of the data +// to encode into the QUIC packet. It is only *after* the QUIC packet +// is encoded that we can know how much was actually written. +// +// Once written to a QUIC Packet, we have to keep the data in memory +// until an acknowledgement is received. In QUIC, acknowledgements are +// received per range of packets. +// +// QuicBuffer is complicated because it needs to be able to accomplish +// three things: (a) buffering uv_buf_t instances passed down from +// JavaScript without memcpy and keeping track of the Write callback +// associated with each, (b) tracking what data has already been +// encoded in a QUIC packet and what data is remaining to be read, and +// (c) tracking which data has been acknowledged and which hasn't. +// QuicBuffer is further complicated by design quirks and limitations +// of the StreamBase API and how it interacts with the JavaScript side. +// +// QuicBuffer is essentially a linked list of QuicBufferChunk instances. +// A single QuicBufferChunk wraps a single non-zero-length uv_buf_t. +// When the QuicBufferChunk is created, we capture the total length +// of the buffer and the total number of bytes remaining to be sent. +// Initially, these numbers are identical. +// +// When data is encoded into a QuicPacket, we advance the QuicBufferChunk's +// remaining-to-be-read by the number of bytes actually encoded. If there +// are no more bytes remaining to be encoded, we move to the next chunk +// in the linked list. +// +// When an acknowledgement is received, we decrement the QuicBufferChunk's +// length by the number of acknowledged bytes. Once the unacknowledged +// length reaches 0, we invoke the callback function associated with the +// QuicBufferChunk (if any). +// +// QuicStream is a StreamBase implementation. For every DoWrite call, +// it receives one or more uv_buf_t instances in a single batch associated +// with a single write callback. For each uv_buf_t DoWrite gets, a +// corresponding QuicBufferChunk is added to the QuicBuffer, with the +// callback associated with the final chunk added to the list. + + +// A QuicBufferChunk contains the actual buffered data +// along with a callback to be called when the data has +// been consumed. +// +// Any given chunk as a remaining-to-be-acknowledged length (length()) and a +// remaining-to-be-read-length (remaining()). The former tracks the number +// of bytes that have yet to be acknowledged by the QUIC peer. Once the +// remaining-to-be-acknowledged length reaches zero, the done callback +// associated with the QuicBufferChunk can be called and the QuicBufferChunk +// can be discarded. The remaining-to-be-read length is adjusted as data is +// serialized into QUIC packets and sent. +// The remaining-to-be-acknowledged length is adjusted using consume(), +// while the remaining-to-be-ead length is adjusted using seek(). +class QuicBufferChunk : public MemoryRetainer { + public: + // Default non-op done handler. + static void default_done(int status) {} + + // In this variant, the QuicBufferChunk owns the underlying + // data storage within a vector. The data will be + // freed when the QuicBufferChunk is destroyed. + inline explicit QuicBufferChunk(size_t len); + + // In this variant, the QuicBufferChunk only maintains a + // pointer to the underlying data buffer. The QuicBufferChunk + // does not take ownership of the buffer. The done callback + // is invoked to let the caller know when the chunk is no + // longer being used. + inline QuicBufferChunk(uv_buf_t buf_, DoneCB done_); + + inline ~QuicBufferChunk() override; + + // Invokes the done callback associated with the QuicBufferChunk. + inline void Done(int status); + + // length() provides the remaining-to-be-acknowledged length. + // The QuicBufferChunk must be retained in memory while this + // count is greater than zero. The length is adjusted by + // calling Consume(); + inline size_t length() const { return length_; } + + // remaining() provides the remaining-to-be-read length number of bytes. + // The length is adjusted by calling Seek() + inline size_t remaining() const { return buf_.len; } + + // Consumes (acknowledges) the given number of bytes. If amount + // is greater than length(), only length() bytes are consumed. + // Returns the actual number of bytes consumed. + inline size_t Consume(size_t amount); + + // Seeks (reads) the given number of bytes. If amount is greater + // than remaining(), only remaining() bytes are read. Returns + // the actual number of bytes read. + inline size_t Seek(size_t amount); + + uint8_t* out() { return reinterpret_cast(buf_.base); } + uv_buf_t buf() { return buf_; } + const uv_buf_t buf() const { return buf_; } + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicBufferChunk) + SET_SELF_SIZE(QuicBufferChunk) + + private: + std::vector data_buf_; + uv_buf_t buf_; + DoneCB done_ = default_done; + size_t length_ = 0; + bool done_called_ = false; + std::unique_ptr next_; + + friend class QuicBuffer; +}; + +class QuicBuffer : public bob::SourceImpl, + public MemoryRetainer { + public: + QuicBuffer() = default; + + inline QuicBuffer(QuicBuffer&& src) noexcept; + inline QuicBuffer& operator=(QuicBuffer&& src) noexcept; + + ~QuicBuffer() override { + Cancel(); // Cancel the remaining data + CHECK_EQ(length_, 0); + } + + // Marks the QuicBuffer as having ended, preventing new QuicBufferChunk + // instances from being appended to the linked list and allowing the + // Pull operation to know when to signal that the flow of data is + // completed. + void End() { ended_ = true; } + bool is_ended() const { return ended_; } + + // Push one or more uv_buf_t instances into the buffer. + // the DoneCB callback will be invoked when the last + // uv_buf_t in the bufs array is consumed and popped out + // of the internal linked list. Ownership of the uv_buf_t + // remains with the caller. + size_t Push( + uv_buf_t* bufs, + size_t nbufs, + DoneCB done = QuicBufferChunk::default_done); + + // Pushes a single QuicBufferChunk into the linked list + void Push(std::unique_ptr chunk); + + // Consume the given number of bytes within the buffer. If amount + // is greater than length(), length() bytes are consumed. Returns + // the actual number of bytes consumed. + inline size_t Consume(size_t amount); + + // Cancels the remaining bytes within the buffer. + inline size_t Cancel(int status = UV_ECANCELED); + + // Seeks (reads) the given number of bytes. If amount is greater + // than remaining(), seeks remaining() bytes. Returns the actual + // number of bytes read. + size_t Seek(size_t amount); + + // The total number of unacknowledged bytes remaining. + size_t length() const { return length_; } + + // The total number of unread bytes remaining. + size_t remaining() const { return remaining_; } + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicBuffer); + SET_SELF_SIZE(QuicBuffer); + + protected: + int DoPull( + bob::Next next, + int options, + ngtcp2_vec* data, + size_t count, + size_t max_count_hint) override; + + private: + inline static bool is_empty(uv_buf_t buf); + size_t Consume(int status, size_t amount); + bool Pop(int status = 0); + inline void Push(uv_buf_t buf, DoneCB done = nullptr); + + std::unique_ptr root_; + QuicBufferChunk* head_ = nullptr; // Current Read Position + QuicBufferChunk* tail_ = nullptr; // Current Write Position + + bool canceled_ = false; + bool ended_ = false; + size_t length_ = 0; + size_t remaining_ = 0; + + friend class QuicBufferChunk; +}; + +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_BUFFER_H_ diff --git a/src/quic/node_quic_crypto.cc b/src/quic/node_quic_crypto.cc new file mode 100644 index 00000000000000..b48a402df56016 --- /dev/null +++ b/src/quic/node_quic_crypto.cc @@ -0,0 +1,960 @@ +#include "node_quic_crypto.h" +#include "env-inl.h" +#include "node_crypto.h" +#include "node_crypto_common.h" +#include "node_process.h" +#include "node_quic_session-inl.h" +#include "node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "node_url.h" +#include "string_bytes.h" +#include "v8.h" +#include "util-inl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace node { + +using crypto::EntropySource; +using v8::Local; +using v8::String; +using v8::Value; + +namespace quic { + +bool SessionTicketAppData::Set(const uint8_t* data, size_t len) { + if (set_) + return false; + set_ = true; + SSL_SESSION_set1_ticket_appdata(session_, data, len); + return set_; +} + +bool SessionTicketAppData::Get(uint8_t** data, size_t* len) const { + return SSL_SESSION_get0_ticket_appdata( + session_, + reinterpret_cast(data), + len) == 1; +} + +namespace { +constexpr int kCryptoTokenKeylen = 32; +constexpr int kCryptoTokenIvlen = 32; + +// Used solely to derive the keys used to generate and +// validate retry tokens. The implementation of this is +// Node.js specific. We use the current implementation +// because it is simple. +bool DeriveTokenKey( + uint8_t* token_key, + uint8_t* token_iv, + const uint8_t* rand_data, + size_t rand_datalen, + const ngtcp2_crypto_ctx& ctx, + const uint8_t* token_secret) { + static constexpr int kCryptoTokenSecretlen = 32; + uint8_t secret[kCryptoTokenSecretlen]; + + return + NGTCP2_OK(ngtcp2_crypto_hkdf_extract( + secret, + &ctx.md, + token_secret, + kTokenSecretLen, + rand_data, + rand_datalen)) && + NGTCP2_OK(ngtcp2_crypto_derive_packet_protection_key( + token_key, + token_iv, + nullptr, + &ctx.aead, + &ctx.md, + secret, + kCryptoTokenSecretlen)); +} + +// Retry tokens are generated only by QUIC servers. They +// are opaque to QUIC clients and must not be guessable by +// on- or off-path attackers. A QUIC server sends a RETRY +// token as a way of initiating explicit path validation +// with a client in response to an initial QUIC packet. +// The client, upon receiving a RETRY, must abandon the +// initial connection attempt and try again, including the +// received retry token in the new initial packet sent to +// the server. If the server is performing explicit +// valiation, it will look for the presence of the retry +// token and validate it if found. The internal structure +// of the retry token must be meaningful to the server, +// and the server must be able to validate the token without +// relying on any state left over from the previous connection +// attempt. The implementation here is entirely Node.js +// specific. +// +// The token is generated by: +// 1. Appending the raw bytes of given socket address, the current +// timestamp, and the original CID together into a single byte +// array. +// 2. Generating a block of random data that is used together with +// the token secret to cryptographically derive an encryption key. +// 3. Encrypting the byte array from step 1 using the encryption key +// from step 2. +// 4. Appending random data generated in step 2 to the token. +// +// The token secret must be kept secret on the QUIC server that +// generated the retry. When multiple QUIC servers are used in a +// cluster, it cannot be guaranteed that the same QUIC server +// instance will receive the subsequent new Initial packet. Therefore, +// all QUIC servers in the cluster should either share or be aware +// of the same token secret or a mechanism needs to be implemented +// to ensure that subsequent packets are routed to the same QUIC +// server instance. +// +// A malicious peer could attempt to guess the token secret by +// sending a large number specially crafted RETRY-eliciting packets +// to a server then analyzing the resulting retry tokens. To reduce +// the possibility of such attacks, the current implementation of +// QuicSocket generates the token secret randomly for each instance, +// and the number of RETRY responses sent to a given remote address +// should be limited. Such attacks should be of little actual value +// in most cases. +bool GenerateRetryToken( + uint8_t* token, + size_t* tokenlen, + const SocketAddress& addr, + const QuicCID& ocid, + const uint8_t* token_secret) { + std::array plaintext; + uint8_t rand_data[kTokenRandLen]; + uint8_t token_key[kCryptoTokenKeylen]; + uint8_t token_iv[kCryptoTokenIvlen]; + + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_ctx_initial(&ctx); + size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead); + uint64_t now = uv_hrtime(); + + auto p = std::begin(plaintext); + p = std::copy_n(addr.raw(), addr.length(), p); + p = std::copy_n(reinterpret_cast(&now), sizeof(uint64_t), p); + p = std::copy_n(ocid->data, ocid->datalen, p); + + EntropySource(rand_data, kTokenRandLen); + + if (!DeriveTokenKey( + token_key, + token_iv, + rand_data, + kTokenRandLen, + ctx, + token_secret)) { + return false; + } + + size_t plaintextlen = std::distance(std::begin(plaintext), p); + if (NGTCP2_ERR(ngtcp2_crypto_encrypt( + token, + &ctx.aead, + plaintext.data(), + plaintextlen, + token_key, + token_iv, + ivlen, + addr.raw(), + addr.length()))) { + return false; + } + + *tokenlen = plaintextlen + ngtcp2_crypto_aead_taglen(&ctx.aead); + memcpy(token + (*tokenlen), rand_data, kTokenRandLen); + *tokenlen += kTokenRandLen; + return true; +} +} // namespace + +// A stateless reset token is used when a QUIC endpoint receives a +// QUIC packet with a short header but the associated connection ID +// cannot be matched to any known QuicSession. In such cases, the +// receiver may choose to send a subtle opaque indication to the +// sending peer that state for the QuicSession has apparently been +// lost. For any on- or off- path attacker, a stateless reset packet +// resembles any other QUIC packet with a short header. In order to +// be successfully handled as a stateless reset, the peer must have +// already seen a reset token issued to it associated with the given +// CID. The token itself is opaque to the peer that receives is but +// must be possible to statelessly recreate by the peer that +// originally created it. The actual implementation is Node.js +// specific but we currently defer to a utility function provided +// by ngtcp2. +bool GenerateResetToken( + uint8_t* token, + const uint8_t* secret, + const QuicCID& cid) { + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_ctx_initial(&ctx); + return NGTCP2_OK(ngtcp2_crypto_generate_stateless_reset_token( + token, + &ctx.md, + secret, + NGTCP2_STATELESS_RESET_TOKENLEN, + cid.cid())); +} + +// Generates a RETRY packet. See the notes for GenerateRetryToken for details. +std::unique_ptr GenerateRetryPacket( + const uint8_t* token_secret, + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr) { + + uint8_t token[256]; + size_t tokenlen = sizeof(token); + + if (!GenerateRetryToken(token, &tokenlen, remote_addr, dcid, token_secret)) + return {}; + + QuicCID cid; + EntropySource(cid.data(), kScidLen); + cid.set_length(kScidLen); + + size_t pktlen = tokenlen + (2 * NGTCP2_MAX_CIDLEN) + scid.length() + 8; + CHECK_LE(pktlen, NGTCP2_MAX_PKT_SIZE); + + auto packet = QuicPacket::Create("retry", pktlen); + ssize_t nwrite = + ngtcp2_crypto_write_retry( + packet->data(), + NGTCP2_MAX_PKTLEN_IPV4, + scid.cid(), + cid.cid(), + dcid.cid(), + token, + tokenlen); + if (nwrite <= 0) + return {}; + packet->set_length(nwrite); + return packet; +} + +// Validates a retry token included in the given header. This will return +// true if the token cannot be validated, false otherwise. A token is +// valid if it can be successfully decrypted using the key derived from +// random data embedded in the token, the structure of the token matches +// that generated by the GenerateRetryToken function, and the token was +// not generated earlier than now - verification_expiration. If validation +// is successful, ocid will be updated to the original connection ID encoded +// in the encrypted token. +bool InvalidRetryToken( + const ngtcp2_pkt_hd& hd, + const SocketAddress& addr, + QuicCID* ocid, + const uint8_t* token_secret, + uint64_t verification_expiration) { + + if (hd.tokenlen < kTokenRandLen) + return true; + + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_ctx_initial(&ctx); + + size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(&ctx.aead); + + size_t ciphertextlen = hd.tokenlen - kTokenRandLen; + const uint8_t* ciphertext = hd.token; + const uint8_t* rand_data = hd.token + ciphertextlen; + + uint8_t token_key[kCryptoTokenKeylen]; + uint8_t token_iv[kCryptoTokenIvlen]; + + if (!DeriveTokenKey( + token_key, + token_iv, + rand_data, + kTokenRandLen, + ctx, + token_secret)) { + return true; + } + + uint8_t plaintext[4096]; + + if (NGTCP2_ERR(ngtcp2_crypto_decrypt( + plaintext, + &ctx.aead, + ciphertext, + ciphertextlen, + token_key, + token_iv, + ivlen, + addr.raw(), + addr.length()))) { + return true; + } + + size_t plaintextlen = ciphertextlen - ngtcp2_crypto_aead_taglen(&ctx.aead); + if (plaintextlen < addr.length() + sizeof(uint64_t)) + return true; + + ssize_t cil = plaintextlen - addr.length() - sizeof(uint64_t); + if ((cil != 0 && (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN)) || + memcmp(plaintext, addr.raw(), addr.length()) != 0) { + return true; + } + + uint64_t t; + memcpy(&t, plaintext + addr.length(), sizeof(uint64_t)); + + // 10-second window by default, but configurable for each + // QuicSocket instance with a MIN_RETRYTOKEN_EXPIRATION second + // minimum and a MAX_RETRYTOKEN_EXPIRATION second maximum. + if (t + verification_expiration * NGTCP2_SECONDS < uv_hrtime()) + return true; + + ngtcp2_cid_init( + ocid->cid(), + plaintext + addr.length() + sizeof(uint64_t), + cil); + + return false; +} + +namespace { + +bool SplitHostname( + const char* hostname, + std::vector* parts, + const char delim = '.') { + static std::string check_str = + "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\x30" + "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x40" + "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F\x50" + "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F\x60" + "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70" + "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"; + + std::stringstream str(hostname); + std::string part; + while (getline(str, part, delim)) { + // if (part.length() == 0 || + // part.find_first_not_of(check_str) != std::string::npos) { + // return false; + // } + for (size_t n = 0; n < part.length(); n++) { + if (part[n] >= 'A' && part[n] <= 'Z') + part[n] = (part[n] | 0x20); // Lower case the letter + if (check_str.find(part[n]) == std::string::npos) + return false; + } + parts->push_back(part); + } + return true; +} + +bool CheckCertNames( + const std::vector& host_parts, + const std::string& name, + bool use_wildcard = true) { + + if (name.length() == 0) + return false; + + std::vector name_parts; + if (!SplitHostname(name.c_str(), &name_parts)) + return false; + + if (name_parts.size() != host_parts.size()) + return false; + + for (size_t n = host_parts.size() - 1; n > 0; --n) { + if (host_parts[n] != name_parts[n]) + return false; + } + + if (name_parts[0].find("*") == std::string::npos || + name_parts[0].find("xn--") != std::string::npos) { + return host_parts[0] == name_parts[0]; + } + + if (!use_wildcard) + return false; + + std::vector sub_parts; + SplitHostname(name_parts[0].c_str(), &sub_parts, '*'); + + if (sub_parts.size() > 2) + return false; + + if (name_parts.size() <= 2) + return false; + + std::string prefix; + std::string suffix; + if (sub_parts.size() == 2) { + prefix = sub_parts[0]; + suffix = sub_parts[1]; + } else { + prefix = ""; + suffix = sub_parts[0]; + } + + if (prefix.length() + suffix.length() > host_parts[0].length()) + return false; + + if (host_parts[0].compare(0, prefix.length(), prefix)) + return false; + + if (host_parts[0].compare( + host_parts[0].length() - suffix.length(), + suffix.length(), suffix)) { + return false; + } + + return true; +} + +} // namespace + +int VerifyHostnameIdentity( + const crypto::SSLPointer& ssl, + const char* hostname) { + int err = X509_V_ERR_HOSTNAME_MISMATCH; + crypto::X509Pointer cert(SSL_get_peer_certificate(ssl.get())); + if (!cert) + return err; + + // There are several pieces of information we need from the cert at this point + // 1. The Subject (if it exists) + // 2. The collection of Alt Names (if it exists) + // + // The certificate may have many Alt Names. We only care about the ones that + // are prefixed with 'DNS:', 'URI:', or 'IP Address:'. We might check + // additional ones later but we'll start with these. + // + // Ideally, we'd be able to *just* use OpenSSL's built in name checking for + // this (SSL_set1_host and X509_check_host) but it does not appear to do + // checking on URI or IP Address Alt names, which is unfortunate. We need + // both of those to retain compatibility with the peer identity verification + // Node.js already does elsewhere. At the very least, we'll use + // X509_check_host here first as a first step. If it is successful, awesome, + // there's nothing else for us to do. Return and be happy! + if (X509_check_host( + cert.get(), + hostname, + strlen(hostname), + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT | + X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS | + X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, + nullptr) > 0) { + return 0; + } + + if (X509_check_ip_asc( + cert.get(), + hostname, + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) > 0) { + return 0; + } + + // If we've made it this far, then we have to perform a more check + return VerifyHostnameIdentity( + hostname, + crypto::GetCertificateCN(cert.get()), + crypto::GetCertificateAltNames(cert.get())); +} + +int VerifyHostnameIdentity( + const char* hostname, + const std::string& cert_cn, + const std::unordered_multimap& altnames) { + + int err = X509_V_ERR_HOSTNAME_MISMATCH; + + // 1. If the hostname is an IP address (v4 or v6), the certificate is valid + // if and only if there is an 'IP Address:' alt name specifying the same + // IP address. The IP address must be canonicalized to ensure a proper + // check. It's possible that the X509_check_ip_asc covers this. If so, + // we can remove this check. + + if (SocketAddress::is_numeric_host(hostname)) { + auto ips = altnames.equal_range("ip"); + for (auto ip = ips.first; ip != ips.second; ++ip) { + if (ip->second.compare(hostname) == 0) { + // Success! + return 0; + } + } + // No match, and since the hostname is an IP address, skip any + // further checks + return err; + } + + auto dns_names = altnames.equal_range("dns"); + auto uri_names = altnames.equal_range("uri"); + + size_t dns_count = std::distance(dns_names.first, dns_names.second); + size_t uri_count = std::distance(uri_names.first, uri_names.second); + + std::vector host_parts; + SplitHostname(hostname, &host_parts); + + // 2. If there no 'DNS:' or 'URI:' Alt names, if the certificate has a + // Subject, then we need to extract the CN field from the Subject. and + // check that the hostname matches the CN, taking into consideration + // the possibility of a wildcard in the CN. If there is a match, congrats, + // we have a valid certificate. Return and be happy. + + if (dns_count == 0 && uri_count == 0) { + if (cert_cn.length() > 0 && CheckCertNames(host_parts, cert_cn)) + return 0; + // No match, and since there are no dns or uri entries, return + return err; + } + + // 3. If, however, there are 'DNS:' and 'URI:' Alt names, things become more + // complicated. Essentially, we need to iterate through each 'DNS:' and + // 'URI:' Alt name to find one that matches. The 'DNS:' Alt names are + // relatively simple but may include wildcards. The 'URI:' Alt names + // require the name to be parsed as a URL, then extract the hostname from + // the URL, which is then checked against the hostname. If you find a + // match, yay! Return and be happy. (Note, it's possible that the 'DNS:' + // check in this step is redundant to the X509_check_host check. If so, + // we can simplify by removing those checks here.) + + // First, let's check dns names + for (auto name = dns_names.first; name != dns_names.second; ++name) { + if (name->first.length() > 0 && + CheckCertNames(host_parts, name->second)) { + return 0; + } + } + + // Then, check uri names + for (auto name = uri_names.first; name != uri_names.second; ++name) { + if (name->first.length() > 0 && + CheckCertNames(host_parts, name->second, false)) { + return 0; + } + } + + // 4. Failing all of the previous checks, we assume the certificate is + // invalid for an unspecified reason. + return err; +} + +// Get the ALPN protocol identifier that was negotiated for the session +Local GetALPNProtocol(const QuicSession& session) { + QuicCryptoContext* ctx = session.crypto_context(); + Environment* env = session.env(); + std::string alpn = ctx->selected_alpn(); + if (alpn == NGTCP2_ALPN_H3 + 1) { + return env->quic_alpn_string(); + } else { + return ToV8Value( + env->context(), + alpn, + env->isolate()).FromMaybe(Local()); + } +} + +namespace { +int CertCB(SSL* ssl, void* arg) { + QuicSession* session = static_cast(arg); + return SSL_get_tlsext_status_type(ssl) == TLSEXT_STATUSTYPE_ocsp ? + session->crypto_context()->OnOCSP() : 1; +} + +void Keylog_CB(const SSL* ssl, const char* line) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + session->crypto_context()->Keylog(line); +} + +int Client_Hello_CB( + SSL* ssl, + int* tls_alert, + void* arg) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + int ret = session->crypto_context()->OnClientHello(); + switch (ret) { + case 0: + return 1; + case -1: + return -1; + default: + *tls_alert = ret; + return 0; + } +} + +int AlpnSelection( + SSL* ssl, + const unsigned char** out, + unsigned char* outlen, + const unsigned char* in, + unsigned int inlen, + void* arg) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + + unsigned char* tmp; + + // The QuicServerSession supports exactly one ALPN identifier. If that does + // not match any of the ALPN identifiers provided in the client request, + // then we fail here. Note that this will not fail the TLS handshake, so + // we have to check later if the ALPN matches the expected identifier or not. + if (SSL_select_next_proto( + &tmp, + outlen, + reinterpret_cast(session->alpn().c_str()), + session->alpn().length(), + in, + inlen) == OPENSSL_NPN_NO_OVERLAP) { + return SSL_TLSEXT_ERR_NOACK; + } + *out = tmp; + return SSL_TLSEXT_ERR_OK; +} + +int AllowEarlyDataCB(SSL* ssl, void* arg) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + return session->allow_early_data() ? 1 : 0; +} + +int TLS_Status_Callback(SSL* ssl, void* arg) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + return session->crypto_context()->OnTLSStatus(); +} + +int New_Session_Callback(SSL* ssl, SSL_SESSION* session) { + QuicSession* s = static_cast(SSL_get_app_data(ssl)); + return s->set_session(session); +} + +int GenerateSessionTicket(SSL* ssl, void* arg) { + QuicSession* s = static_cast(SSL_get_app_data(ssl)); + SessionTicketAppData app_data(SSL_get_session(ssl)); + s->SetSessionTicketAppData(app_data); + return 1; +} + +SSL_TICKET_RETURN DecryptSessionTicket( + SSL* ssl, + SSL_SESSION* session, + const unsigned char* keyname, + size_t keyname_len, + SSL_TICKET_STATUS status, + void* arg) { + QuicSession* s = static_cast(SSL_get_app_data(ssl)); + SessionTicketAppData::Flag flag = SessionTicketAppData::Flag::STATUS_NONE; + switch (status) { + default: + return SSL_TICKET_RETURN_IGNORE; + case SSL_TICKET_EMPTY: + // Fall through + case SSL_TICKET_NO_DECRYPT: + return SSL_TICKET_RETURN_IGNORE_RENEW; + case SSL_TICKET_SUCCESS_RENEW: + flag = SessionTicketAppData::Flag::STATUS_RENEW; + // Fall through + case SSL_TICKET_SUCCESS: + SessionTicketAppData app_data(session); + switch (s->GetSessionTicketAppData(app_data, flag)) { + default: + return SSL_TICKET_RETURN_IGNORE; + case SessionTicketAppData::Status::TICKET_IGNORE: + return SSL_TICKET_RETURN_IGNORE; + case SessionTicketAppData::Status::TICKET_IGNORE_RENEW: + return SSL_TICKET_RETURN_IGNORE_RENEW; + case SessionTicketAppData::Status::TICKET_USE: + return SSL_TICKET_RETURN_USE; + case SessionTicketAppData::Status::TICKET_USE_RENEW: + return SSL_TICKET_RETURN_USE_RENEW; + } + } +} + +int SetEncryptionSecrets( + SSL* ssl, + OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t* read_secret, + const uint8_t* write_secret, + size_t secret_len) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + return session->crypto_context()->OnSecrets( + from_ossl_level(ossl_level), + read_secret, + write_secret, + secret_len) ? 1 : 0; +} + +int AddHandshakeData( + SSL* ssl, + OSSL_ENCRYPTION_LEVEL ossl_level, + const uint8_t* data, + size_t len) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + session->crypto_context()->WriteHandshake( + from_ossl_level(ossl_level), + data, + len); + return 1; +} + +int FlushFlight(SSL* ssl) { return 1; } + +int SendAlert( + SSL* ssl, + enum ssl_encryption_level_t level, + uint8_t alert) { + QuicSession* session = static_cast(SSL_get_app_data(ssl)); + session->crypto_context()->set_tls_alert(alert); + return 1; +} + +bool SetTransportParams(QuicSession* session, const crypto::SSLPointer& ssl) { + ngtcp2_transport_params params; + ngtcp2_conn_get_local_transport_params(session->connection(), ¶ms); + uint8_t buf[512]; + ssize_t nwrite = ngtcp2_encode_transport_params( + buf, + arraysize(buf), + NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + ¶ms); + return nwrite >= 0 && + SSL_set_quic_transport_params(ssl.get(), buf, nwrite) == 1; +} + +SSL_QUIC_METHOD quic_method = SSL_QUIC_METHOD{ + SetEncryptionSecrets, + AddHandshakeData, + FlushFlight, + SendAlert +}; + +void SetHostname(const crypto::SSLPointer& ssl, const std::string& hostname) { + // If the hostname is an IP address, use an empty string + // as the hostname instead. + if (SocketAddress::is_numeric_host(hostname.c_str())) { + SSL_set_tlsext_host_name(ssl.get(), ""); + } else { + SSL_set_tlsext_host_name(ssl.get(), hostname.c_str()); + } +} + +} // namespace + +void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl) { + QuicCryptoContext* ctx = session->crypto_context(); + Environment* env = session->env(); + + SSL_set_app_data(ssl.get(), session); + SSL_set_cert_cb(ssl.get(), CertCB, + const_cast(reinterpret_cast(session))); + SSL_set_verify(ssl.get(), SSL_VERIFY_NONE, crypto::VerifyCallback); + + // Enable tracing if the `--trace-tls` command line flag is used. + if (env->options()->trace_tls) { + ctx->EnableTrace(); + if (env->quic_state()->warn_trace_tls) { + env->quic_state()->warn_trace_tls = false; + ProcessEmitWarning(env, + "Enabling --trace-tls can expose sensitive data " + "in the resulting log"); + } + } + + switch (ctx->side()) { + case NGTCP2_CRYPTO_SIDE_CLIENT: { + SSL_set_connect_state(ssl.get()); + crypto::SetALPN(ssl, session->alpn()); + SetHostname(ssl, session->hostname()); + if (ctx->is_option_set(QUICCLIENTSESSION_OPTION_REQUEST_OCSP)) + SSL_set_tlsext_status_type(ssl.get(), TLSEXT_STATUSTYPE_ocsp); + break; + } + case NGTCP2_CRYPTO_SIDE_SERVER: { + SSL_set_accept_state(ssl.get()); + if (ctx->is_option_set(QUICSERVERSESSION_OPTION_REQUEST_CERT)) { + int verify_mode = SSL_VERIFY_PEER; + if (ctx->is_option_set(QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED)) + verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + SSL_set_verify(ssl.get(), verify_mode, crypto::VerifyCallback); + } + break; + } + default: + UNREACHABLE(); + } + + SetTransportParams(session, ssl); +} + +void InitializeSecureContext( + BaseObjectPtr sc, + bool early_data, + ngtcp2_crypto_side side) { + // TODO(@jasnell): Using a static value for this at the moment but + // we need to determine if a non-static or per-session value is better. + constexpr static unsigned char session_id_ctx[] = "node.js quic server"; + switch (side) { + case NGTCP2_CRYPTO_SIDE_SERVER: + SSL_CTX_set_options( + **sc, + (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | + SSL_OP_SINGLE_ECDH_USE | + SSL_OP_CIPHER_SERVER_PREFERENCE | + SSL_OP_NO_ANTI_REPLAY); + + SSL_CTX_set_mode(**sc, SSL_MODE_RELEASE_BUFFERS); + + SSL_CTX_set_alpn_select_cb(**sc, AlpnSelection, nullptr); + SSL_CTX_set_client_hello_cb(**sc, Client_Hello_CB, nullptr); + + SSL_CTX_set_session_ticket_cb( + **sc, + GenerateSessionTicket, + DecryptSessionTicket, + nullptr); + + if (early_data) { + SSL_CTX_set_max_early_data(**sc, 0xffffffff); + SSL_CTX_set_allow_early_data_cb(**sc, AllowEarlyDataCB, nullptr); + } + + SSL_CTX_set_session_id_context( + **sc, + session_id_ctx, + sizeof(session_id_ctx) - 1); + break; + case NGTCP2_CRYPTO_SIDE_CLIENT: + SSL_CTX_set_session_cache_mode( + **sc, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL_STORE); + SSL_CTX_sess_set_new_cb(**sc, New_Session_Callback); + break; + default: + UNREACHABLE(); + } + SSL_CTX_set_min_proto_version(**sc, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(**sc, TLS1_3_VERSION); + SSL_CTX_set_default_verify_paths(**sc); + SSL_CTX_set_tlsext_status_cb(**sc, TLS_Status_Callback); + SSL_CTX_set_keylog_callback(**sc, Keylog_CB); + SSL_CTX_set_tlsext_status_arg(**sc, nullptr); + SSL_CTX_set_quic_method(**sc, &quic_method); +} + +bool DeriveAndInstallInitialKey( + const QuicSession& session, + const QuicCID& dcid) { + uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_hp[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + return NGTCP2_OK(ngtcp2_crypto_derive_and_install_initial_key( + session.connection(), + rx_secret, + tx_secret, + initial_secret, + rx_key, + rx_iv, + rx_hp, + tx_key, + tx_iv, + tx_hp, + dcid.cid(), + session.crypto_context()->side())); +} + +ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) { + switch (ossl_level) { + case ssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case ssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case ssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case ssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APP; + default: + UNREACHABLE(); + } +} + +const char* crypto_level_name(ngtcp2_crypto_level level) { + switch (level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return "initial"; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return "early"; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return "handshake"; + case NGTCP2_CRYPTO_LEVEL_APP: + return "app"; + default: + UNREACHABLE(); + } +} + +// When using IPv6, QUIC recommends the use of IPv6 Flow Labels +// as specified in https://tools.ietf.org/html/rfc6437. These +// are used as a means of reliably associating packets exchanged +// as part of a single flow and protecting against certain kinds +// of attacks. +uint32_t GenerateFlowLabel( + const SocketAddress& local, + const SocketAddress& remote, + const QuicCID& cid, + const uint8_t* secret, + size_t secretlen) { + static constexpr size_t kInfoLen = + (sizeof(sockaddr_in6) * 2) + NGTCP2_MAX_CIDLEN; + + uint32_t label = 0; + + std::array plaintext; + size_t infolen = local.length() + remote.length() + cid.length(); + CHECK_LE(infolen, kInfoLen); + + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_ctx_initial(&ctx); + + auto p = std::begin(plaintext); + p = std::copy_n(local.raw(), local.length(), p); + p = std::copy_n(remote.raw(), remote.length(), p); + p = std::copy_n(cid->data, cid->datalen, p); + + ngtcp2_crypto_hkdf_expand( + reinterpret_cast(&label), + sizeof(label), + &ctx.md, + secret, + secretlen, + plaintext.data(), + infolen); + + label &= kLabelMask; + DCHECK_LE(label, kLabelMask); + return label; +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_crypto.h b/src/quic/node_quic_crypto.h new file mode 100644 index 00000000000000..5bada2d209765b --- /dev/null +++ b/src/quic/node_quic_crypto.h @@ -0,0 +1,159 @@ +#ifndef SRC_QUIC_NODE_QUIC_CRYPTO_H_ +#define SRC_QUIC_NODE_QUIC_CRYPTO_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_crypto.h" +#include "node_quic_util.h" +#include "v8.h" + +#include +#include +#include + +#include + +namespace node { + +namespace quic { + +// Crypto and OpenSSL related utility functions used in +// various places throughout the QUIC implementation. + +// Forward declaration +class QuicSession; +class QuicPacket; + +// many ngtcp2 functions return 0 to indicate success +// and non-zero to indicate failure. Most of the time, +// for such functions we don't care about the specific +// return value so we simplify using a macro. + +#define NGTCP2_ERR(V) (V != 0) +#define NGTCP2_OK(V) (V == 0) + +// Called by QuicInitSecureContext to initialize the +// given SecureContext with the defaults for the given +// QUIC side (client or server). +void InitializeSecureContext( + BaseObjectPtr sc, + bool early_data, + ngtcp2_crypto_side side); + +// Called in the QuicSession::InitServer and +// QuicSession::InitClient to configure the +// appropriate settings for the SSL* associated +// with the session. +void InitializeTLS(QuicSession* session, const crypto::SSLPointer& ssl); + +// Called when the client QuicSession is created and +// when the server QuicSession first receives the +// client hello. +bool DeriveAndInstallInitialKey( + const QuicSession& session, + const QuicCID& dcid); + +// Generates a stateless reset token using HKDF with the +// cid and token secret as input. The token secret is +// either provided by user code when a QuicSocket is +// created or is generated randomly. +// +// QUIC leaves the generation of stateless session tokens +// up to the implementation to figure out. The idea, however, +// is that it ought to be possible to generate a stateless +// reset token reliably even when all state for a connection +// has been lost. We use the cid as it's the only reliably +// consistent bit of data we have when a session is destroyed. +bool GenerateResetToken( + uint8_t* token, + const uint8_t* secret, + const QuicCID& cid); + +// The Retry Token is an encrypted token that is sent to the client +// by the server as part of the path validation flow. The plaintext +// format within the token is opaque and only meaningful the server. +// We can structure it any way we want. It needs to: +// * be hard to guess +// * be time limited +// * be specific to the client address +// * be specific to the original cid +// * contain random data. +std::unique_ptr GenerateRetryPacket( + const uint8_t* token_secret, + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr); + +// The IPv6 Flow Label is generated and set whenever IPv6 is used. +// The label is derived as a cryptographic function of the CID, +// local and remote addresses, and the given secret, that is then +// truncated to a 20-bit value (per IPv6 requirements). In QUIC, +// the flow label *may* be used as a way of disambiguating IP +// packets that belong to the same flow from a remote peer. +uint32_t GenerateFlowLabel( + const SocketAddress& local, + const SocketAddress& remote, + const QuicCID& cid, + const uint8_t* secret, + size_t secretlen); + +// Verifies the validity of a retry token. Returns true if the +// token is *not valid*, false otherwise. If the token is valid, +// the ocid will be updated to the original CID value encoded +// within the successfully validated, encrypted token. +bool InvalidRetryToken( + const ngtcp2_pkt_hd& hd, + const SocketAddress& addr, + QuicCID* ocid, + const uint8_t* token_secret, + uint64_t verification_expiration); + +int VerifyHostnameIdentity(const crypto::SSLPointer& ssl, const char* hostname); +int VerifyHostnameIdentity( + const char* hostname, + const std::string& cert_cn, + const std::unordered_multimap& altnames); + +// Get the ALPN protocol identifier that was negotiated for the session +v8::Local GetALPNProtocol(const QuicSession& session); + +ngtcp2_crypto_level from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level); +const char* crypto_level_name(ngtcp2_crypto_level level); + +// SessionTicketAppData is a utility class that is used only during +// the generation or access of TLS stateless sesson tickets. It +// exists solely to provide a easier way for QuicApplication instances +// to set relevant metadata in the session ticket when it is created, +// and the exract and subsequently verify that data when a ticket is +// received and is being validated. The app data is completely opaque +// to anything other than the server-side of the QuicApplication that +// sets it. +class SessionTicketAppData { + public: + enum class Status { + TICKET_USE, + TICKET_USE_RENEW, + TICKET_IGNORE, + TICKET_IGNORE_RENEW + }; + + enum class Flag { + STATUS_NONE, + STATUS_RENEW + }; + + explicit SessionTicketAppData(SSL_SESSION* session) : session_(session) {} + bool Set(const uint8_t* data, size_t len); + bool Get(uint8_t** data, size_t* len) const; + + private: + bool set_ = false; + SSL_SESSION* session_; +}; + +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS +#endif // SRC_QUIC_NODE_QUIC_CRYPTO_H_ diff --git a/src/quic/node_quic_default_application.cc b/src/quic/node_quic_default_application.cc new file mode 100644 index 00000000000000..8f3f0349cf9673 --- /dev/null +++ b/src/quic/node_quic_default_application.cc @@ -0,0 +1,181 @@ +#include "debug_utils-inl.h" +#include "node_quic_buffer-inl.h" +#include "node_quic_default_application.h" +#include "node_quic_session-inl.h" +#include "node_quic_socket-inl.h" +#include "node_quic_stream-inl.h" +#include "node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include + +#include + +namespace node { +namespace quic { + +namespace { +void Consume(ngtcp2_vec** pvec, size_t* pcnt, size_t len) { + ngtcp2_vec* v = *pvec; + size_t cnt = *pcnt; + + for (; cnt > 0; --cnt, ++v) { + if (v->len > len) { + v->len -= len; + v->base += len; + break; + } + len -= v->len; + } + + *pvec = v; + *pcnt = cnt; +} + +int IsEmpty(const ngtcp2_vec* vec, size_t cnt) { + size_t i; + for (i = 0; i < cnt && vec[i].len == 0; ++i) {} + return i == cnt; +} +} // anonymous namespace + +DefaultApplication::DefaultApplication( + QuicSession* session) : + QuicApplication(session) {} + +bool DefaultApplication::Initialize() { + if (needs_init()) { + Debug(session(), "Default QUIC Application Initialized"); + set_init_done(); + } + return needs_init(); +} + +void DefaultApplication::ScheduleStream(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + Debug(session(), "Scheduling stream %" PRIu64, stream_id); + if (LIKELY(stream)) + stream->Schedule(&stream_queue_); +} + +void DefaultApplication::UnscheduleStream(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + Debug(session(), "Unscheduling stream %" PRIu64, stream_id); + if (LIKELY(stream)) + stream->Unschedule(); +} + +void DefaultApplication::StreamClose( + int64_t stream_id, + uint64_t app_error_code) { + if (!session()->HasStream(stream_id)) + return; + if (app_error_code == 0) + app_error_code = NGTCP2_APP_NOERROR; + UnscheduleStream(stream_id); + QuicApplication::StreamClose(stream_id, app_error_code); +} + +void DefaultApplication::ResumeStream(int64_t stream_id) { + Debug(session(), "Stream %" PRId64 " has data to send", stream_id); + ScheduleStream(stream_id); +} + +bool DefaultApplication::ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + // Ensure that the QuicStream exists before deferring to + // QuicApplication specific processing logic. + Debug(session(), "Default QUIC Application receiving stream data"); + BaseObjectPtr stream = session()->FindStream(stream_id); + if (!stream) { + // Shutdown the stream explicitly if the session is being closed. + if (session()->is_gracefully_closing()) { + session()->ResetStream(stream_id, NGTCP2_ERR_CLOSING); + return true; + } + + // One potential DOS attack vector is to send a bunch of + // empty stream frames to commit resources. Check that + // here. Essentially, we only want to create a new stream + // if the datalen is greater than 0, otherwise, we ignore + // the packet. ngtcp2 should be handling this for us, + // but we handle it just to be safe. + if (UNLIKELY(datalen == 0)) + return true; + + stream = session()->CreateStream(stream_id); + } + CHECK(stream); + + stream->ReceiveData(fin, data, datalen, offset); + return true; +} + +int DefaultApplication::GetStreamData(StreamData* stream_data) { + QuicStream* stream = stream_queue_.PopFront(); + // If stream is nullptr, there are no streams with data pending. + if (stream == nullptr) + return 0; + + stream_data->stream.reset(stream); + stream_data->id = stream->id(); + + auto next = [&]( + int status, + const ngtcp2_vec* data, + size_t count, + bob::Done done) { + switch (status) { + case bob::Status::STATUS_BLOCK: + // Fall through + case bob::Status::STATUS_WAIT: + // Fall through + case bob::Status::STATUS_EOS: + return; + case bob::Status::STATUS_END: + stream_data->fin = 1; + } + stream_data->count = count; + + if (count > 0) { + stream->Schedule(&stream_queue_); + stream_data->remaining = get_length(data, count); + } else { + stream_data->remaining = 0; + } + }; + + if (LIKELY(!stream->is_eos())) { + CHECK_GE(stream->Pull( + std::move(next), + bob::Options::OPTIONS_SYNC, + stream_data->data, + arraysize(stream_data->data), + kMaxVectorCount), 0); + } + + return 0; +} + +bool DefaultApplication::StreamCommit( + StreamData* stream_data, + size_t datalen) { + CHECK(stream_data->stream); + stream_data->remaining -= datalen; + Consume(&stream_data->buf, &stream_data->count, datalen); + stream_data->stream->Commit(datalen); + return true; +} + +bool DefaultApplication::ShouldSetFin(const StreamData& stream_data) { + if (!stream_data.stream || + !IsEmpty(stream_data.buf, stream_data.count)) + return false; + return !stream_data.stream->is_writable(); +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_default_application.h b/src/quic/node_quic_default_application.h new file mode 100644 index 00000000000000..9fb5e050aa021f --- /dev/null +++ b/src/quic/node_quic_default_application.h @@ -0,0 +1,61 @@ +#ifndef SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ +#define SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_quic_stream.h" +#include "node_quic_session.h" +#include "node_quic_util.h" +#include "util.h" +#include "v8.h" + +namespace node { + +namespace quic { + +// The DefaultApplication is used whenever an unknown/unrecognized +// alpn identifier is used. It switches the QUIC implementation into +// a minimal/generic mode that defers all application level processing +// to the user-code level. Headers are not supported by QuicStream +// instances created under the default application. +class DefaultApplication final : public QuicApplication { + public: + explicit DefaultApplication(QuicSession* session); + + bool Initialize() override; + + void StopTrackingMemory(void* ptr) override { + // Do nothing. Not used. + } + + bool ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) override; + + int GetStreamData(StreamData* stream_data) override; + + void ResumeStream(int64_t stream_id) override; + void StreamClose(int64_t stream_id, uint64_t app_error_code) override; + bool ShouldSetFin(const StreamData& stream_data) override; + bool StreamCommit(StreamData* stream_data, size_t datalen) override; + + SET_SELF_SIZE(DefaultApplication) + SET_MEMORY_INFO_NAME(DefaultApplication) + SET_NO_MEMORY_INFO() + + private: + void ScheduleStream(int64_t stream_id); + void UnscheduleStream(int64_t stream_id); + + QuicStream::Queue stream_queue_; +}; + +} // namespace quic + +} // namespace node + +#endif // NODE_WANT_INTERNALS +#endif // SRC_QUIC_NODE_QUIC_DEFAULT_APPLICATION_H_ diff --git a/src/quic/node_quic_http3_application.cc b/src/quic/node_quic_http3_application.cc new file mode 100644 index 00000000000000..94b815ac7a732b --- /dev/null +++ b/src/quic/node_quic_http3_application.cc @@ -0,0 +1,937 @@ +#include "node.h" +#include "debug_utils-inl.h" +#include "node_mem-inl.h" +#include "node_quic_buffer-inl.h" +#include "node_quic_http3_application.h" +#include "node_quic_session-inl.h" +#include "node_quic_socket-inl.h" +#include "node_quic_stream-inl.h" +#include "node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "node_http_common-inl.h" + +#include +#include +#include +#include + +namespace node { + +using v8::Eternal; +using v8::MaybeLocal; +using v8::String; +using v8::Value; + +namespace quic { + +// nghttp3 uses a numeric identifier for a large number +// of known HTTP header names. These allow us to use +// static strings for those rather than allocating new +// strings all of the time. The list of strings supported +// is included in node_http_common.h +#define V1(name, value) case NGHTTP3_QPACK_TOKEN__##name: return value; +#define V2(name, value) case NGHTTP3_QPACK_TOKEN_##name: return value; +const char* Http3HeaderTraits::ToHttpHeaderName(int32_t token) { + switch (token) { + default: + // Fall through + case -1: return nullptr; + HTTP_SPECIAL_HEADERS(V1) + HTTP_REGULAR_HEADERS(V2) + } +} +#undef V1 +#undef V2 + +template +void Http3Application::SetConfig( + int idx, + M T::*member) { + AliasedFloat64Array& buffer = env()->quic_state()->http3config_buffer; + uint64_t flags = static_cast(buffer[IDX_HTTP3_CONFIG_COUNT]); + if (flags & (1ULL << idx)) + config_.*member = static_cast(buffer[idx]); +} + +Http3Application::Http3Application( + QuicSession* session) + : QuicApplication(session), + alloc_info_(MakeAllocator()) { + // Collect Configuration Details. + SetConfig(IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, + &Http3ApplicationConfig::qpack_max_table_capacity); + SetConfig(IDX_HTTP3_QPACK_BLOCKED_STREAMS, + &Http3ApplicationConfig::qpack_blocked_streams); + SetConfig(IDX_HTTP3_MAX_HEADER_LIST_SIZE, + &Http3ApplicationConfig::max_header_list_size); + SetConfig(IDX_HTTP3_MAX_PUSHES, + &Http3ApplicationConfig::max_pushes); + SetConfig(IDX_HTTP3_MAX_HEADER_PAIRS, + &Http3ApplicationConfig::max_header_pairs); + SetConfig(IDX_HTTP3_MAX_HEADER_LENGTH, + &Http3ApplicationConfig::max_header_length); + set_max_header_pairs( + session->is_server() + ? GetServerMaxHeaderPairs(config_.max_header_pairs) + : GetClientMaxHeaderPairs(config_.max_header_pairs)); + set_max_header_length(config_.max_header_length); + + session->env()->quic_state()->http3config_buffer[IDX_HTTP3_CONFIG_COUNT] = 0; +} + +// Push streams in HTTP/3 are a bit complicated. +// First, it's important to know that only an HTTP/3 server can +// create a push stream. +// Second, it's important to recognize that a push stream is +// essentially an *assumed* request. For instance, if a client +// requests a webpage that has links to css and js files, and +// the server expects the client to send subsequent requests +// for those css and js files, the server can shortcut the +// process by opening a push stream for each additional resource +// it assumes the client to make. +// Third, a push stream can only be opened within the context +// of an HTTP/3 request/response. Essentially, a server receives +// a request and while processing the response, the server can +// open one or more push streams. +// +// Now... a push stream consists of two components: a push promise +// and a push fulfillment. The push promise is sent *as part of +// the response on the original stream* and is assigned a push id +// and a block of headers containing the *assumed request headers*. +// The push promise is sent on the request/response bidirectional +// stream. +// The push fulfillment is a unidirectional stream opened by the +// server that contains the push id, the response header block, and +// the response payload. +// Here's where it can get a bit complicated: the server sends the +// push promise and the push fulfillment on two different, and +// independent QUIC streams. The push id is used to correlate +// those on the client side, but, it's entirely possible for the +// client to receive the push fulfillment before it actually receives +// the push promise. It's *unlikely*, but it's possible. Fortunately, +// nghttp3 handles the complexity of that for us internally but +// makes for some weird timing and could lead to some amount of +// buffering to occur. +// +// The *logical* order of events from the client side *should* +// be: (a) receive the push promise containing assumed request +// headers, (b) receive the push fulfillment containing the +// response headers followed immediately by the response payload. +// +// On the server side, the steps are: (a) first create the push +// promise creating the push_id then (b) open the unidirectional +// stream that will be used to fullfil the push promise. Once that +// unidirectional stream is created, the push id and unidirectional +// stream ID must be bound. The CreateAndBindPushStream handles (b) +int64_t Http3Application::CreateAndBindPushStream(int64_t push_id) { + CHECK(session()->is_server()); + int64_t stream_id; + if (!session()->OpenUnidirectionalStream(&stream_id)) + return 0; + return nghttp3_conn_bind_push_stream( + connection(), + push_id, + stream_id) == 0 ? stream_id : 0; +} + +bool Http3Application::SubmitPushPromise( + int64_t id, + int64_t* push_id, + int64_t* stream_id, + const Http3Headers& headers) { + // Successfully creating the push promise and opening the + // fulfillment stream will queue nghttp3 up to send data. + // Creating the SendSessionScope here ensures that when + // SubmitPush exits, SendPendingData will be called if + // we are not within the context of an ngtcp2 callback. + QuicSession::SendSessionScope send_scope(session()); + + Debug( + session(), + "Submitting %d push promise headers", + headers.length()); + if (nghttp3_conn_submit_push_promise( + connection(), + push_id, + id, + headers.data(), + headers.length()) != 0) { + return false; + } + // Once we've successfully submitting the push promise and have + // a push id assigned, we create the push fulfillment stream. + *stream_id = CreateAndBindPushStream(*push_id); + return *stream_id != 0; // push stream can never use stream id 0 +} + +bool Http3Application::SubmitInformation( + int64_t id, + const Http3Headers& headers) { + QuicSession::SendSessionScope send_scope(session()); + Debug( + session(), + "Submitting %d informational headers for stream %" PRId64, + headers.length(), + id); + return nghttp3_conn_submit_info( + connection(), + id, + headers.data(), + headers.length()) == 0; +} + +bool Http3Application::SubmitTrailers( + int64_t id, + const Http3Headers& headers) { + QuicSession::SendSessionScope send_scope(session()); + Debug( + session(), + "Submitting %d trailing headers for stream %" PRId64, + headers.length(), + id); + return nghttp3_conn_submit_trailers( + connection(), + id, + headers.data(), + headers.length()) == 0; +} + +bool Http3Application::SubmitHeaders( + int64_t id, + const Http3Headers& headers, + int32_t flags) { + QuicSession::SendSessionScope send_scope(session()); + static constexpr nghttp3_data_reader reader = { + Http3Application::OnReadData }; + const nghttp3_data_reader* reader_ptr = nullptr; + if (!(flags & QUICSTREAM_HEADER_FLAGS_TERMINAL)) + reader_ptr = &reader; + + switch (session()->crypto_context()->side()) { + case NGTCP2_CRYPTO_SIDE_CLIENT: + return nghttp3_conn_submit_request( + connection(), + id, + headers.data(), + headers.length(), + reader_ptr, + nullptr) == 0; + case NGTCP2_CRYPTO_SIDE_SERVER: + return nghttp3_conn_submit_response( + connection(), + id, + headers.data(), + headers.length(), + reader_ptr) == 0; + default: + UNREACHABLE(); + } +} + +// SubmitPush initiates a push stream by first creating a push promise +// with an associated push id, then opening the unidirectional stream +// that is used to fullfill it. Assuming both operations are successful, +// the QuicStream instance is created and added to the server QuicSession. +// +// The headers block passed to the submit push contains the assumed +// *request* headers. The response headers are provided using the +// SubmitHeaders() function on the created QuicStream. +BaseObjectPtr Http3Application::SubmitPush( + int64_t id, + v8::Local headers) { + // If the QuicSession is not a server session, return false + // immediately. Push streams cannot be sent by an HTTP/3 client. + if (!session()->is_server()) + return {}; + + Http3Headers nva(env(), headers); + int64_t push_id; + int64_t stream_id; + + // There are several reasons why push may fail. We currently handle + // them all the same. Later we might want to differentiate when the + // return value is NGHTTP3_ERR_PUSH_ID_BLOCKED. + return SubmitPushPromise(id, &push_id, &stream_id, nva) ? + QuicStream::New(session(), stream_id, push_id) : + BaseObjectPtr(); +} + +// Submit informational headers (response headers that use a 1xx +// status code). If the QuicSession is not a server session, return +// false immediately because info headers cannot be sent by a +// client +bool Http3Application::SubmitInformation( + int64_t stream_id, + v8::Local headers) { + if (!session()->is_server()) + return false; + Http3Headers nva(session()->env(), headers); + return SubmitInformation(stream_id, nva); +} + +// For client sessions, submits request headers. For server sessions, +// submits response headers. +bool Http3Application::SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) { + Http3Headers nva(session()->env(), headers); + return SubmitHeaders(stream_id, nva, flags); +} + +// Submits trailing headers for the HTTP/3 request or response. +bool Http3Application::SubmitTrailers( + int64_t stream_id, + v8::Local headers) { + Http3Headers nva(session()->env(), headers); + return SubmitTrailers(stream_id, nva); +} + +void Http3Application::CheckAllocatedSize(size_t previous_size) const { + CHECK_GE(current_nghttp3_memory_, previous_size); +} + +void Http3Application::IncreaseAllocatedSize(size_t size) { + current_nghttp3_memory_ += size; +} + +void Http3Application::DecreaseAllocatedSize(size_t size) { + current_nghttp3_memory_ -= size; +} + +void Http3Application::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackFieldWithSize("current_nghttp3_memory", + current_nghttp3_memory_); +} + +// Creates the underlying nghttp3 connection state for the session. +void Http3Application::CreateConnection() { + // nghttp3_conn_server_new and nghttp3_conn_client_new share + // identical definitions, so new_fn will work for both. + using new_fn = decltype(&nghttp3_conn_server_new); + static new_fn fns[] = { + nghttp3_conn_client_new, // NGTCP2_CRYPTO_SIDE_CLIENT + nghttp3_conn_server_new, // NGTCP2_CRYPTO_SIDE_SERVER + }; + + ngtcp2_crypto_side side = session()->crypto_context()->side(); + nghttp3_conn* conn; + + CHECK_EQ(fns[side]( + &conn, + &callbacks_[side], + &config_, + &alloc_info_, + this), 0); + CHECK_NOT_NULL(conn); + connection_.reset(conn); +} + +// The HTTP/3 QUIC binding uses a single unidirectional control +// stream in each direction to exchange frames impacting the entire +// connection. +bool Http3Application::CreateAndBindControlStream() { + if (!session()->OpenUnidirectionalStream(&control_stream_id_)) + return false; + Debug( + session(), + "Open stream %" PRId64 " and bind as control stream", + control_stream_id_); + return nghttp3_conn_bind_control_stream( + connection(), + control_stream_id_) == 0; +} + +// The HTTP/3 QUIC binding creates two unidirectional streams in +// each direction to exchange header compression details. +bool Http3Application::CreateAndBindQPackStreams() { + if (!session()->OpenUnidirectionalStream(&qpack_enc_stream_id_) || + !session()->OpenUnidirectionalStream(&qpack_dec_stream_id_)) { + return false; + } + Debug( + session(), + "Open streams %" PRId64 " and %" PRId64 " and bind as qpack streams", + qpack_enc_stream_id_, + qpack_dec_stream_id_); + return nghttp3_conn_bind_qpack_streams( + connection(), + qpack_enc_stream_id_, + qpack_dec_stream_id_) == 0; +} + +bool Http3Application::Initialize() { + if (!needs_init()) + return false; + + // The QuicSession must allow for at least three local unidirectional streams. + // This number is fixed by the http3 specification and represent the + // control stream and two qpack management streams. + if (session()->max_local_streams_uni() < 3) + return false; + + Debug(session(), "QPack Max Table Capacity: %" PRIu64, + config_.qpack_max_table_capacity); + Debug(session(), "QPack Blocked Streams: %" PRIu64, + config_.qpack_blocked_streams); + Debug(session(), "Max Header List Size: %" PRIu64, + config_.max_header_list_size); + Debug(session(), "Max Pushes: %" PRIu64, + config_.max_pushes); + + CreateConnection(); + Debug(session(), "HTTP/3 connection created"); + + ngtcp2_transport_params params; + session()->GetLocalTransportParams(¶ms); + + if (session()->is_server()) { + nghttp3_conn_set_max_client_streams_bidi( + connection(), + params.initial_max_streams_bidi); + } + + if (!CreateAndBindControlStream() || + !CreateAndBindQPackStreams()) { + return false; + } + + set_init_done(); + return true; +} + +// All HTTP/3 control, header, and stream data arrives as QUIC stream data. +// Here we pass the received data off to nghttp3 for processing. This will +// trigger the invocation of the various nghttp3 callbacks. +bool Http3Application::ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + Debug(session(), "Receiving %" PRIu64 " bytes for stream %" PRIu64 "%s", + datalen, stream_id, fin == 1 ? " (fin)" : ""); + ssize_t nread = + nghttp3_conn_read_stream( + connection(), stream_id, data, datalen, fin); + if (nread < 0) { + Debug(session(), "Failure to read HTTP/3 Stream Data [%" PRId64 "]", nread); + return false; + } + + return true; +} + +// This is the QUIC-level stream data acknowledgement. It is called for +// all streams, including unidirectional streams. This has to forward on +// to nghttp3 for processing. The Http3Application::AckedStreamData might +// be called as a result to acknowledge (and free) QuicStream data. +void Http3Application::AcknowledgeStreamData( + int64_t stream_id, + uint64_t offset, + size_t datalen) { + if (nghttp3_conn_add_ack_offset(connection(), stream_id, datalen) != 0) + Debug(session(), "Failure to acknowledge HTTP/3 Stream Data"); +} + +void Http3Application::StreamClose( + int64_t stream_id, + uint64_t app_error_code) { + if (app_error_code == 0) + app_error_code = NGTCP2_APP_NOERROR; + nghttp3_conn_close_stream(connection(), stream_id, app_error_code); + QuicApplication::StreamClose(stream_id, app_error_code); +} + +void Http3Application::StreamReset( + int64_t stream_id, + uint64_t app_error_code) { + nghttp3_conn_reset_stream(connection(), stream_id); + QuicApplication::StreamReset(stream_id, app_error_code); +} + +// When SendPendingData tries to send data for a given stream and there +// is no data to send but the QuicStream is still writable, it will +// be paused. When there's data available, the stream is resumed. +void Http3Application::ResumeStream(int64_t stream_id) { + nghttp3_conn_resume_stream(connection(), stream_id); +} + +// When stream data cannot be sent because of flow control, it is marked +// as being blocked. When the flow control windows expands, nghttp3 has +// to be told to unblock the stream so it knows to try sending data again. +void Http3Application::ExtendMaxStreamData( + int64_t stream_id, + uint64_t max_data) { + nghttp3_conn_unblock_stream(connection(), stream_id); +} + +// When stream data cannot be sent because of flow control, it is marked +// as being blocked. +bool Http3Application::BlockStream(int64_t stream_id) { + int err = nghttp3_conn_block_stream(connection(), stream_id); + if (err != 0) { + session()->set_last_error(QUIC_ERROR_APPLICATION, err); + return false; + } + return true; +} + +// nghttp3 keeps track of how much QuicStream data it has available and +// has sent. StreamCommit is called when a QuicPacket is serialized +// and updates nghttp3's internal state. +bool Http3Application::StreamCommit(StreamData* stream_data, size_t datalen) { + int err = nghttp3_conn_add_write_offset( + connection(), + stream_data->id, + datalen); + if (err != 0) { + session()->set_last_error(QUIC_ERROR_APPLICATION, err); + return false; + } + return true; +} + +// GetStreamData is called by SendPendingData to collect the QuicStream data +// that is to be packaged into a serialized QuicPacket. There may or may not +// be any stream data to send. The call to nghttp3_conn_writev_stream will +// provide any available stream data (if any). If nghttp3 is not sure if +// there is data to send, it will subsequently call Http3Application::ReadData +// to collect available data from the QuicStream. +int Http3Application::GetStreamData(StreamData* stream_data) { + ssize_t ret = 0; + if (connection() && session()->max_data_left()) { + ret = nghttp3_conn_writev_stream( + connection(), + &stream_data->id, + &stream_data->fin, + reinterpret_cast(stream_data->data), + sizeof(stream_data->data)); + if (ret < 0) + return static_cast(ret); + else + stream_data->remaining = stream_data->count = static_cast(ret); + } + if (stream_data->id > -1) { + Debug(session(), "Selected %" PRId64 " buffers for stream %" PRId64 "%s", + stream_data->count, + stream_data->id, + stream_data->fin == 1 ? " (fin)" : ""); + } + return 0; +} + +// Determines whether SendPendingData should set fin on the QuicStream +bool Http3Application::ShouldSetFin(const StreamData& stream_data) { + return stream_data.id > -1 && + !is_control_stream(stream_data.id) && + stream_data.fin == 1; +} + +// This is where nghttp3 pulls the data from the outgoing +// buffer to prepare it to be sent on the QUIC stream. +ssize_t Http3Application::ReadData( + int64_t stream_id, + nghttp3_vec* vec, + size_t veccnt, + uint32_t* pflags) { + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + + ssize_t ret = NGHTTP3_ERR_WOULDBLOCK; + + auto next = [&]( + int status, + const ngtcp2_vec* data, + size_t count, + bob::Done done) { + CHECK_LE(count, veccnt); + + switch (status) { + case bob::Status::STATUS_BLOCK: + // Fall through + case bob::Status::STATUS_WAIT: + // Fall through + case bob::Status::STATUS_EOS: + return; + case bob::Status::STATUS_END: + *pflags |= NGHTTP3_DATA_FLAG_EOF; + break; + } + + ret = count; + size_t numbytes = + nghttp3_vec_len( + reinterpret_cast(data), + count); + std::move(done)(numbytes); + + Debug(session(), "Sending %" PRIu64 " bytes for stream %" PRId64, + numbytes, stream_id); + }; + + CHECK_GE(stream->Pull( + std::move(next), + // Set OPTIONS_END here because nghttp3 takes over responsibility + // for ensuring the data all gets written out. + bob::Options::OPTIONS_END | bob::Options::OPTIONS_SYNC, + reinterpret_cast(vec), + veccnt, + kMaxVectorCount), 0); + + return ret; +} + +// Outgoing data is retained in memory until it is acknowledged. +void Http3Application::AckedStreamData(int64_t stream_id, size_t datalen) { + Acknowledge(stream_id, 0, datalen); +} + +void Http3Application::StreamClosed( + int64_t stream_id, + uint64_t app_error_code) { + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + stream->ReceiveData(1, nullptr, 0, 0); + session()->listener()->OnStreamClose(stream_id, app_error_code); +} + +BaseObjectPtr Http3Application::FindOrCreateStream( + int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + if (!stream) { + if (session()->is_gracefully_closing()) { + nghttp3_conn_close_stream(connection(), stream_id, NGTCP2_ERR_CLOSING); + return {}; + } + stream = session()->CreateStream(stream_id); + nghttp3_conn_set_stream_user_data(connection(), stream_id, stream.get()); + } + CHECK(stream); + return stream; +} + +void Http3Application::ReceiveData( + int64_t stream_id, + const uint8_t* data, + size_t datalen) { + FindOrCreateStream(stream_id)->ReceiveData(0, data, datalen, 0); +} + +void Http3Application::DeferredConsume( + int64_t stream_id, + size_t consumed) { + // Do nothing here for now. nghttp3 uses the on_deferred_consume + // callback to notify when stream data that had previously been + // deferred has been delivered to the application so that the + // stream data offset can be extended. However, we extend the + // data offset from within QuicStream when the data is delivered + // so we don't have to do it here. +} + +// Called when a nghttp3 detects that a new block of headers +// has been received. Http3Application::ReceiveHeader will +// be called for each name+value pair received, then +// Http3Application::EndHeaders will be called to finalize +// the header block. +void Http3Application::BeginHeaders( + int64_t stream_id, + QuicStreamHeadersKind kind) { + Debug(session(), "Starting header block for stream %" PRId64, stream_id); + FindOrCreateStream(stream_id)->BeginHeaders(kind); +} + +// As each header name+value pair is received, it is stored internally +// by the QuicStream until stream->EndHeaders() is called, during which +// the collected headers are converted to an array and passed off to +// the javascript side. +bool Http3Application::ReceiveHeader( + int64_t stream_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags) { + // Protect against zero-length headers (zero-length if either the + // name or value are zero-length). Such headers are simply ignored. + if (!Http3Header::IsZeroLength(name, value)) { + Debug(session(), "Receiving header for stream %" PRId64, stream_id); + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + if (token == NGHTTP3_QPACK_TOKEN__STATUS) { + nghttp3_vec vec = nghttp3_rcbuf_get_buf(value); + if (vec.base[0] == '1') + stream->set_headers_kind(QUICSTREAM_HEADERS_KIND_INFORMATIONAL); + else + stream->set_headers_kind(QUICSTREAM_HEADERS_KIND_INITIAL); + } + auto header = std::make_unique( + session()->env(), + token, + name, + value, + flags); + return stream->AddHeader(std::move(header)); + } + return true; +} + +// Marks the completion of a headers block. +void Http3Application::EndHeaders(int64_t stream_id, int64_t push_id) { + Debug(session(), "Ending header block for stream %" PRId64, stream_id); + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + stream->EndHeaders(); +} + +void Http3Application::CancelPush( + int64_t push_id, + int64_t stream_id) { + Debug(session(), "push stream canceled"); +} + +void Http3Application::PushStream( + int64_t push_id, + int64_t stream_id) { + Debug(session(), "Received push stream %" PRIu64 " (%" PRIu64 ")", + stream_id, push_id); +} + +void Http3Application::SendStopSending( + int64_t stream_id, + uint64_t app_error_code) { + session()->ResetStream(stream_id, app_error_code); +} + +void Http3Application::EndStream(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + stream->ReceiveData(1, nullptr, 0, 0); +} + +const nghttp3_conn_callbacks Http3Application::callbacks_[2] = { + // NGTCP2_CRYPTO_SIDE_CLIENT + { + OnAckedStreamData, + OnStreamClose, + OnReceiveData, + OnDeferredConsume, + OnBeginHeaders, + OnReceiveHeader, + OnEndHeaders, + OnBeginTrailers, // Begin Trailers + OnReceiveHeader, // Receive Trailer + OnEndHeaders, // End Trailers + OnBeginPushPromise, + OnReceivePushPromise, + OnEndPushPromise, + OnCancelPush, + OnSendStopSending, + OnPushStream, + OnEndStream + }, + // NGTCP2_CRYPTO_SIDE_SERVER + { + OnAckedStreamData, + OnStreamClose, + OnReceiveData, + OnDeferredConsume, + OnBeginHeaders, + OnReceiveHeader, + OnEndHeaders, + OnBeginTrailers, // Begin Trailers + OnReceiveHeader, // Receive Trailer + OnEndHeaders, // End Trailers + OnBeginPushPromise, + OnReceivePushPromise, + OnEndPushPromise, + OnCancelPush, + OnSendStopSending, + OnPushStream, + OnEndStream + } +}; + +int Http3Application::OnAckedStreamData( + nghttp3_conn* conn, + int64_t stream_id, + size_t datalen, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->AckedStreamData(stream_id, datalen); + return 0; +} + +int Http3Application::OnStreamClose( + nghttp3_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->StreamClosed(stream_id, app_error_code); + return 0; +} + +int Http3Application::OnReceiveData( + nghttp3_conn* conn, + int64_t stream_id, + const uint8_t* data, + size_t datalen, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->ReceiveData(stream_id, data, datalen); + return 0; +} + +int Http3Application::OnDeferredConsume( + nghttp3_conn* conn, + int64_t stream_id, + size_t consumed, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->DeferredConsume(stream_id, consumed); + return 0; +} + +int Http3Application::OnBeginHeaders( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->BeginHeaders(stream_id); + return 0; +} + +int Http3Application::OnBeginTrailers( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->BeginHeaders(stream_id, QUICSTREAM_HEADERS_KIND_TRAILING); + return 0; +} + +int Http3Application::OnReceiveHeader( + nghttp3_conn* conn, + int64_t stream_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + // TODO(@jasnell): Need to determine the appropriate response code here + // for when the header is not going to be accepted. + return app->ReceiveHeader(stream_id, token, name, value, flags) ? + 0 : NGHTTP3_ERR_CALLBACK_FAILURE; +} + +int Http3Application::OnEndHeaders( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->EndHeaders(stream_id); + return 0; +} + +int Http3Application::OnBeginPushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->BeginHeaders(stream_id, QUICSTREAM_HEADERS_KIND_PUSH); + return 0; +} + +int Http3Application::OnReceivePushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + if (!app->ReceiveHeader(stream_id, token, name, value, flags)) + return NGHTTP3_ERR_CALLBACK_FAILURE; + return 0; +} + +int Http3Application::OnEndPushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->EndHeaders(stream_id, push_id); + return 0; +} + +int Http3Application::OnCancelPush( + nghttp3_conn* conn, + int64_t push_id, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->CancelPush(push_id, stream_id); + return 0; +} + +int Http3Application::OnSendStopSending( + nghttp3_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->SendStopSending(stream_id, app_error_code); + return 0; +} + +int Http3Application::OnPushStream( + nghttp3_conn* conn, + int64_t push_id, + int64_t stream_id, + void* conn_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->PushStream(push_id, stream_id); + return 0; +} + +int Http3Application::OnEndStream( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + app->EndStream(stream_id); + return 0; +} + +ssize_t Http3Application::OnReadData( + nghttp3_conn* conn, + int64_t stream_id, + nghttp3_vec* vec, + size_t veccnt, + uint32_t* pflags, + void* conn_user_data, + void* stream_user_data) { + Http3Application* app = static_cast(conn_user_data); + return app->ReadData(stream_id, vec, veccnt, pflags); +} +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_http3_application.h b/src/quic/node_quic_http3_application.h new file mode 100644 index 00000000000000..1430b5f4f77054 --- /dev/null +++ b/src/quic/node_quic_http3_application.h @@ -0,0 +1,333 @@ +#ifndef SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ +#define SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" +#include "node_http_common.h" +#include "node_mem.h" +#include "node_quic_session.h" +#include "node_quic_stream-inl.h" +#include "node_quic_util.h" +#include "v8.h" +#include +#include + +#include +namespace node { + +namespace quic { + +constexpr uint64_t DEFAULT_QPACK_MAX_TABLE_CAPACITY = 4096; +constexpr uint64_t DEFAULT_QPACK_BLOCKED_STREAMS = 100; +constexpr size_t DEFAULT_MAX_HEADER_LIST_SIZE = 65535; +constexpr size_t DEFAULT_MAX_PUSHES = 65535; + +struct Http3RcBufferPointerTraits { + typedef nghttp3_rcbuf rcbuf_t; + typedef nghttp3_vec vector_t; + + static void inc(rcbuf_t* buf) { + nghttp3_rcbuf_incref(buf); + } + static void dec(rcbuf_t* buf) { + nghttp3_rcbuf_decref(buf); + } + static vector_t get_vec(const rcbuf_t* buf) { + return nghttp3_rcbuf_get_buf(buf); + } + static bool is_static(const rcbuf_t* buf) { + return nghttp3_rcbuf_is_static(buf); + } +}; + +struct Http3HeadersTraits { + typedef nghttp3_nv nv_t; + static const uint8_t kNoneFlag = NGHTTP3_NV_FLAG_NONE; +}; + +using Http3ConnectionPointer = DeleteFnPtr; +using Http3RcBufferPointer = NgRcBufPointer; +using Http3Headers = NgHeaders; + +struct Http3HeaderTraits { + typedef Http3RcBufferPointer rcbufferpointer_t; + typedef QuicApplication allocator_t; + + static const char* ToHttpHeaderName(int32_t token); +}; + +using Http3Header = NgHeader; + +struct Http3ApplicationConfig : public nghttp3_conn_settings { + Http3ApplicationConfig() { + nghttp3_conn_settings_default(this); + qpack_max_table_capacity = DEFAULT_QPACK_MAX_TABLE_CAPACITY; + qpack_blocked_streams = DEFAULT_QPACK_BLOCKED_STREAMS; + max_header_list_size = DEFAULT_MAX_HEADER_LIST_SIZE; + max_pushes = DEFAULT_MAX_PUSHES; + } + uint64_t max_header_pairs = DEFAULT_MAX_HEADER_LIST_PAIRS; + uint64_t max_header_length = DEFAULT_MAX_HEADER_LENGTH; +}; + +class Http3Application; +using Http3MemoryManager = + mem::NgLibMemoryManager; + +// Http3Application is used whenever the h3 alpn identifier is used. +// It causes the QuicSession to apply HTTP/3 semantics to the connection, +// including handling of headers and other HTTP/3 specific processing. +class Http3Application final : + public QuicApplication, + public Http3MemoryManager { + public: + explicit Http3Application(QuicSession* session); + + bool Initialize() override; + + void StopTrackingMemory(void* ptr) override { + Http3MemoryManager::StopTrackingMemory(ptr); + } + + bool ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) override; + + void AcknowledgeStreamData( + int64_t stream_id, + uint64_t offset, + size_t datalen) override; + + void StreamClose(int64_t stream_id, uint64_t app_error_code) override; + + void StreamReset( + int64_t stream_id, + uint64_t app_error_code) override; + + void ResumeStream(int64_t stream_id) override; + + void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) override; + + bool SubmitInformation( + int64_t stream_id, + v8::Local headers) override; + + bool SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) override; + + bool SubmitTrailers( + int64_t stream_id, + v8::Local headers) override; + + BaseObjectPtr SubmitPush( + int64_t id, + v8::Local headers) override; + + // Implementation for mem::NgLibMemoryManager + void CheckAllocatedSize(size_t previous_size) const; + void IncreaseAllocatedSize(size_t size); + void DecreaseAllocatedSize(size_t size); + + SET_SELF_SIZE(Http3Application) + SET_MEMORY_INFO_NAME(Http3Application) + void MemoryInfo(MemoryTracker* tracker) const override; + + private: + template + void SetConfig(int idx, M T::*member); + + nghttp3_conn* connection() const { return connection_.get(); } + BaseObjectPtr FindOrCreateStream(int64_t stream_id); + + bool CreateAndBindControlStream(); + bool CreateAndBindQPackStreams(); + int64_t CreateAndBindPushStream(int64_t push_id); + + int GetStreamData(StreamData* stream_data) override; + + bool BlockStream(int64_t stream_id) override; + bool StreamCommit(StreamData* stream_data, size_t datalen) override; + bool ShouldSetFin(const StreamData& data) override; + bool SubmitPushPromise( + int64_t id, + int64_t* push_id, + int64_t* stream_id, + const Http3Headers& headers); + bool SubmitInformation(int64_t id, const Http3Headers& headers); + bool SubmitTrailers(int64_t id, const Http3Headers& headers); + bool SubmitHeaders(int64_t id, const Http3Headers& headers, int32_t flags); + + ssize_t ReadData( + int64_t stream_id, + nghttp3_vec* vec, + size_t veccnt, + uint32_t* pflags); + + void AckedStreamData(int64_t stream_id, size_t datalen); + void StreamClosed(int64_t stream_id, uint64_t app_error_code); + void ReceiveData(int64_t stream_id, const uint8_t* data, size_t datalen); + void DeferredConsume(int64_t stream_id, size_t consumed); + void BeginHeaders( + int64_t stream_id, + QuicStreamHeadersKind kind = QUICSTREAM_HEADERS_KIND_NONE); + bool ReceiveHeader( + int64_t stream_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags); + void EndHeaders(int64_t stream_id, int64_t push_id = 0); + void CancelPush(int64_t push_id, int64_t stream_id); + void SendStopSending(int64_t stream_id, uint64_t app_error_code); + void PushStream(int64_t push_id, int64_t stream_id); + void EndStream(int64_t stream_id); + + bool is_control_stream(int64_t stream_id) const { + return stream_id == control_stream_id_ || + stream_id == qpack_dec_stream_id_ || + stream_id == qpack_enc_stream_id_; + } + + nghttp3_mem alloc_info_; + Http3ConnectionPointer connection_; + int64_t control_stream_id_; + int64_t qpack_enc_stream_id_; + int64_t qpack_dec_stream_id_; + size_t current_nghttp3_memory_ = 0; + + Http3ApplicationConfig config_; + + void CreateConnection(); + + static const nghttp3_conn_callbacks callbacks_[2]; + + static int OnAckedStreamData( + nghttp3_conn* conn, + int64_t stream_id, + size_t datalen, + void* conn_user_data, + void* stream_user_data); + + static int OnStreamClose( + nghttp3_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* conn_user_data, + void* stream_user_data); + + static int OnReceiveData( + nghttp3_conn* conn, + int64_t stream_id, + const uint8_t* data, + size_t datalen, + void* conn_user_data, + void* stream_user_data); + + static int OnDeferredConsume( + nghttp3_conn* conn, + int64_t stream_id, + size_t consumed, + void* conn_user_data, + void* stream_user_data); + + static int OnBeginHeaders( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data); + + static int OnBeginTrailers( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data); + + static int OnReceiveHeader( + nghttp3_conn* conn, + int64_t stream_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags, + void* conn_user_data, + void* stream_user_data); + + static int OnEndHeaders( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data); + + static int OnBeginPushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + void* conn_user_data, + void* stream_user_data); + + static int OnReceivePushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + int32_t token, + nghttp3_rcbuf* name, + nghttp3_rcbuf* value, + uint8_t flags, + void* conn_user_data, + void* stream_user_data); + + static int OnEndPushPromise( + nghttp3_conn* conn, + int64_t stream_id, + int64_t push_id, + void* conn_user_data, + void* stream_user_data); + + static int OnCancelPush( + nghttp3_conn* conn, + int64_t push_id, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data); + + static int OnSendStopSending( + nghttp3_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* conn_user_data, + void* stream_user_data); + + static int OnPushStream( + nghttp3_conn* conn, + int64_t push_id, + int64_t stream_id, + void* conn_user_data); + + static int OnEndStream( + nghttp3_conn* conn, + int64_t stream_id, + void* conn_user_data, + void* stream_user_data); + + static ssize_t OnReadData( + nghttp3_conn* conn, + int64_t stream_id, + nghttp3_vec* vec, + size_t veccnt, + uint32_t* pflags, + void* conn_user_data, + void* stream_user_data); +}; + +} // namespace quic + +} // namespace node + +#endif // NODE_WANT_INTERNALS +#endif // SRC_QUIC_NODE_QUIC_HTTP3_APPLICATION_H_ diff --git a/src/quic/node_quic_session-inl.h b/src/quic/node_quic_session-inl.h new file mode 100644 index 00000000000000..d7e5acbb769881 --- /dev/null +++ b/src/quic/node_quic_session-inl.h @@ -0,0 +1,614 @@ +#ifndef SRC_QUIC_NODE_QUIC_SESSION_INL_H_ +#define SRC_QUIC_NODE_QUIC_SESSION_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "debug_utils-inl.h" +#include "node_crypto.h" +#include "node_crypto_common.h" +#include "node_quic_crypto.h" +#include "node_quic_session.h" +#include "node_quic_socket-inl.h" +#include "node_quic_stream-inl.h" + +#include +#include +#include + +namespace node { + +namespace quic { + +void QuicSessionConfig::GenerateStatelessResetToken( + QuicSession* session, + const QuicCID& cid) { + transport_params.stateless_reset_token_present = 1; + StatelessResetToken token( + transport_params.stateless_reset_token, + session->socket()->session_reset_secret(), + cid); + Debug(session, "Generated stateless reset token %s for CID %s", token, cid); +} + +void QuicSessionConfig::GeneratePreferredAddressToken( + ConnectionIDStrategy connection_id_strategy, + QuicSession* session, + QuicCID* pscid) { + connection_id_strategy(session, pscid->cid(), kScidLen); + transport_params.preferred_address.cid = **pscid; + + StatelessResetToken( + transport_params.preferred_address.stateless_reset_token, + session->socket()->session_reset_secret(), + *pscid); +} + +void QuicSessionConfig::set_original_connection_id(const QuicCID& ocid) { + if (ocid) { + transport_params.original_connection_id = *ocid; + transport_params.original_connection_id_present = 1; + } +} + +void QuicSessionConfig::set_qlog(const ngtcp2_qlog_settings& qlog_) { + qlog = qlog_; +} + +QuicCryptoContext::QuicCryptoContext( + QuicSession* session, + BaseObjectPtr secure_context, + ngtcp2_crypto_side side, + uint32_t options) : + session_(session), + secure_context_(secure_context), + side_(side), + options_(options) { + ssl_.reset(SSL_new(secure_context_->ctx_.get())); + CHECK(ssl_); +} + +void QuicCryptoContext::Initialize() { + InitializeTLS(session(), ssl_); +} + +// Cancels and frees any remaining outbound handshake data +// at each crypto level. +uint64_t QuicCryptoContext::Cancel() { + uint64_t len = handshake_[0].Cancel(); + len += handshake_[1].Cancel(); + len += handshake_[2].Cancel(); + return len; +} + +v8::MaybeLocal QuicCryptoContext::ocsp_response() const { + Environment* env = session()->env(); + return crypto::GetSSLOCSPResponse( + env, + ssl_.get(), + v8::Undefined(env->isolate())); +} + +ngtcp2_crypto_level QuicCryptoContext::read_crypto_level() const { + return from_ossl_level(SSL_quic_read_level(ssl_.get())); +} + +ngtcp2_crypto_level QuicCryptoContext::write_crypto_level() const { + return from_ossl_level(SSL_quic_write_level(ssl_.get())); +} + +// TLS Keylogging is enabled per-QuicSession by attaching an handler to the +// "keylog" event. Each keylog line is emitted to JavaScript where it can +// be routed to whatever destination makes sense. Typically, this will be +// to a keylog file that can be consumed by tools like Wireshark to intercept +// and decrypt QUIC network traffic. +void QuicCryptoContext::Keylog(const char* line) { + if (UNLIKELY(session_->state_[IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED] == 1)) + session_->listener()->OnKeylog(line, strlen(line)); +} + +void QuicCryptoContext::OnClientHelloDone() { + // Continue the TLS handshake when this function exits + // otherwise it will stall and fail. + TLSHandshakeScope handshake(this, &in_client_hello_); + // Disable the callback at this point so we don't loop continuously + session_->state_[IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] = 0; +} + +// Following a pause in the handshake for OCSP or client hello, we kickstart +// the handshake again here by triggering ngtcp2 to serialize data. +void QuicCryptoContext::ResumeHandshake() { + // We haven't received any actual new handshake data but calling + // this will trigger the handshake to continue. + Receive(read_crypto_level(), 0, nullptr, 0); + session_->SendPendingData(); +} + +// For 0RTT, this sets the TLS session data from the given buffer. +bool QuicCryptoContext::set_session(crypto::SSLSessionPointer session) { + if (side_ == NGTCP2_CRYPTO_SIDE_CLIENT && session != nullptr) { + early_data_ = + SSL_SESSION_get_max_early_data(session.get()) == 0xffffffffUL; + } + return crypto::SetTLSSession(ssl_, std::move(session)); +} + +v8::MaybeLocal QuicCryptoContext::hello_ciphers() const { + return crypto::GetClientHelloCiphers(session()->env(), ssl_); +} + +v8::MaybeLocal QuicCryptoContext::cipher_name() const { + return crypto::GetCipherName(session()->env(), ssl_); +} + +v8::MaybeLocal QuicCryptoContext::cipher_version() const { + return crypto::GetCipherVersion(session()->env(), ssl_); +} + +const char* QuicCryptoContext::servername() const { + return crypto::GetServerName(ssl_.get()); +} + +const char* QuicCryptoContext::hello_alpn() const { + return crypto::GetClientHelloALPN(ssl_); +} + +const char* QuicCryptoContext::hello_servername() const { + return crypto::GetClientHelloServerName(ssl_); +} + +v8::MaybeLocal QuicCryptoContext::ephemeral_key() const { + return crypto::GetEphemeralKey(session()->env(), ssl_); +} + +v8::MaybeLocal QuicCryptoContext::peer_cert(bool abbreviated) const { + return crypto::GetPeerCert( + session()->env(), + ssl_, + abbreviated, + session()->is_server()); +} + +v8::MaybeLocal QuicCryptoContext::cert() const { + return crypto::GetCert(session()->env(), ssl_); +} + +std::string QuicCryptoContext::selected_alpn() const { + const unsigned char* alpn_buf = nullptr; + unsigned int alpnlen; + SSL_get0_alpn_selected(ssl_.get(), &alpn_buf, &alpnlen); + return alpnlen ? + std::string(reinterpret_cast(alpn_buf), alpnlen) : + std::string(); +} + +bool QuicCryptoContext::early_data() const { + return + (early_data_ && + SSL_get_early_data_status(ssl_.get()) == SSL_EARLY_DATA_ACCEPTED) || + SSL_get_max_early_data(ssl_.get()) == 0xffffffffUL; +} + +void QuicCryptoContext::set_tls_alert(int err) { + Debug(session(), "TLS Alert [%d]: %s", err, SSL_alert_type_string_long(err)); + session_->set_last_error(QuicError(QUIC_ERROR_CRYPTO, err)); +} + +// Derives and installs the initial keying material for a newly +// created session. +bool QuicCryptoContext::SetupInitialKey(const QuicCID& dcid) { + Debug(session(), "Deriving and installing initial keys"); + return DeriveAndInstallInitialKey(*session(), dcid); +} + +QuicApplication::QuicApplication(QuicSession* session) : session_(session) {} + +void QuicApplication::set_stream_fin(int64_t stream_id) { + BaseObjectPtr stream = session()->FindStream(stream_id); + CHECK(stream); + stream->set_fin_sent(); +} + +ssize_t QuicApplication::WriteVStream( + QuicPathStorage* path, + uint8_t* buf, + ssize_t* ndatalen, + const StreamData& stream_data) { + CHECK_LE(stream_data.count, kMaxVectorCount); + return ngtcp2_conn_writev_stream( + session()->connection(), + &path->path, + buf, + session()->max_packet_length(), + ndatalen, + stream_data.remaining > 0 ? + NGTCP2_WRITE_STREAM_FLAG_MORE : + NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_data.id, + stream_data.fin, + stream_data.buf, + stream_data.count, + uv_hrtime()); +} + +std::unique_ptr QuicApplication::CreateStreamDataPacket() { + return QuicPacket::Create( + "stream data", + session()->max_packet_length()); +} + +Environment* QuicApplication::env() const { + return session()->env(); +} + +// Every QUIC session will have multiple CIDs associated with it. +void QuicSession::AssociateCID(const QuicCID& cid) { + socket()->AssociateCID(cid, scid_); +} + +void QuicSession::DisassociateCID(const QuicCID& cid) { + if (is_server()) + socket()->DisassociateCID(cid); +} + +void QuicSession::StartHandshake() { + if (crypto_context_->is_handshake_started() || is_server()) + return; + crypto_context_->handshake_started(); + SendPendingData(); +} + +void QuicSession::ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) { + Debug(this, + "Extending max stream %" PRId64 " data to %" PRIu64, + stream_id, max_data); + application_->ExtendMaxStreamData(stream_id, max_data); +} + +void QuicSession::ExtendMaxStreamsRemoteUni(uint64_t max_streams) { + Debug(this, "Extend remote max unidirectional streams: %" PRIu64, + max_streams); + application_->ExtendMaxStreamsRemoteUni(max_streams); +} + +void QuicSession::ExtendMaxStreamsRemoteBidi(uint64_t max_streams) { + Debug(this, "Extend remote max bidirectional streams: %" PRIu64, + max_streams); + application_->ExtendMaxStreamsRemoteBidi(max_streams); +} + +void QuicSession::ExtendMaxStreamsUni(uint64_t max_streams) { + Debug(this, "Setting max unidirectional streams to %" PRIu64, max_streams); + state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI] = + static_cast(max_streams); +} + +void QuicSession::ExtendMaxStreamsBidi(uint64_t max_streams) { + Debug(this, "Setting max bidirectional streams to %" PRIu64, max_streams); + state_[IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI] = + static_cast(max_streams); +} + +// Extends the stream-level flow control by the given number of bytes. +void QuicSession::ExtendStreamOffset(int64_t stream_id, size_t amount) { + Debug(this, "Extending max stream %" PRId64 " offset by %" PRId64 " bytes", + stream_id, amount); + ngtcp2_conn_extend_max_stream_offset( + connection(), + stream_id, + amount); +} + +// Extends the connection-level flow control for the entire session by +// the given number of bytes. +void QuicSession::ExtendOffset(size_t amount) { + Debug(this, "Extending session offset by %" PRId64 " bytes", amount); + ngtcp2_conn_extend_max_offset(connection(), amount); +} + +// Copies the local transport params into the given struct for serialization. +void QuicSession::GetLocalTransportParams(ngtcp2_transport_params* params) { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + ngtcp2_conn_get_local_transport_params(connection(), params); +} + +// Gets the QUIC version negotiated for this QuicSession +uint32_t QuicSession::negotiated_version() const { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + return ngtcp2_conn_get_negotiated_version(connection()); +} + +// The HandshakeCompleted function is called by ngtcp2 once it +// determines that the TLS Handshake is done. The only thing we +// need to do at this point is let the javascript side know. +void QuicSession::HandshakeCompleted() { + RemoteTransportParamsDebug transport_params(this); + Debug(this, "Handshake is completed. %s", transport_params); + RecordTimestamp(&QuicSessionStats::handshake_completed_at); + if (is_server()) HandshakeConfirmed(); + listener()->OnHandshakeCompleted(); +} + +void QuicSession::HandshakeConfirmed() { + Debug(this, "Handshake is confirmed"); + RecordTimestamp(&QuicSessionStats::handshake_confirmed_at); + state_[IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED] = 1; +} + +bool QuicSession::is_handshake_completed() const { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + return ngtcp2_conn_get_handshake_completed(connection()); +} + +void QuicSession::InitApplication() { + Debug(this, "Initializing application handler for ALPN %s", + alpn().c_str() + 1); + application_->Initialize(); +} + +// When a QuicSession hits the idle timeout, it is to be silently and +// immediately closed without attempting to send any additional data to +// the peer. All existing streams are abandoned and closed. +void QuicSession::OnIdleTimeout() { + if (!is_flag_set(QUICSESSION_FLAG_DESTROYED)) { + state_[IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT] = 1; + Debug(this, "Idle timeout"); + SilentClose(); + } +} + +// Captures the error code and family information from a received +// connection close frame. +void QuicSession::GetConnectionCloseInfo() { + ngtcp2_connection_close_error_code close_code; + ngtcp2_conn_get_connection_close_error_code(connection(), &close_code); + set_last_error(QuicError(close_code)); +} + +// Removes the given connection id from the QuicSession. +void QuicSession::RemoveConnectionID(const QuicCID& cid) { + if (!is_flag_set(QUICSESSION_FLAG_DESTROYED)) + DisassociateCID(cid); +} + +QuicCID QuicSession::dcid() const { + return QuicCID(ngtcp2_conn_get_dcid(connection())); +} + +// The retransmit timer allows us to trigger retransmission +// of packets in case they are considered lost. The exact amount +// of time is determined internally by ngtcp2 according to the +// guidelines established by the QUIC spec but we use a libuv +// timer to actually monitor. Here we take the calculated timeout +// and extend out the libuv timer. +void QuicSession::UpdateRetransmitTimer(uint64_t timeout) { + DCHECK_NOT_NULL(retransmit_); + retransmit_->Update(timeout); +} + +void QuicSession::CheckAllocatedSize(size_t previous_size) const { + CHECK_GE(current_ngtcp2_memory_, previous_size); +} + +void QuicSession::IncreaseAllocatedSize(size_t size) { + current_ngtcp2_memory_ += size; +} + +void QuicSession::DecreaseAllocatedSize(size_t size) { + current_ngtcp2_memory_ -= size; +} + +uint64_t QuicSession::max_data_left() const { + return ngtcp2_conn_get_max_data_left(connection()); +} + +uint64_t QuicSession::max_local_streams_uni() const { + return ngtcp2_conn_get_max_local_streams_uni(connection()); +} + +void QuicSession::set_last_error(QuicError error) { + last_error_ = error; +} + +void QuicSession::set_last_error(int32_t family, uint64_t code) { + set_last_error({ family, code }); +} + +void QuicSession::set_last_error(int32_t family, int code) { + set_last_error({ family, code }); +} + +bool QuicSession::is_in_closing_period() const { + return ngtcp2_conn_is_in_closing_period(connection()); +} + +bool QuicSession::is_in_draining_period() const { + return ngtcp2_conn_is_in_draining_period(connection()); +} + +bool QuicSession::HasStream(int64_t id) const { + return streams_.find(id) != std::end(streams_); +} + +bool QuicSession::allow_early_data() const { + // TODO(@jasnell): For now, we always allow early data. + // Later there will be reasons we do not want to allow + // it, such as lack of available system resources. + return true; +} + +void QuicSession::SetSessionTicketAppData( + const SessionTicketAppData& app_data) { + application_->SetSessionTicketAppData(app_data); +} + +SessionTicketAppData::Status QuicSession::GetSessionTicketAppData( + const SessionTicketAppData& app_data, + SessionTicketAppData::Flag flag) { + return application_->GetSessionTicketAppData(app_data, flag); +} + +bool QuicSession::is_gracefully_closing() const { + return is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING); +} + +bool QuicSession::is_destroyed() const { + return is_flag_set(QUICSESSION_FLAG_DESTROYED); +} + +bool QuicSession::is_stateless_reset() const { + return is_flag_set(QUICSESSION_FLAG_STATELESS_RESET); +} + +bool QuicSession::is_server() const { + return crypto_context_->side() == NGTCP2_CRYPTO_SIDE_SERVER; +} + +void QuicSession::StartGracefulClose() { + set_flag(QUICSESSION_FLAG_GRACEFUL_CLOSING); + RecordTimestamp(&QuicSessionStats::closing_at); +} + +// The connection ID Strategy is a function that generates +// connection ID values. By default these are generated randomly. +void QuicSession::set_connection_id_strategy(ConnectionIDStrategy strategy) { + CHECK_NOT_NULL(strategy); + connection_id_strategy_ = strategy; +} + +void QuicSession::set_preferred_address_strategy( + PreferredAddressStrategy strategy) { + preferred_address_strategy_ = strategy; +} + +QuicSocket* QuicSession::socket() const { + return socket_.get(); +} + +// Indicates that the stream is blocked from transmitting any +// data. The specific handling of this is application specific. +// By default, we keep track of statistics but leave it up to +// the application to perform specific handling. +void QuicSession::StreamDataBlocked(int64_t stream_id) { + IncrementStat(&QuicSessionStats::block_count); + listener_->OnStreamBlocked(stream_id); +} + +// When a server advertises a preferred address in its initial +// transport parameters, ngtcp2 on the client side will trigger +// the OnSelectPreferredAdddress callback which will call this. +// The paddr argument contains the advertised preferred address. +// If the new address is going to be used, it needs to be copied +// over to dest, otherwise dest is left alone. There are two +// possible strategies that we currently support via user +// configuration: use the preferred address or ignore it. +void QuicSession::SelectPreferredAddress( + const QuicPreferredAddress& preferred_address) { + CHECK(!is_server()); + preferred_address_strategy_(this, preferred_address); +} + +// This variant of SendPacket is used by QuicApplication +// instances to transmit a packet and update the network +// path used at the same time. +bool QuicSession::SendPacket( + std::unique_ptr packet, + const ngtcp2_path_storage& path) { + UpdateEndpoint(path.path); + return SendPacket(std::move(packet)); +} + +void QuicSession::set_local_address(const ngtcp2_addr* addr) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + ngtcp2_conn_set_local_addr(connection(), addr); +} + +// Set the transport parameters received from the remote peer +void QuicSession::set_remote_transport_params() { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + ngtcp2_conn_get_remote_transport_params(connection(), &transport_params_); + set_flag(QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS); +} + +void QuicSession::StopIdleTimer() { + CHECK_NOT_NULL(idle_); + idle_->Stop(); +} + +void QuicSession::StopRetransmitTimer() { + CHECK_NOT_NULL(retransmit_); + retransmit_->Stop(); +} + +// Called by the OnVersionNegotiation callback when a version +// negotiation frame has been received by the client. The sv +// parameter is an array of versions supported by the remote peer. +void QuicSession::VersionNegotiation(const uint32_t* sv, size_t nsv) { + CHECK(!is_server()); + if (!is_flag_set(QUICSESSION_FLAG_DESTROYED)) + listener()->OnVersionNegotiation(NGTCP2_PROTO_VER, sv, nsv); +} + +// Every QUIC session has a remote address and local address. +// Those endpoints can change through the lifetime of a connection, +// so whenever a packet is successfully processed, or when a +// response is to be sent, we have to keep track of the path +// and update as we go. +void QuicSession::UpdateEndpoint(const ngtcp2_path& path) { + remote_address_.Update(path.remote.addr, path.remote.addrlen); + local_address_.Update(path.local.addr, path.local.addrlen); + + // If the updated remote address is IPv6, set the flow label + if (remote_address_.family() == AF_INET6) { + // TODO(@jasnell): Currently, this reuses the session reset secret. + // That may or may not be a good idea, we need to verify and may + // need to have a distinct secret for flow labels. + uint32_t flow_label = + GenerateFlowLabel( + local_address_, + remote_address_, + scid_, + socket()->session_reset_secret(), + NGTCP2_STATELESS_RESET_TOKENLEN); + remote_address_.set_flow_label(flow_label); + } +} + +// Submits information headers only if the selected application +// supports headers. +bool QuicSession::SubmitInformation( + int64_t stream_id, + v8::Local headers) { + return application_->SubmitInformation(stream_id, headers); +} + +// Submits initial headers only if the selected application +// supports headers. For http3, for instance, this is the +// method used to submit both request and response headers. +bool QuicSession::SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) { + return application_->SubmitHeaders(stream_id, headers, flags); +} + +// Submits trailing headers only if the selected application +// supports headers. +bool QuicSession::SubmitTrailers( + int64_t stream_id, + v8::Local headers) { + return application_->SubmitTrailers(stream_id, headers); +} + +// Submits a new push stream +BaseObjectPtr QuicSession::SubmitPush( + int64_t stream_id, + v8::Local headers) { + return application_->SubmitPush(stream_id, headers); +} + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_SESSION_INL_H_ diff --git a/src/quic/node_quic_session.cc b/src/quic/node_quic_session.cc new file mode 100644 index 00000000000000..a6f7b6df0427ea --- /dev/null +++ b/src/quic/node_quic_session.cc @@ -0,0 +1,3763 @@ +#include "node_quic_session-inl.h" // NOLINT(build/include) +#include "aliased_buffer.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "node_crypto_common.h" +#include "ngtcp2/ngtcp2.h" +#include "ngtcp2/ngtcp2_crypto.h" +#include "ngtcp2/ngtcp2_crypto_openssl.h" +#include "node.h" +#include "node_buffer.h" +#include "node_crypto.h" +#include "node_errors.h" +#include "node_internals.h" +#include "node_http_common-inl.h" +#include "node_mem-inl.h" +#include "node_process.h" +#include "node_quic_buffer-inl.h" +#include "node_quic_crypto.h" +#include "node_quic_socket-inl.h" +#include "node_quic_stream-inl.h" +#include "node_quic_state.h" +#include "node_quic_util-inl.h" +#include "node_quic_default_application.h" +#include "node_quic_http3_application.h" +#include "node_sockaddr-inl.h" +#include "v8.h" +#include "uv.h" + +#include +#include +#include +#include + +namespace node { + +using crypto::EntropySource; +using crypto::SecureContext; + +using v8::Array; +using v8::ArrayBufferView; +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Integer; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::PropertyAttribute; +using v8::String; +using v8::Undefined; +using v8::Value; + +namespace quic { + +namespace { +void SetConfig(Environment* env, int idx, uint64_t* val) { + AliasedFloat64Array& buffer = env->quic_state()->quicsessionconfig_buffer; + uint64_t flags = static_cast(buffer[IDX_QUIC_SESSION_CONFIG_COUNT]); + if (flags & (1ULL << idx)) + *val = static_cast(buffer[idx]); +} + +// Forwards detailed(verbose) debugging information from ngtcp2. Enabled using +// the NODE_DEBUG_NATIVE=NGTCP2_DEBUG category. +void Ngtcp2DebugLog(void* user_data, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + std::string format(fmt, strlen(fmt) + 1); + format[strlen(fmt)] = '\n'; + // Debug() does not work with the va_list here. So we use vfprintf + // directly instead. Ngtcp2DebugLog is only enabled when the debug + // category is enabled. + vfprintf(stderr, format.c_str(), ap); + va_end(ap); +} + +void CopyPreferredAddress( + uint8_t* dest, + size_t destlen, + uint16_t* port, + const sockaddr* addr) { + const sockaddr_in* src = reinterpret_cast(addr); + memcpy(dest, &src->sin_addr, destlen); + *port = SocketAddress::GetPort(addr); +} + +} // namespace + +std::string QuicSession::RemoteTransportParamsDebug::ToString() const { + ngtcp2_transport_params params; + ngtcp2_conn_get_remote_transport_params(session->connection(), ¶ms); + std::string out = "Remote Transport Params:\n"; + out += " Ack Delay Exponent: " + + std::to_string(params.ack_delay_exponent) + "\n"; + out += " Active Connection ID Limit: " + + std::to_string(params.active_connection_id_limit) + "\n"; + out += " Disable Active Migration: " + + std::string(params.disable_active_migration ? "Yes" : "No") + "\n"; + out += " Initial Max Data: " + + std::to_string(params.initial_max_data) + "\n"; + out += " Initial Max Stream Data Bidi Local: " + + std::to_string(params.initial_max_stream_data_bidi_local) + "\n"; + out += " Initial Max Stream Data Bidi Remote: " + + std::to_string(params.initial_max_stream_data_bidi_remote) + "\n"; + out += " Initial Max Stream Data Uni: " + + std::to_string(params.initial_max_stream_data_uni) + "\n"; + out += " Initial Max Streams Bidi: " + + std::to_string(params.initial_max_streams_bidi) + "\n"; + out += " Initial Max Streams Uni: " + + std::to_string(params.initial_max_streams_uni) + "\n"; + out += " Max Ack Delay: " + + std::to_string(params.max_ack_delay) + "\n"; + out += " Max Idle Timeout: " + + std::to_string(params.max_idle_timeout) + "\n"; + out += " Max Packet Size: " + + std::to_string(params.max_packet_size) + "\n"; + + if (!session->is_server()) { + if (params.original_connection_id_present) { + QuicCID cid(params.original_connection_id); + out += " Original Connection ID: " + cid.ToString() + "\n"; + } else { + out += " Original Connection ID: N/A \n"; + } + + if (params.preferred_address_present) { + out += " Preferred Address Present: Yes\n"; + // TODO(@jasnell): Serialize the IPv4 and IPv6 address options + } else { + out += " Preferred Address Present: No\n"; + } + + if (params.stateless_reset_token_present) { + StatelessResetToken token(params.stateless_reset_token); + out += " Stateless Reset Token: " + token.ToString() + "\n"; + } else { + out += " Stateless Reset Token: N/A"; + } + } + return out; +} + +void QuicSessionConfig::ResetToDefaults(Environment* env) { + ngtcp2_settings_default(this); + initial_ts = uv_hrtime(); + // Detailed(verbose) logging provided by ngtcp2 is only enabled + // when the NODE_DEBUG_NATIVE=NGTCP2_DEBUG category is used. + if (UNLIKELY(env->enabled_debug_list()->enabled( + DebugCategory::NGTCP2_DEBUG))) { + log_printf = Ngtcp2DebugLog; + } + transport_params.active_connection_id_limit = + DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + transport_params.initial_max_stream_data_bidi_local = + DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL; + transport_params.initial_max_stream_data_bidi_remote = + DEFAULT_MAX_STREAM_DATA_BIDI_REMOTE; + transport_params.initial_max_stream_data_uni = + DEFAULT_MAX_STREAM_DATA_UNI; + transport_params.initial_max_streams_bidi = + DEFAULT_MAX_STREAMS_BIDI; + transport_params.initial_max_streams_uni = + DEFAULT_MAX_STREAMS_UNI; + transport_params.initial_max_data = DEFAULT_MAX_DATA; + transport_params.max_idle_timeout = DEFAULT_MAX_IDLE_TIMEOUT; + transport_params.max_packet_size = + NGTCP2_MAX_PKT_SIZE; + transport_params.max_ack_delay = + NGTCP2_DEFAULT_MAX_ACK_DELAY; + transport_params.disable_active_migration = 0; + transport_params.preferred_address_present = 0; + transport_params.stateless_reset_token_present = 0; +} + +// Sets the QuicSessionConfig using an AliasedBuffer for efficiency. +void QuicSessionConfig::Set( + Environment* env, + const sockaddr* preferred_addr) { + ResetToDefaults(env); + SetConfig(env, IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, + &transport_params.active_connection_id_limit); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, + &transport_params.initial_max_stream_data_bidi_local); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, + &transport_params.initial_max_stream_data_bidi_remote); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, + &transport_params.initial_max_stream_data_uni); + SetConfig(env, IDX_QUIC_SESSION_MAX_DATA, + &transport_params.initial_max_data); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAMS_BIDI, + &transport_params.initial_max_streams_bidi); + SetConfig(env, IDX_QUIC_SESSION_MAX_STREAMS_UNI, + &transport_params.initial_max_streams_uni); + SetConfig(env, IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, + &transport_params.max_idle_timeout); + SetConfig(env, IDX_QUIC_SESSION_MAX_PACKET_SIZE, + &transport_params.max_packet_size); + SetConfig(env, IDX_QUIC_SESSION_MAX_ACK_DELAY, + &transport_params.max_ack_delay); + + transport_params.max_idle_timeout = + transport_params.max_idle_timeout * 1000000000; + + // TODO(@jasnell): QUIC allows both IPv4 and IPv6 addresses to be + // specified. Here we're specifying one or the other. Need to + // determine if that's what we want or should we support both. + if (preferred_addr != nullptr) { + transport_params.preferred_address_present = 1; + switch (preferred_addr->sa_family) { + case AF_INET: { + CopyPreferredAddress( + transport_params.preferred_address.ipv4_addr, + sizeof(transport_params.preferred_address.ipv4_addr), + &transport_params.preferred_address.ipv4_port, + preferred_addr); + break; + } + case AF_INET6: { + CopyPreferredAddress( + transport_params.preferred_address.ipv6_addr, + sizeof(transport_params.preferred_address.ipv6_addr), + &transport_params.preferred_address.ipv6_port, + preferred_addr); + break; + } + default: + UNREACHABLE(); + } + } +} + +void QuicSessionListener::OnKeylog(const char* line, size_t len) { + if (previous_listener_ != nullptr) + previous_listener_->OnKeylog(line, len); +} + +void QuicSessionListener::OnClientHello( + const char* alpn, + const char* server_name) { + if (previous_listener_ != nullptr) + previous_listener_->OnClientHello(alpn, server_name); +} + +QuicSessionListener::~QuicSessionListener() { + if (session_) + session_->RemoveListener(this); +} + +void QuicSessionListener::OnCert(const char* server_name) { + if (previous_listener_ != nullptr) + previous_listener_->OnCert(server_name); +} + +void QuicSessionListener::OnOCSP(Local ocsp) { + if (previous_listener_ != nullptr) + previous_listener_->OnOCSP(ocsp); +} + +void QuicSessionListener::OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id) { + if (previous_listener_ != nullptr) + previous_listener_->OnStreamHeaders(stream_id, kind, headers, push_id); +} + +void QuicSessionListener::OnStreamClose( + int64_t stream_id, + uint64_t app_error_code) { + if (previous_listener_ != nullptr) + previous_listener_->OnStreamClose(stream_id, app_error_code); +} + +void QuicSessionListener::OnStreamReset( + int64_t stream_id, + uint64_t app_error_code) { + if (previous_listener_ != nullptr) + previous_listener_->OnStreamReset(stream_id, app_error_code); +} + +void QuicSessionListener::OnSessionDestroyed() { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionDestroyed(); +} + +void QuicSessionListener::OnSessionClose(QuicError error) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionClose(error); +} + +void QuicSessionListener::OnStreamReady(BaseObjectPtr stream) { + if (previous_listener_ != nullptr) + previous_listener_->OnStreamReady(stream); +} + +void QuicSessionListener::OnHandshakeCompleted() { + if (previous_listener_ != nullptr) + previous_listener_->OnHandshakeCompleted(); +} + +void QuicSessionListener::OnPathValidation( + ngtcp2_path_validation_result res, + const sockaddr* local, + const sockaddr* remote) { + if (previous_listener_ != nullptr) + previous_listener_->OnPathValidation(res, local, remote); +} + +void QuicSessionListener::OnSessionTicket(int size, SSL_SESSION* session) { + if (previous_listener_ != nullptr) { + previous_listener_->OnSessionTicket(size, session); + } +} + +void QuicSessionListener::OnStreamBlocked(int64_t stream_id) { + if (previous_listener_ != nullptr) { + previous_listener_->OnStreamBlocked(stream_id); + } +} + +void QuicSessionListener::OnSessionSilentClose( + bool stateless_reset, + QuicError error) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionSilentClose(stateless_reset, error); +} + +void QuicSessionListener::OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address) { + if (previous_listener_ != nullptr) + previous_listener_->OnUsePreferredAddress(family, preferred_address); +} + +void QuicSessionListener::OnVersionNegotiation( + uint32_t supported_version, + const uint32_t* versions, + size_t vcnt) { + if (previous_listener_ != nullptr) + previous_listener_->OnVersionNegotiation(supported_version, versions, vcnt); +} + +void QuicSessionListener::OnQLog(const uint8_t* data, size_t len) { + if (previous_listener_ != nullptr) + previous_listener_->OnQLog(data, len); +} + +void JSQuicSessionListener::OnKeylog(const char* line, size_t len) { + Environment* env = session()->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local line_bf = Buffer::Copy(env, line, 1 + len).ToLocalChecked(); + char* data = Buffer::Data(line_bf); + data[len] = '\n'; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_keylog_function(), 1, &line_bf); +} + +void JSQuicSessionListener::OnStreamBlocked(int64_t stream_id) { + Environment* env = session()->env(); + + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + BaseObjectPtr stream = session()->FindStream(stream_id); + stream->MakeCallback(env->quic_on_stream_blocked_function(), 0, nullptr); +} + +void JSQuicSessionListener::OnClientHello( + const char* alpn, + const char* server_name) { + + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Undefined(env->isolate()), + Undefined(env->isolate()), + session()->crypto_context()->hello_ciphers().ToLocalChecked() + }; + + if (alpn != nullptr) { + argv[0] = String::NewFromUtf8( + env->isolate(), + alpn, + v8::NewStringType::kNormal).ToLocalChecked(); + } + if (server_name != nullptr) { + argv[1] = String::NewFromUtf8( + env->isolate(), + server_name, + v8::NewStringType::kNormal).ToLocalChecked(); + } + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_client_hello_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnCert(const char* server_name) { + Environment* env = session()->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local servername = Undefined(env->isolate()); + if (server_name != nullptr) { + servername = OneByteString( + env->isolate(), + server_name, + strlen(server_name)); + } + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_cert_function(), 1, &servername); +} + +void JSQuicSessionListener::OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + MaybeStackBuffer, 16> head(headers.size()); + size_t n = 0; + for (const auto& header : headers) { + // name and value should never be empty here, and if + // they are, there's an actual bug so go ahead and crash + Local pair[] = { + header->GetName(session()->application()).ToLocalChecked(), + header->GetValue(session()->application()).ToLocalChecked() + }; + head[n++] = Array::New(env->isolate(), pair, arraysize(pair)); + } + Local argv[] = { + Number::New(env->isolate(), static_cast(stream_id)), + Array::New(env->isolate(), head.out(), n), + Integer::New(env->isolate(), kind), + Undefined(env->isolate()) + }; + if (kind == QUICSTREAM_HEADERS_KIND_PUSH) + argv[3] = Number::New(env->isolate(), static_cast(push_id)); + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_headers_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnOCSP(Local ocsp) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + BaseObjectPtr ptr(session()); + session()->MakeCallback(env->quic_on_session_status_function(), 1, &ocsp); +} + +void JSQuicSessionListener::OnStreamClose( + int64_t stream_id, + uint64_t app_error_code) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(stream_id)), + Number::New(env->isolate(), static_cast(app_error_code)) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_close_function(), + arraysize(argv), + argv); +} + +void JSQuicSessionListener::OnStreamReset( + int64_t stream_id, + uint64_t app_error_code) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(stream_id)), + Number::New(env->isolate(), static_cast(app_error_code)) + }; + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_reset_function(), + arraysize(argv), + argv); +} + +void JSQuicSessionListener::OnSessionDestroyed() { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + // Emit the 'close' event in JS. This needs to happen after destroying the + // connection, because doing so also releases the last qlog data. + session()->MakeCallback( + env->quic_on_session_destroyed_function(), 0, nullptr); +} + +void JSQuicSessionListener::OnSessionClose(QuicError error) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + Number::New(env->isolate(), static_cast(error.code)), + Integer::New(env->isolate(), error.family) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_close_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnStreamReady(BaseObjectPtr stream) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local argv[] = { + stream->object(), + Number::New(env->isolate(), static_cast(stream->id())), + Number::New(env->isolate(), static_cast(stream->push_id())) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_stream_ready_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnHandshakeCompleted() { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + QuicCryptoContext* ctx = session()->crypto_context(); + Local servername = Undefined(env->isolate()); + const char* hostname = ctx->servername(); + if (hostname != nullptr) { + servername = + String::NewFromUtf8( + env->isolate(), + hostname, + v8::NewStringType::kNormal).ToLocalChecked(); + } + + int err = ctx->VerifyPeerIdentity( + hostname != nullptr ? + hostname : + session()->hostname().c_str()); + + Local argv[] = { + servername, + GetALPNProtocol(*session()), + ctx->cipher_name().ToLocalChecked(), + ctx->cipher_version().ToLocalChecked(), + Integer::New(env->isolate(), session()->max_pktlen_), + crypto::GetValidationErrorReason(env, err).ToLocalChecked(), + crypto::GetValidationErrorCode(env, err).ToLocalChecked(), + session()->crypto_context()->early_data() ? + v8::True(env->isolate()) : + v8::False(env->isolate()) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_handshake_function(), + arraysize(argv), + argv); +} + +void JSQuicSessionListener::OnPathValidation( + ngtcp2_path_validation_result res, + const sockaddr* local, + const sockaddr* remote) { + // This is a fairly expensive operation because both the local and + // remote addresses have to converted into JavaScript objects. We + // only do this if a pathValidation handler is registered. + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Local context = env->context(); + Context::Scope context_scope(context); + Local argv[] = { + Integer::New(env->isolate(), res), + AddressToJS(env, local), + AddressToJS(env, remote) + }; + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_path_validation_function(), + arraysize(argv), + argv); +} + +void JSQuicSessionListener::OnSessionTicket(int size, SSL_SESSION* sess) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + v8::Undefined(env->isolate()), + v8::Undefined(env->isolate()) + }; + + AllocatedBuffer session_ticket = env->AllocateManaged(size); + unsigned char* session_data = + reinterpret_cast(session_ticket.data()); + memset(session_data, 0, size); + i2d_SSL_SESSION(sess, &session_data); + if (!session_ticket.empty()) + argv[0] = session_ticket.ToBuffer().ToLocalChecked(); + + if (session()->is_flag_set( + QuicSession::QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS)) { + argv[1] = Buffer::Copy( + env, + reinterpret_cast(&session()->transport_params_), + sizeof(session()->transport_params_)).ToLocalChecked(); + } + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_ticket_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnSessionSilentClose( + bool stateless_reset, + QuicError error) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local argv[] = { + stateless_reset ? v8::True(env->isolate()) : v8::False(env->isolate()), + Number::New(env->isolate(), static_cast(error.code)), + Integer::New(env->isolate(), error.family) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_silent_close_function(), arraysize(argv), argv); +} + +void JSQuicSessionListener::OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Local context = env->context(); + Context::Scope context_scope(context); + + std::string hostname = family == AF_INET ? + preferred_address.preferred_ipv4_address(): + preferred_address.preferred_ipv6_address(); + int16_t port = + family == AF_INET ? + preferred_address.preferred_ipv4_port() : + preferred_address.preferred_ipv6_port(); + + Local argv[] = { + String::NewFromOneByte( + env->isolate(), + reinterpret_cast(hostname.c_str()), + v8::NewStringType::kNormal).ToLocalChecked(), + Integer::New(env->isolate(), port), + Integer::New(env->isolate(), family) + }; + + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_use_preferred_address_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnVersionNegotiation( + uint32_t supported_version, + const uint32_t* vers, + size_t vcnt) { + Environment* env = session()->env(); + HandleScope scope(env->isolate()); + Local context = env->context(); + Context::Scope context_scope(context); + + MaybeStackBuffer, 4> versions(vcnt); + for (size_t n = 0; n < vcnt; n++) + versions[n] = Integer::New(env->isolate(), vers[n]); + + // Currently, we only support one version of QUIC but in + // the future that may change. The callback below passes + // an array back to the JavaScript side to future-proof. + Local supported = + Integer::New(env->isolate(), supported_version); + + Local argv[] = { + Integer::New(env->isolate(), NGTCP2_PROTO_VER), + Array::New(env->isolate(), versions.out(), vcnt), + Array::New(env->isolate(), &supported, 1) + }; + + // Grab a shared pointer to this to prevent the QuicSession + // from being freed while the MakeCallback is running. + BaseObjectPtr ptr(session()); + session()->MakeCallback( + env->quic_on_session_version_negotiation_function(), + arraysize(argv), argv); +} + +void JSQuicSessionListener::OnQLog(const uint8_t* data, size_t len) { + Environment* env = session()->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + Local str = + String::NewFromOneByte(env->isolate(), + data, + v8::NewStringType::kNormal, + len).ToLocalChecked(); + + session()->MakeCallback(env->quic_on_session_qlog_function(), 1, &str); +} + +// Generates a new random connection ID. +void QuicSession::RandomConnectionIDStrategy( + QuicSession* session, + ngtcp2_cid* cid, + size_t cidlen) { + // CID min and max length is determined by the QUIC specification. + CHECK_LE(cidlen, NGTCP2_MAX_CIDLEN); + CHECK_GE(cidlen, NGTCP2_MIN_CIDLEN); + cid->datalen = cidlen; + // cidlen shouldn't ever be zero here but just in case that + // behavior changes in ngtcp2 in the future... + if (LIKELY(cidlen > 0)) + EntropySource(cid->data, cidlen); +} + +// Check required capabilities were not excluded from the OpenSSL build: +// - OPENSSL_NO_SSL_TRACE excludes SSL_trace() +// - OPENSSL_NO_STDIO excludes BIO_new_fp() +// HAVE_SSL_TRACE is available on the internal tcp_wrap binding for the tests. +#if defined(OPENSSL_NO_SSL_TRACE) || defined(OPENSSL_NO_STDIO) +# define HAVE_SSL_TRACE 0 +#else +# define HAVE_SSL_TRACE 1 +#endif + + +void QuicCryptoContext::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("initial_crypto", handshake_[0]); + tracker->TrackField("handshake_crypto", handshake_[1]); + tracker->TrackField("app_crypto", handshake_[2]); + tracker->TrackField("ocsp_response", ocsp_response_); +} + +bool QuicCryptoContext::SetSecrets( + ngtcp2_crypto_level level, + const uint8_t* rx_secret, + const uint8_t* tx_secret, + size_t secretlen) { + + static constexpr int kCryptoKeylen = 64; + static constexpr int kCryptoIvlen = 64; + static constexpr char kQuicClientEarlyTrafficSecret[] = + "QUIC_CLIENT_EARLY_TRAFFIC_SECRET"; + static constexpr char kQuicClientHandshakeTrafficSecret[] = + "QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + static constexpr char kQuicClientTrafficSecret0[] = + "QUIC_CLIENT_TRAFFIC_SECRET_0"; + static constexpr char kQuicServerHandshakeTrafficSecret[] = + "QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET"; + static constexpr char kQuicServerTrafficSecret[] = + "QUIC_SERVER_TRAFFIC_SECRET_0"; + + uint8_t rx_key[kCryptoKeylen]; + uint8_t rx_hp[kCryptoKeylen]; + uint8_t tx_key[kCryptoKeylen]; + uint8_t tx_hp[kCryptoKeylen]; + uint8_t rx_iv[kCryptoIvlen]; + uint8_t tx_iv[kCryptoIvlen]; + + if (NGTCP2_ERR(ngtcp2_crypto_derive_and_install_key( + session()->connection(), + ssl_.get(), + rx_key, + rx_iv, + rx_hp, + tx_key, + tx_iv, + tx_hp, + level, + rx_secret, + tx_secret, + secretlen, + side_))) { + return false; + } + + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: + crypto::LogSecret( + ssl_, + kQuicClientEarlyTrafficSecret, + rx_secret, + secretlen); + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + crypto::LogSecret( + ssl_, + kQuicClientHandshakeTrafficSecret, + rx_secret, + secretlen); + crypto::LogSecret( + ssl_, + kQuicServerHandshakeTrafficSecret, + tx_secret, + secretlen); + break; + case NGTCP2_CRYPTO_LEVEL_APP: + crypto::LogSecret( + ssl_, + kQuicClientTrafficSecret0, + rx_secret, + secretlen); + crypto::LogSecret( + ssl_, + kQuicServerTrafficSecret, + tx_secret, + secretlen); + break; + default: + UNREACHABLE(); + } + + return true; +} + +void QuicCryptoContext::AcknowledgeCryptoData( + ngtcp2_crypto_level level, + size_t datalen) { + // It is possible for the QuicSession to have been destroyed but not yet + // deconstructed. In such cases, we want to ignore the callback as there + // is nothing to do but wait for further cleanup to happen. + if (UNLIKELY(session_->is_destroyed())) + return; + Debug(session(), + "Acknowledging %d crypto bytes for %s level", + datalen, + crypto_level_name(level)); + + // Consumes (frees) the given number of bytes in the handshake buffer. + handshake_[level].Consume(datalen); + + // Update the statistics for the handshake, allowing us to track + // how long the handshake is taking to be acknowledged. A malicious + // peer could potentially force the QuicSession to hold on to + // crypto data for a long time by not sending an acknowledgement. + // The histogram will allow us to track the time periods between + // acknowlegements. + session()->RecordAck(&QuicSessionStats::handshake_acked_at); +} + +void QuicCryptoContext::EnableTrace() { +#if HAVE_SSL_TRACE + if (!bio_trace_) { + bio_trace_.reset(BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT)); + SSL_set_msg_callback( + ssl_.get(), + [](int write_p, + int version, + int content_type, + const void* buf, + size_t len, + SSL* ssl, + void* arg) -> void { + crypto::MarkPopErrorOnReturn mark_pop_error_on_return; + SSL_trace(write_p, version, content_type, buf, len, ssl, arg); + }); + SSL_set_msg_callback_arg(ssl_.get(), bio_trace_.get()); + } +#endif +} + +// If a 'clientHello' event listener is registered on the JavaScript +// QuicServerSession object, the STATE_CLIENT_HELLO_ENABLED state +// will be set and the OnClientHello will cause the 'clientHello' +// event to be emitted. +// +// The 'clientHello' callback will be given it's own callback function +// that must be called when the client has completed handling the event. +// The handshake will not continue until it is called. +// +// The intent here is to allow user code the ability to modify or +// replace the SecurityContext based on the server name, ALPN, or +// other handshake characteristics. +// +// The user can also set a 'cert' event handler that will be called +// when the peer certificate is received, allowing additional tweaks +// and verifications to be performed. +int QuicCryptoContext::OnClientHello() { + if (LIKELY(session_->state_[ + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED] == 0)) { + return 0; + } + + TLSCallbackScope callback_scope(this); + + // Not an error but does suspend the handshake until we're ready to go. + // A callback function is passed to the JavaScript function below that + // must be called in order to turn QUICSESSION_FLAG_CLIENT_HELLO_CB_RUNNING + // off. Once that callback is invoked, the TLS Handshake will resume. + // It is recommended that the user not take a long time to invoke the + // callback in order to avoid stalling out the QUIC connection. + if (in_client_hello_) + return -1; + in_client_hello_ = true; + + QuicCryptoContext* ctx = session_->crypto_context(); + session_->listener()->OnClientHello( + ctx->hello_alpn(), + ctx->hello_servername()); + + // Returning -1 here will keep the TLS handshake paused until the + // client hello callback is invoked. Returning 0 means that the + // handshake is ready to proceed. When the OnClientHello callback + // is called above, it may be resolved synchronously or asynchronously. + // In case it is resolved synchronously, we need the check below. + return in_client_hello_ ? -1 : 0; +} + +// The OnCert callback provides an opportunity to prompt the server to +// perform on OCSP request on behalf of the client (when the client +// requests it). If there is a listener for the 'OCSPRequest' event +// on the JavaScript side, the IDX_QUIC_SESSION_STATE_CERT_ENABLED +// session state slot will equal 1, which will cause the callback to +// be invoked. The callback will be given a reference to a JavaScript +// function that must be called in order for the TLS handshake to +// continue. +int QuicCryptoContext::OnOCSP() { + if (LIKELY(session_->state_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] == 0)) { + Debug(session(), "No OCSPRequest handler registered"); + return 1; + } + + Debug(session(), "Client is requesting an OCSP Response"); + TLSCallbackScope callback_scope(this); + + // As in node_crypto.cc, this is not an error, but does suspend the + // handshake to continue when OnOCSP is complete. + if (in_ocsp_request_) + return -1; + in_ocsp_request_ = true; + + session_->listener()->OnCert(session_->crypto_context()->servername()); + + // Returning -1 here means that we are still waiting for the OCSP + // request to be completed. When the OnCert handler is invoked + // above, it can be resolve synchronously or asynchonously. If + // resolved synchronously, we need the check below. + return in_ocsp_request_ ? -1 : 1; +} + +// The OnCertDone function is called by the QuicSessionOnCertDone +// function when usercode is done handling the OCSPRequest event. +void QuicCryptoContext::OnOCSPDone( + BaseObjectPtr context, + Local ocsp_response) { + Debug(session(), + "OCSPRequest completed. Context Provided? %s, OCSP Provided? %s", + context ? "Yes" : "No", + ocsp_response->IsArrayBufferView() ? "Yes" : "No"); + // Continue the TLS handshake when this function exits + // otherwise it will stall and fail. + TLSHandshakeScope handshake_scope(this, &in_ocsp_request_); + + // Disable the callback at this point so we don't loop continuously + session_->state_[IDX_QUIC_SESSION_STATE_CERT_ENABLED] = 0; + + if (context) { + int err = crypto::UseSNIContext(ssl_, context); + if (!err) { + unsigned long err = ERR_get_error(); // NOLINT(runtime/int) + return !err ? + THROW_ERR_QUIC_FAILURE_SETTING_SNI_CONTEXT(session_->env()) : + crypto::ThrowCryptoError(session_->env(), err); + } + } + + if (ocsp_response->IsArrayBufferView()) { + ocsp_response_.Reset( + session_->env()->isolate(), + ocsp_response.As()); + } +} + +// At this point in time, the TLS handshake secrets have been +// generated by openssl for this end of the connection and are +// ready to be used. Within this function, we need to install +// the secrets into the ngtcp2 connection object, store the +// remote transport parameters, and begin initialization of +// the QuicApplication that was selected. +bool QuicCryptoContext::OnSecrets( + ngtcp2_crypto_level level, + const uint8_t* rx_secret, + const uint8_t* tx_secret, + size_t secretlen) { + + auto maybe_init_app = OnScopeLeave([&]() { + if (level == NGTCP2_CRYPTO_LEVEL_APP) + session()->InitApplication(); + }); + + Debug(session(), + "Received secrets for %s crypto level", + crypto_level_name(level)); + + if (!SetSecrets(level, rx_secret, tx_secret, secretlen)) + return false; + + if (level == NGTCP2_CRYPTO_LEVEL_APP) + session_->set_remote_transport_params(); + + return true; +} + +// When the client has requested OSCP, this function will be called to provide +// the OSCP response. The OnCert() callback should have already been called +// by this point if any data is to be provided. If it hasn't, and ocsp_response_ +// is empty, no OCSP response will be sent. +int QuicCryptoContext::OnTLSStatus() { + Environment* env = session_->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + switch (side_) { + case NGTCP2_CRYPTO_SIDE_SERVER: { + if (ocsp_response_.IsEmpty()) { + Debug(session(), "There is no OCSP response"); + return SSL_TLSEXT_ERR_NOACK; + } + + Local obj = + PersistentToLocal::Default( + env->isolate(), + ocsp_response_); + size_t len = obj->ByteLength(); + + unsigned char* data = crypto::MallocOpenSSL(len); + obj->CopyContents(data, len); + + Debug(session(), "There is an OCSP response of %d bytes", len); + + if (!SSL_set_tlsext_status_ocsp_resp(ssl_.get(), data, len)) + OPENSSL_free(data); + + ocsp_response_.Reset(); + + return SSL_TLSEXT_ERR_OK; + } + case NGTCP2_CRYPTO_SIDE_CLIENT: { + Local res; + if (ocsp_response().ToLocal(&res)) + session_->listener()->OnOCSP(res); + return 1; + } + default: + UNREACHABLE(); + } +} + +// Called by ngtcp2 when a chunk of peer TLS handshake data is received. +// For every chunk, we move the TLS handshake further along until it +// is complete. +int QuicCryptoContext::Receive( + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t* data, + size_t datalen) { + if (UNLIKELY(session_->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + + // Statistics are collected so we can monitor how long the + // handshake is taking to operate and complete. + if (session_->GetStat(&QuicSessionStats::handshake_start_at) == 0) + session_->RecordTimestamp(&QuicSessionStats::handshake_start_at); + session_->RecordTimestamp(&QuicSessionStats::handshake_continue_at); + + Debug(session(), "Receiving %d bytes of crypto data", datalen); + + // Internally, this passes the handshake data off to openssl + // for processing. The handshake may or may not complete. + int ret = ngtcp2_crypto_read_write_crypto_data( + session_->connection(), + ssl_.get(), + crypto_level, + data, + datalen); + switch (ret) { + case 0: + return 0; + // In either of following cases, the handshake is being + // paused waiting for user code to take action (for instance + // OCSP requests or client hello modification) + case NGTCP2_CRYPTO_ERR_TLS_WANT_X509_LOOKUP: + Debug(session(), "TLS handshake wants X509 Lookup"); + return 0; + case NGTCP2_CRYPTO_ERR_TLS_WANT_CLIENT_HELLO_CB: + Debug(session(), "TLS handshake wants client hello callback"); + return 0; + default: + return ret; + } +} + +// Triggers key update to begin. This will fail and return false +// if either a previous key update is in progress and has not been +// confirmed or if the initial handshake has not yet been confirmed. +bool QuicCryptoContext::InitiateKeyUpdate() { + if (UNLIKELY(session_->is_destroyed())) + return false; + + // There's no user code that should be able to run while UpdateKey + // is running, but we need to gate on it just to be safe. + auto leave = OnScopeLeave([&]() { in_key_update_ = false; }); + CHECK(!in_key_update_); + in_key_update_ = true; + Debug(session(), "Initiating Key Update"); + + session_->IncrementStat(&QuicSessionStats::keyupdate_count); + + return ngtcp2_conn_initiate_key_update( + session_->connection(), + uv_hrtime()) == 0; +} + +int QuicCryptoContext::VerifyPeerIdentity(const char* hostname) { + int err = crypto::VerifyPeerCertificate(ssl_); + if (err) + return err; + + // QUIC clients are required to verify the peer identity, servers are not. + switch (side_) { + case NGTCP2_CRYPTO_SIDE_CLIENT: + if (LIKELY(is_option_set( + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY))) { + return VerifyHostnameIdentity(ssl_, hostname); + } + break; + case NGTCP2_CRYPTO_SIDE_SERVER: + // TODO(@jasnell): In the future, we may want to implement this but + // for now we keep things simple and skip peer identity verification. + break; + } + + return 0; +} + +// Write outbound TLS handshake data into the ngtcp2 connection +// to prepare it to be serialized. The outbound data must be +// stored in the handshake_ until it is acknowledged by the +// remote peer. It's important to keep in mind that there is +// a potential security risk here -- that is, a malicious peer +// can cause the local session to keep sent handshake data in +// memory by failing to acknowledge it or slowly acknowledging +// it. We currently do not track how much data is being buffered +// here but we do record statistics on how long the handshake +// data is foreced to be kept in memory. +void QuicCryptoContext::WriteHandshake( + ngtcp2_crypto_level level, + const uint8_t* data, + size_t datalen) { + Debug(session(), + "Writing %d bytes of %s handshake data.", + datalen, + crypto_level_name(level)); + std::unique_ptr buffer = + std::make_unique(datalen); + memcpy(buffer->out(), data, datalen); + session_->RecordTimestamp(&QuicSessionStats::handshake_send_at); + CHECK_EQ( + ngtcp2_conn_submit_crypto_data( + session_->connection(), + level, + buffer->out(), + datalen), 0); + handshake_[level].Push(std::move(buffer)); +} + +void QuicApplication::Acknowledge( + int64_t stream_id, + uint64_t offset, + size_t datalen) { + BaseObjectPtr stream = session()->FindStream(stream_id); + if (LIKELY(stream)) { + stream->Acknowledge(offset, datalen); + ResumeStream(stream_id); + } +} + +bool QuicApplication::SendPendingData() { + // The maximum number of packets to send per call + static constexpr size_t kMaxPackets = 16; + QuicPathStorage path; + std::unique_ptr packet; + uint8_t* pos = nullptr; + size_t packets_sent = 0; + int err; + + for (;;) { + ssize_t ndatalen; + StreamData stream_data; + err = GetStreamData(&stream_data); + if (err < 0) { + session()->set_last_error(QUIC_ERROR_APPLICATION, err); + return false; + } + + // If stream_data.id is -1, then we're not serializing any data for any + // specific stream. We still need to process QUIC session packets tho. + if (stream_data.id > -1) + Debug(session(), "Serializing packets for stream id %" PRId64, + stream_data.id); + else + Debug(session(), "Serializing session packets"); + + // If the packet was sent previously, then packet will have been reset. + if (!packet) { + packet = CreateStreamDataPacket(); + pos = packet->data(); + } + + ssize_t nwrite = WriteVStream(&path, pos, &ndatalen, stream_data); + + if (nwrite <= 0) { + switch (nwrite) { + case 0: + goto congestion_limited; + case NGTCP2_ERR_PKT_NUM_EXHAUSTED: + // There is a finite number of packets that can be sent + // per connection. Once those are exhausted, there's + // absolutely nothing we can do except immediately + // and silently tear down the QuicSession. This has + // to be silent because we can't even send a + // CONNECTION_CLOSE since even those require a + // packet number. + session()->SilentClose(); + return false; + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + session()->StreamDataBlocked(stream_data.id); + if (session()->max_data_left() == 0) + goto congestion_limited; + // Fall through + case NGTCP2_ERR_STREAM_SHUT_WR: + if (UNLIKELY(!BlockStream(stream_data.id))) + return false; + continue; + case NGTCP2_ERR_STREAM_NOT_FOUND: + continue; + case NGTCP2_ERR_WRITE_STREAM_MORE: + CHECK_GT(ndatalen, 0); + CHECK(StreamCommit(&stream_data, ndatalen)); + pos += ndatalen; + continue; + } + session()->set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); + return false; + } + + pos += nwrite; + + if (ndatalen >= 0) + CHECK(StreamCommit(&stream_data, ndatalen)); + + Debug(session(), "Sending %" PRIu64 " bytes in serialized packet", nwrite); + packet->set_length(nwrite); + if (!session()->SendPacket(std::move(packet), path)) + return false; + packet.reset(); + pos = nullptr; + MaybeSetFin(stream_data); + if (++packets_sent == kMaxPackets) + break; + } + return true; + + congestion_limited: + // We are either congestion limited or done. + if (pos - packet->data()) { + // Some data was serialized into the packet. We need to send it. + packet->set_length(pos - packet->data()); + Debug(session(), "Congestion limited, but %" PRIu64 " bytes pending", + packet->length()); + if (!session()->SendPacket(std::move(packet), path)) + return false; + } + return true; +} + +void QuicApplication::MaybeSetFin(const StreamData& stream_data) { + if (ShouldSetFin(stream_data)) + set_stream_fin(stream_data.id); +} + +void QuicApplication::StreamOpen(int64_t stream_id) { + Debug(session(), "QUIC Stream %" PRId64 " is open", stream_id); +} + +void QuicApplication::StreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id) { + session()->listener()->OnStreamHeaders(stream_id, kind, headers, push_id); +} + +void QuicApplication::StreamClose( + int64_t stream_id, + uint64_t app_error_code) { + session()->listener()->OnStreamClose(stream_id, app_error_code); +} + +void QuicApplication::StreamReset( + int64_t stream_id, + uint64_t app_error_code) { + session()->listener()->OnStreamReset(stream_id, app_error_code); +} + +// Determines which QuicApplication variant the QuicSession will be using +// based on the alpn configured for the application. For now, this is +// determined through configuration when tghe QuicSession is created +// and is not negotiable. In the future, we may allow it to be negotiated. +QuicApplication* QuicSession::SelectApplication(QuicSession* session) { + std::string alpn = session->alpn(); + if (alpn == NGTCP2_ALPN_H3) { + Debug(this, "Selecting HTTP/3 Application"); + return new Http3Application(session); + } + // In the future, we may end up supporting additional + // QUIC protocols. As they are added, extend the cases + // here to create and return them. + + Debug(this, "Selecting Default Application"); + return new DefaultApplication(session); +} + +// Server QuicSession Constructor +QuicSession::QuicSession( + QuicSocket* socket, + const QuicSessionConfig& config, + Local wrap, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn, + uint32_t options, + QlogMode qlog) + : QuicSession( + NGTCP2_CRYPTO_SIDE_SERVER, + socket, + wrap, + socket->server_secure_context(), + AsyncWrap::PROVIDER_QUICSERVERSESSION, + alpn, + std::string(""), // empty hostname. not used on server side + rcid, + options, + nullptr) { + // The config is copied by assignment in the call below. + InitServer(config, local_addr, remote_addr, dcid, ocid, version, qlog); +} + +// Client QuicSession Constructor +QuicSession::QuicSession( + QuicSocket* socket, + v8::Local wrap, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + Local dcid, + PreferredAddressStrategy preferred_address_strategy, + const std::string& alpn, + const std::string& hostname, + uint32_t options, + QlogMode qlog) + : QuicSession( + NGTCP2_CRYPTO_SIDE_CLIENT, + socket, + wrap, + secure_context, + AsyncWrap::PROVIDER_QUICCLIENTSESSION, + alpn, + hostname, + QuicCID(), + options, + preferred_address_strategy) { + InitClient( + local_addr, + remote_addr, + early_transport_params, + std::move(early_session_ticket), + dcid, + qlog); +} + +// QuicSession is an abstract base class that defines the code used by both +// server and client sessions. +QuicSession::QuicSession( + ngtcp2_crypto_side side, + QuicSocket* socket, + Local wrap, + BaseObjectPtr secure_context, + AsyncWrap::ProviderType provider_type, + const std::string& alpn, + const std::string& hostname, + const QuicCID& rcid, + uint32_t options, + PreferredAddressStrategy preferred_address_strategy) + : AsyncWrap(socket->env(), wrap, provider_type), + StatsBase(socket->env(), wrap, + HistogramOptions::ACK | + HistogramOptions::RATE), + alloc_info_(MakeAllocator()), + socket_(socket), + alpn_(alpn), + hostname_(hostname), + idle_(new Timer(socket->env(), [this]() { OnIdleTimeout(); })), + retransmit_(new Timer(socket->env(), [this]() { MaybeTimeout(); })), + rcid_(rcid), + state_(env()->isolate(), IDX_QUIC_SESSION_STATE_COUNT) { + PushListener(&default_listener_); + set_connection_id_strategy(RandomConnectionIDStrategy); + set_preferred_address_strategy(preferred_address_strategy); + crypto_context_.reset( + new QuicCryptoContext( + this, + secure_context, + side, + options)); + application_.reset(SelectApplication(this)); + + // TODO(@jasnell): For now, the following is a check rather than properly + // handled. Before this code moves out of experimental, this should be + // properly handled. + wrap->DefineOwnProperty( + env()->context(), + env()->state_string(), + state_.GetJSArray(), + PropertyAttribute::ReadOnly).Check(); + + // TODO(@jasnell): memory accounting + // env_->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); +} + +QuicSession::~QuicSession() { + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + crypto_context_->Cancel(); + connection_.reset(); + + QuicSessionListener* listener_ = listener(); + listener_->OnSessionDestroyed(); + if (listener_ == listener()) + RemoveListener(listener_); + + DebugStats(); +} + +template +void QuicSessionStatsTraits::ToString(const QuicSession& ptr, Fn&& add_field) { +#define V(_n, name, label) \ + add_field(label, ptr.GetStat(&QuicSessionStats::name)); + SESSION_STATS(V) +#undef V +} + +void QuicSession::PushListener(QuicSessionListener* listener) { + CHECK_NOT_NULL(listener); + CHECK(!listener->session_); + + listener->previous_listener_ = listener_; + listener->session_.reset(this); + + listener_ = listener; +} + +void QuicSession::RemoveListener(QuicSessionListener* listener) { + CHECK_NOT_NULL(listener); + + QuicSessionListener* previous; + QuicSessionListener* current; + + for (current = listener_, previous = nullptr; + /* No loop condition because we want a crash if listener is not found */ + ; previous = current, current = current->previous_listener_) { + CHECK_NOT_NULL(current); + if (current == listener) { + if (previous != nullptr) + previous->previous_listener_ = current->previous_listener_; + else + listener_ = listener->previous_listener_; + break; + } + } + + listener->session_.reset(); + listener->previous_listener_ = nullptr; +} + +// The diagnostic_name is used in Debug output. +std::string QuicSession::diagnostic_name() const { + return std::string("QuicSession ") + + (is_server() ? "Server" : "Client") + + " (" + alpn().substr(1) + ", " + + std::to_string(static_cast(get_async_id())) + ")"; +} + +// Locate the QuicStream with the given id or return nullptr +BaseObjectPtr QuicSession::FindStream(int64_t id) const { + auto it = streams_.find(id); + return it == std::end(streams_) ? BaseObjectPtr() : it->second; +} + +// Invoked when ngtcp2 receives an acknowledgement for stream data. +void QuicSession::AckedStreamDataOffset( + int64_t stream_id, + uint64_t offset, + size_t datalen) { + // It is possible for the QuicSession to have been destroyed but not yet + // deconstructed. In such cases, we want to ignore the callback as there + // is nothing to do but wait for further cleanup to happen. + if (LIKELY(!is_flag_set(QUICSESSION_FLAG_DESTROYED))) { + Debug(this, + "Received acknowledgement for %" PRIu64 + " bytes of stream %" PRId64 " data", + datalen, stream_id); + + application_->AcknowledgeStreamData(stream_id, offset, datalen); + } +} + +// Attaches the session to the given QuicSocket. The complexity +// here is that any CID's associated with the session have to +// be associated with the new QuicSocket. +void QuicSession::AddToSocket(QuicSocket* socket) { + CHECK_NOT_NULL(socket); + Debug(this, "Adding QuicSession to %s", socket->diagnostic_name()); + socket->AddSession(scid_, BaseObjectPtr(this)); + switch (crypto_context_->side()) { + case NGTCP2_CRYPTO_SIDE_SERVER: { + socket->AssociateCID(rcid_, scid_); + socket->AssociateCID(pscid_, scid_); + break; + } + case NGTCP2_CRYPTO_SIDE_CLIENT: { + std::vector cids(ngtcp2_conn_get_num_scid(connection())); + ngtcp2_conn_get_scid(connection(), cids.data()); + for (const ngtcp2_cid& cid : cids) { + socket->AssociateCID(QuicCID(&cid), scid_); + } + break; + } + default: + UNREACHABLE(); + } + + std::vector tokens( + ngtcp2_conn_get_num_active_dcid(connection())); + ngtcp2_conn_get_active_dcid(connection(), tokens.data()); + for (const ngtcp2_cid_token& token : tokens) { + if (token.token_present) { + socket->AssociateStatelessResetToken( + StatelessResetToken(token.token), + BaseObjectPtr(this)); + } + } +} + +// Add the given QuicStream to this QuicSession's collection of streams. All +// streams added must be removed before the QuicSession instance is freed. +void QuicSession::AddStream(BaseObjectPtr stream) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + Debug(this, "Adding stream %" PRId64 " to session", stream->id()); + streams_.emplace(stream->id(), stream); + + // Update tracking statistics for the number of streams associated with + // this session. + switch (stream->origin()) { + case QuicStreamOrigin::QUIC_STREAM_CLIENT: + if (is_server()) + IncrementStat(&QuicSessionStats::streams_in_count); + else + IncrementStat(&QuicSessionStats::streams_out_count); + break; + case QuicStreamOrigin::QUIC_STREAM_SERVER: + if (is_server()) + IncrementStat(&QuicSessionStats::streams_out_count); + else + IncrementStat(&QuicSessionStats::streams_in_count); + } + IncrementStat(&QuicSessionStats::streams_out_count); + switch (stream->direction()) { + case QuicStreamDirection::QUIC_STREAM_BIRECTIONAL: + IncrementStat(&QuicSessionStats::bidi_stream_count); + break; + case QuicStreamDirection::QUIC_STREAM_UNIDIRECTIONAL: + IncrementStat(&QuicSessionStats::uni_stream_count); + break; + } +} + +// Like the silent close, the immediate close must start with +// the JavaScript side, first shutting down any existing +// streams before entering the closing period. Unlike silent +// close, however, all streams are closed using proper +// STOP_SENDING and RESET_STREAM frames and a CONNECTION_CLOSE +// frame is ultimately sent to the peer. This makes the +// naming a bit of a misnomer in that the connection is +// not immediately torn down, but is allowed to drain +// properly per the QUIC spec description of "immediate close". +void QuicSession::ImmediateClose() { + // Calling either ImmediateClose or SilentClose will cause + // the QUICSESSION_FLAG_CLOSING to be set. In either case, + // we should never re-enter ImmediateClose or SilentClose. + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + set_flag(QUICSESSION_FLAG_CLOSING); + + QuicError err = last_error(); + Debug(this, "Immediate close with code %" PRIu64 " (%s)", + err.code, + err.family_name()); + + listener()->OnSessionClose(err); +} + +// Creates a new stream object and passes it off to the javascript side. +// This has to be called from within a handlescope/contextscope. +BaseObjectPtr QuicSession::CreateStream(int64_t stream_id) { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + CHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + + BaseObjectPtr stream = QuicStream::New(this, stream_id); + CHECK(stream); + listener()->OnStreamReady(stream); + return stream; +} + +// Mark the QuicSession instance destroyed. After this is called, +// the QuicSession instance will be generally unusable but most +// likely will not be immediately freed. +void QuicSession::Destroy() { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + // If we're not in the closing or draining periods, + // then we should at least attempt to send a connection + // close to the peer. + if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) && + !is_in_closing_period() && + !is_in_draining_period()) { + Debug(this, "Making attempt to send a connection close"); + set_last_error(); + SendConnectionClose(); + } + + // Streams should have already been destroyed by this point. + CHECK(streams_.empty()); + + // Mark the session destroyed. + set_flag(QUICSESSION_FLAG_DESTROYED); + set_flag(QUICSESSION_FLAG_CLOSING, false); + set_flag(QUICSESSION_FLAG_GRACEFUL_CLOSING, false); + + // Stop and free the idle and retransmission timers if they are active. + StopIdleTimer(); + StopRetransmitTimer(); + + // The QuicSession instances are kept alive using + // BaseObjectPtr. The only persistent BaseObjectPtr + // is the map in the associated QuicSocket. Removing + // the QuicSession from the QuicSocket will free + // that pointer, allowing the QuicSession to be + // deconstructed once the stack unwinds and any + // remaining shared_ptr instances fall out of scope. + RemoveFromSocket(); +} + +// Generates and associates a new connection ID for this QuicSession. +// ngtcp2 will call this multiple times at the start of a new connection +// in order to build a pool of available CIDs. +bool QuicSession::GetNewConnectionID( + ngtcp2_cid* cid, + uint8_t* token, + size_t cidlen) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + CHECK_NOT_NULL(connection_id_strategy_); + connection_id_strategy_(this, cid, cidlen); + QuicCID cid_(cid); + StatelessResetToken(token, socket()->session_reset_secret(), cid_); + AssociateCID(cid_); + return true; +} + +void QuicSession::HandleError() { + if (is_destroyed() || (is_in_closing_period() && !is_server())) + return; + + if (!SendConnectionClose()) { + set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_INTERNAL); + ImmediateClose(); + } +} + +// The the retransmit libuv timer fires, it will call MaybeTimeout, +// which determines whether or not we need to retransmit data to +// to packet loss or ack delay. +void QuicSession::MaybeTimeout() { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + uint64_t now = uv_hrtime(); + bool transmit = false; + if (ngtcp2_conn_loss_detection_expiry(connection()) <= now) { + Debug(this, "Retransmitting due to loss detection"); + CHECK_EQ(ngtcp2_conn_on_loss_detection_timer(connection(), now), 0); + IncrementStat(&QuicSessionStats::loss_retransmit_count); + transmit = true; + } else if (ngtcp2_conn_ack_delay_expiry(connection()) <= now) { + Debug(this, "Retransmitting due to ack delay"); + ngtcp2_conn_cancel_expired_ack_delay_timer(connection(), now); + IncrementStat(&QuicSessionStats::ack_delay_retransmit_count); + transmit = true; + } + if (transmit) + SendPendingData(); +} + +bool QuicSession::OpenBidirectionalStream(int64_t* stream_id) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + return ngtcp2_conn_open_bidi_stream(connection(), stream_id, nullptr) == 0; +} + +bool QuicSession::OpenUnidirectionalStream(int64_t* stream_id) { + DCHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + DCHECK(!is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)); + if (ngtcp2_conn_open_uni_stream(connection(), stream_id, nullptr)) + return false; + ngtcp2_conn_shutdown_stream_read(connection(), *stream_id, 0); + return true; +} + +// When ngtcp2 receives a successfull response to a PATH_CHALLENGE, +// it will trigger the OnPathValidation callback which will, in turn +// invoke this. There's really nothing to do here but update stats and +// and optionally notify the javascript side if there is a handler registered. +// Notifying the JavaScript side is purely informational. +void QuicSession::PathValidation( + const ngtcp2_path* path, + ngtcp2_path_validation_result res) { + if (res == NGTCP2_PATH_VALIDATION_RESULT_SUCCESS) { + IncrementStat(&QuicSessionStats::path_validation_success_count); + } else { + IncrementStat(&QuicSessionStats::path_validation_failure_count); + } + + // Only emit the callback if there is a handler for the pathValidation + // event on the JavaScript QuicSession object. + if (UNLIKELY(state_[IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED] == 1)) { + listener_->OnPathValidation( + res, + reinterpret_cast(path->local.addr), + reinterpret_cast(path->remote.addr)); + } +} + +// Calling Ping will trigger the ngtcp2_conn to serialize any +// packets it currently has pending along with a probe frame +// that should keep the connection alive. This is a fire and +// forget and any errors that may occur will be ignored. The +// idle_timeout and retransmit timers will be updated. If Ping +// is called while processing an ngtcp2 callback, or if the +// closing or draining period has started, this is a non-op. +void QuicSession::Ping() { + if (Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) || + is_flag_set(QUICSESSION_FLAG_DESTROYED) || + is_flag_set(QUICSESSION_FLAG_CLOSING) || + is_in_closing_period() || + is_in_draining_period()) { + return; + } + // TODO(@jasnell): We might want to revisit whether to handle + // errors right here. For now, we're ignoring them with the + // intent of capturing them elsewhere. + WritePackets("ping"); + UpdateIdleTimer(); + ScheduleRetransmit(); +} + +// A Retry will effectively restart the TLS handshake process +// by generating new initial crypto material. This should only ever +// be called on client sessions +bool QuicSession::ReceiveRetry() { + CHECK(!is_server()); + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + Debug(this, "A retry packet was received. Restarting the handshake"); + IncrementStat(&QuicSessionStats::retry_count); + return DeriveAndInstallInitialKey(*this, dcid()); +} + +// When the QuicSocket receives a QUIC packet, it is forwarded on to here +// for processing. +bool QuicSession::Receive( + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) { + Debug(this, "Ignoring packet because session is destroyed"); + return false; + } + + Debug(this, "Receiving QUIC packet"); + IncrementStat(&QuicSessionStats::bytes_received, nread); + + // Closing period starts once ngtcp2 has detected that the session + // is being shutdown locally. Note that this is different that the + // is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING) function, which + // indicates a graceful shutdown that allows the session and streams + // to finish naturally. When is_in_closing_period is true, ngtcp2 is + // actively in the process of shutting down the connection and a + // CONNECTION_CLOSE has already been sent. The only thing we can do + // at this point is either ignore the packet or send another + // CONNECTION_CLOSE. + if (is_in_closing_period()) { + Debug(this, "QUIC packet received while in closing period"); + IncrementConnectionCloseAttempts(); + // For server QuicSession instances, we serialize the connection close + // packet once but may sent it multiple times. If the client keeps + // transmitting, then the connection close may have gotten lost. + // We don't want to send the connection close in response to + // every received packet, however, so we use an exponential + // backoff, increasing the ratio of packets received to connection + // close frame sent with every one we send. + if (!ShouldAttemptConnectionClose()) { + Debug(this, "Not sending connection close"); + return false; + } + Debug(this, "Sending connection close"); + return SendConnectionClose(); + } + + // When is_in_draining_period is true, ngtcp2 has received a + // connection close and we are simply discarding received packets. + // No outbound packets may be sent. Return true here because + // the packet was correctly processed, even tho it is being + // ignored. + if (is_in_draining_period()) { + Debug(this, "QUIC packet received while in draining period"); + return true; + } + + // It's possible for the remote address to change from one + // packet to the next so we have to look at the addr on + // every packet. + remote_address_ = remote_addr; + QuicPath path(local_addr, remote_address_); + + { + // These are within a scope to ensure that the InternalCallbackScope + // and HandleScope are both exited before continuing on with the + // function. This allows any nextTicks and queued tasks to be processed + // before we continue. + auto update_stats = OnScopeLeave([&](){ + UpdateDataStats(); + }); + Debug(this, "Processing received packet"); + HandleScope handle_scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + if (!ReceivePacket(&path, data, nread)) { + Debug(this, "Failure processing received packet (code %" PRIu64 ")", + last_error().code); + HandleError(); + return false; + } + } + + if (is_destroyed()) { + Debug(this, "Session was destroyed while processing the received packet"); + // If the QuicSession has been destroyed but it is not + // in the closing period, a CONNECTION_CLOSE has not yet + // been sent to the peer. Let's attempt to send one. + if (!is_in_closing_period() && !is_in_draining_period()) { + Debug(this, "Attempting to send connection close"); + set_last_error(); + SendConnectionClose(); + } + return true; + } + + // Only send pending data if we haven't entered draining mode. + // We enter the draining period when a CONNECTION_CLOSE has been + // received from the remote peer. + if (is_in_draining_period()) { + Debug(this, "In draining period after processing packet"); + // If processing the packet puts us into draining period, there's + // absolutely nothing left for us to do except silently close + // and destroy this QuicSession. + GetConnectionCloseInfo(); + SilentClose(); + return true; + } + Debug(this, "Sending pending data after processing packet"); + SendPendingData(); + UpdateIdleTimer(); + UpdateRecoveryStats(); + Debug(this, "Successfully processed received packet"); + return true; +} + +// The ReceiveClientInitial function is called by ngtcp2 when +// a new connection has been initiated. The very first step to +// establishing a communication channel is to setup the keys +// that will be used to secure the communication. +bool QuicSession::ReceiveClientInitial(const QuicCID& dcid) { + if (UNLIKELY(is_flag_set(QUICSESSION_FLAG_DESTROYED))) + return false; + Debug(this, "Receiving client initial parameters"); + crypto_context_->handshake_started(); + return DeriveAndInstallInitialKey(*this, dcid); +} + +// Performs intake processing on a received QUIC packet. The received +// data is passed on to ngtcp2 for parsing and processing. ngtcp2 will, +// in turn, invoke a series of callbacks to handle the received packet. +bool QuicSession::ReceivePacket( + ngtcp2_path* path, + const uint8_t* data, + ssize_t nread) { + DCHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + + // If the QuicSession has been destroyed, we're not going + // to process any more packets for it. + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return true; + + uint64_t now = uv_hrtime(); + SetStat(&QuicSessionStats::received_at, now); + int err = ngtcp2_conn_read_pkt(connection(), path, data, nread, now); + if (err < 0) { + switch (err) { + // In either of the following two cases, the caller will + // handle the next steps... + case NGTCP2_ERR_DRAINING: + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + break; + default: + // Per ngtcp2: If NGTCP2_ERR_RETRY is returned, + // QuicSession must be a server and must perform + // address validation by sending a Retry packet + // then immediately close the connection. + if (err == NGTCP2_ERR_RETRY && is_server()) { + socket()->SendRetry(scid_, rcid_, local_address_, remote_address_); + ImmediateClose(); + break; + } + set_last_error(QUIC_ERROR_SESSION, err); + return false; + } + } + return true; +} + +// Called by ngtcp2 when a chunk of stream data has been received. If +// the stream does not yet exist, it is created, then the data is +// forwarded on. +bool QuicSession::ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + auto leave = OnScopeLeave([&]() { + // This extends the flow control window for the entire session + // but not for the individual Stream. Stream flow control is + // only expanded as data is read on the JavaScript side. + // TODO(jasnell): The strategy for extending the flow control + // window is going to be revisited soon. We don't need to + // extend on every chunk of data. + ExtendOffset(datalen); + }); + + // QUIC does not permit zero-length stream packets if + // fin is not set. ngtcp2 prevents these from coming + // through but just in case of regression in that impl, + // let's double check and simply ignore such packets + // so we do not commit any resources. + if (UNLIKELY(fin == 0 && datalen == 0)) + return true; + + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + + HandleScope scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + + // From here, we defer to the QuicApplication specific processing logic + return application_->ReceiveStreamData(stream_id, fin, data, datalen, offset); +} + +// Removes the QuicSession from the current socket. This is +// done with when the session is being destroyed or being +// migrated to another QuicSocket. It is important to keep in mind +// that the QuicSocket uses a BaseObjectPtr for the QuicSession. +// If the session is removed and there are no other references held, +// the session object will be destroyed automatically. +void QuicSession::RemoveFromSocket() { + CHECK(socket_); + Debug(this, "Removing QuicSession from %s", socket_->diagnostic_name()); + if (is_server()) { + socket_->DisassociateCID(rcid_); + socket_->DisassociateCID(pscid_); + } + + std::vector cids(ngtcp2_conn_get_num_scid(connection())); + std::vector tokens( + ngtcp2_conn_get_num_active_dcid(connection())); + ngtcp2_conn_get_scid(connection(), cids.data()); + ngtcp2_conn_get_active_dcid(connection(), tokens.data()); + + for (const ngtcp2_cid& cid : cids) + socket_->DisassociateCID(QuicCID(&cid)); + + for (const ngtcp2_cid_token& token : tokens) { + if (token.token_present) { + socket_->DisassociateStatelessResetToken( + StatelessResetToken(token.token)); + } + } + + Debug(this, "Removed from the QuicSocket"); + BaseObjectPtr socket = std::move(socket_); + socket->RemoveSession(scid_, remote_address_); +} + +// Removes the given stream from the QuicSession. All streams must +// be removed before the QuicSession is destroyed. +void QuicSession::RemoveStream(int64_t stream_id) { + Debug(this, "Removing stream %" PRId64, stream_id); + + // ngtcp2 does no extend the max streams count automatically + // except in very specific conditions, none of which apply + // once we've gotten this far. We need to manually extend when + // a remote peer initiated stream is removed. + if (!ngtcp2_conn_is_local_stream(connection_.get(), stream_id)) { + if (ngtcp2_is_bidi_stream(stream_id)) + ngtcp2_conn_extend_max_streams_bidi(connection_.get(), 1); + else + ngtcp2_conn_extend_max_streams_uni(connection_.get(), 1); + } + + // This will have the side effect of destroying the QuicStream + // instance. + streams_.erase(stream_id); + // Ensure that the stream state is closed and discarded by ngtcp2 + // Be sure to call this after removing the stream from the map + // above so that when ngtcp2 closes the stream, the callback does + // not attempt to loop back around and destroy the already removed + // QuicStream instance. Typically, the stream is already going to + // be closed by this point. + ngtcp2_conn_shutdown_stream(connection(), stream_id, NGTCP2_NO_ERROR); +} + +// The retransmit timer allows us to trigger retransmission +// of packets in case they are considered lost. The exact amount +// of time is determined internally by ngtcp2 according to the +// guidelines established by the QUIC spec but we use a libuv +// timer to actually monitor. +void QuicSession::ScheduleRetransmit() { + uint64_t now = uv_hrtime(); + uint64_t expiry = ngtcp2_conn_get_expiry(connection()); + uint64_t interval = (expiry - now) / 1000000UL; + if (expiry < now || interval == 0) interval = 1; + Debug(this, "Scheduling the retransmit timer for %" PRIu64, interval); + UpdateRetransmitTimer(interval); +} + +// Transmits either a protocol or application connection +// close to the peer. The choice of which is send is +// based on the current value of last_error_. +bool QuicSession::SendConnectionClose() { + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + + // Do not send any frames at all if we're in the draining period + // or in the middle of a silent close + if (is_in_draining_period() || is_flag_set(QUICSESSION_FLAG_SILENT_CLOSE)) + return true; + + // The specific handling of connection close varies for client + // and server QuicSession instances. For servers, we will + // serialize the connection close once but may end up transmitting + // it multiple times; whereas for clients, we will serialize it + // once and send once only. + QuicError error = last_error(); + + // If initial keys have not yet been installed, use the alternative + // ImmediateConnectionClose to send a stateless connection close to + // the peer. + if (crypto_context()->write_crypto_level() == + NGTCP2_CRYPTO_LEVEL_INITIAL) { + socket()->ImmediateConnectionClose( + dcid(), + scid_, + local_address_, + remote_address_, + error.code); + return true; + } + + switch (crypto_context_->side()) { + case NGTCP2_CRYPTO_SIDE_SERVER: { + // If we're not already in the closing period, + // first attempt to write any pending packets, then + // start the closing period. If closing period has + // already started, skip this. + if (!is_in_closing_period() && + (!WritePackets("server connection close - write packets") || + !StartClosingPeriod())) { + return false; + } + + UpdateIdleTimer(); + CHECK_GT(conn_closebuf_->length(), 0); + + return SendPacket(QuicPacket::Copy(conn_closebuf_)); + } + case NGTCP2_CRYPTO_SIDE_CLIENT: { + UpdateIdleTimer(); + auto packet = QuicPacket::Create("client connection close"); + + // If we're not already in the closing period, + // first attempt to write any pending packets, then + // start the closing period. Note that the behavior + // here is different than the server + if (!is_in_closing_period() && + !WritePackets("client connection close - write packets")) { + return false; + } + ssize_t nwrite = + SelectCloseFn(error.family)( + connection(), + nullptr, + packet->data(), + max_pktlen_, + error.code, + uv_hrtime()); + if (nwrite < 0) { + Debug(this, "Error writing connection close: %d", nwrite); + set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); + return false; + } + packet->set_length(nwrite); + return SendPacket(std::move(packet)); + } + default: + UNREACHABLE(); + } +} + +void QuicSession::IgnorePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address) { + Debug(session, "Ignoring server preferred address"); +} + +void QuicSession::UsePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address) { + static constexpr int idx = + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED; + int family = session->socket()->local_address().family(); + if (preferred_address.Use(family)) { + Debug(session, "Using server preferred address"); + // Emit only if the QuicSession has a usePreferredAddress handler + // on the JavaScript side. + if (UNLIKELY(session->state_[idx] == 1)) { + session->listener()->OnUsePreferredAddress(family, preferred_address); + } + } else { + // If Use returns false, the advertised preferred address does not + // match the current local preferred endpoint IP version. + Debug(session, + "Not using server preferred address due to IP version mismatch"); + } +} + +// Passes a serialized packet to the associated QuicSocket. +bool QuicSession::SendPacket(std::unique_ptr packet) { + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + CHECK(!is_in_draining_period()); + + // There's nothing to send. + if (packet->length() == 0) + return true; + + IncrementStat(&QuicSessionStats::bytes_sent, packet->length()); + RecordTimestamp(&QuicSessionStats::sent_at); + ScheduleRetransmit(); + + Debug(this, "Sending %" PRIu64 " bytes to %s from %s", + packet->length(), + remote_address_, + local_address_); + + int err = socket()->SendPacket( + local_address_, + remote_address_, + std::move(packet), + BaseObjectPtr(this)); + + if (err != 0) { + set_last_error(QUIC_ERROR_SESSION, err); + return false; + } + + return true; +} + +// Sends any pending handshake or session packet data. +void QuicSession::SendPendingData() { + // Do not proceed if: + // * We are in the ngtcp2 callback scope + // * The QuicSession has been destroyed + // * The QuicSession is in the draining period + // * The QuicSession is a server in the closing period + // * The QuicSession is not currently associated with a QuicSocket + if (Ngtcp2CallbackScope::InNgtcp2CallbackScope(this) || + is_destroyed() || + is_in_draining_period() || + (is_server() && is_in_closing_period()) || + socket() == nullptr) { + return; + } + + if (!application_->SendPendingData()) { + Debug(this, "Error sending QUIC application data"); + HandleError(); + } +} + +// When completing the TLS handshake, the TLS session information +// is provided to the QuicSession so that the session ticket and +// the remote transport parameters can be captured to support 0RTT +// session resumption. +int QuicSession::set_session(SSL_SESSION* session) { + CHECK(!is_server()); + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + int size = i2d_SSL_SESSION(session, nullptr); + if (size > SecureContext::kMaxSessionSize) + return 0; + listener_->OnSessionTicket(size, session); + return 1; +} + +// A client QuicSession can be migrated to a different QuicSocket instance. +// TODO(@jasnell): This will be revisited. +bool QuicSession::set_socket(QuicSocket* socket, bool nat_rebinding) { + CHECK(!is_server()); + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + + if (is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)) + return false; + + if (socket == nullptr || socket == socket_.get()) + return true; + + Debug(this, "Migrating to %s", socket->diagnostic_name()); + + SendSessionScope send(this); + + // Ensure that we maintain a reference to keep this from being + // destroyed while we are starting the migration. + BaseObjectPtr ptr(this); + + // Step 1: Remove the session from the current socket + RemoveFromSocket(); + + // Step 2: Add this Session to the given Socket + AddToSocket(socket); + + // Step 3: Update the internal references and make sure + // we are listening. + socket_.reset(socket); + socket->ReceiveStart(); + + // Step 4: Update ngtcp2 + auto& local_address = socket->local_address(); + if (nat_rebinding) { + ngtcp2_addr addr; + ngtcp2_addr_init( + &addr, + local_address.data(), + local_address.length(), + nullptr); + ngtcp2_conn_set_local_addr(connection(), &addr); + } else { + QuicPath path(local_address, remote_address_); + if (ngtcp2_conn_initiate_migration( + connection(), + &path, + uv_hrtime()) != 0) { + return false; + } + } + + return true; +} + +void QuicSession::ResumeStream(int64_t stream_id) { + application()->ResumeStream(stream_id); +} + +void QuicSession::ResetStream(int64_t stream_id, uint64_t code) { + SendSessionScope scope(this); + CHECK_EQ(ngtcp2_conn_shutdown_stream(connection(), stream_id, code), 0); +} + +// Silent Close must start with the JavaScript side, which must +// clean up state, abort any still existing QuicSessions, then +// destroy the handle when done. The most important characteristic +// of the SilentClose is that no frames are sent to the peer. +// +// When a valid stateless reset is received, the connection is +// immediately and unrecoverably closed at the ngtcp2 level. +// Specifically, it will be put into the draining_period so +// absolutely no frames can be sent. What we need to do is +// notify the JavaScript side and destroy the connection with +// a flag set that indicates stateless reset. +void QuicSession::SilentClose() { + // Calling either ImmediateClose or SilentClose will cause + // the QUICSESSION_FLAG_CLOSING to be set. In either case, + // we should never re-enter ImmediateClose or SilentClose. + CHECK(!is_flag_set(QUICSESSION_FLAG_CLOSING)); + set_flag(QUICSESSION_FLAG_SILENT_CLOSE); + set_flag(QUICSESSION_FLAG_CLOSING); + + QuicError err = last_error(); + Debug(this, + "Silent close with %s code %" PRIu64 " (stateless reset? %s)", + err.family_name(), + err.code, + is_stateless_reset() ? "yes" : "no"); + + listener()->OnSessionSilentClose(is_stateless_reset(), err); +} +// Begin connection close by serializing the CONNECTION_CLOSE packet. +// There are two variants: one to serialize an application close, the +// other to serialize a protocol close. The frames are generally +// identical with the exception of a bit in the header. On server +// QuicSessions, we serialize the frame once and may retransmit it +// multiple times. On client QuicSession instances, we only ever +// serialize the connection close once. +bool QuicSession::StartClosingPeriod() { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return false; + if (is_in_closing_period()) + return true; + + StopRetransmitTimer(); + UpdateIdleTimer(); + + QuicError error = last_error(); + Debug(this, "Closing period has started. Error %d", error.code); + + // Once the CONNECTION_CLOSE packet is written, + // is_in_closing_period will return true. + conn_closebuf_ = QuicPacket::Create( + "server connection close"); + ssize_t nwrite = + SelectCloseFn(error.family)( + connection(), + nullptr, + conn_closebuf_->data(), + max_pktlen_, + error.code, + uv_hrtime()); + if (nwrite < 0) { + if (nwrite == NGTCP2_ERR_PKT_NUM_EXHAUSTED) { + set_last_error(QUIC_ERROR_SESSION, NGTCP2_ERR_PKT_NUM_EXHAUSTED); + SilentClose(); + } else { + set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); + } + return false; + } + conn_closebuf_->set_length(nwrite); + return true; +} + +// Called by ngtcp2 when a stream has been closed. If the stream does +// not exist, the close is ignored. +void QuicSession::StreamClose(int64_t stream_id, uint64_t app_error_code) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + Debug(this, "Closing stream %" PRId64 " with code %" PRIu64, + stream_id, + app_error_code); + + application_->StreamClose(stream_id, app_error_code); +} + +// Called by ngtcp2 when a stream has been opened. All we do is log +// the activity here. We do not want to actually commit any resources +// until data is received for the stream. This allows us to prevent +// a stream commitment attack. The only exception is shutting the +// stream down explicitly if we are in a graceful close period. +void QuicSession::StreamOpen(int64_t stream_id) { + if (is_flag_set(QUICSESSION_FLAG_GRACEFUL_CLOSING)) { + ngtcp2_conn_shutdown_stream( + connection(), + stream_id, + NGTCP2_ERR_CLOSING); + } + Debug(this, "Stream %" PRId64 " opened", stream_id); + return application_->StreamOpen(stream_id); +} + +// Called when the QuicSession has received a RESET_STREAM frame from the +// peer, indicating that it will no longer send additional frames for the +// stream. If the stream is not yet known, reset is ignored. If the stream +// has already received a STREAM frame with fin set, the stream reset is +// ignored (the QUIC spec permits implementations to handle this situation +// however they want.) If the stream has not yet received a STREAM frame +// with the fin set, then the RESET_STREAM causes the readable side of the +// stream to be abruptly closed and any additional stream frames that may +// be received will be discarded if their offset is greater than final_size. +// On the JavaScript side, receiving a C is undistinguishable from +// a normal end-of-stream. No additional data events will be emitted, the +// end event will be emitted, and the readable side of the duplex will be +// closed. +// +// If the stream is still writable, no additional action is taken. If, +// however, the writable side of the stream has been closed (or was never +// open in the first place as in the case of peer-initiated unidirectional +// streams), the reset will cause the stream to be immediately destroyed. +void QuicSession::StreamReset( + int64_t stream_id, + uint64_t final_size, + uint64_t app_error_code) { + if (is_flag_set(QUICSESSION_FLAG_DESTROYED)) + return; + + Debug(this, + "Reset stream %" PRId64 " with code %" PRIu64 + " and final size %" PRIu64, + stream_id, + app_error_code, + final_size); + + BaseObjectPtr stream = FindStream(stream_id); + + if (stream) { + stream->set_final_size(final_size); + application_->StreamReset(stream_id, app_error_code); + } +} + +void QuicSession::UpdateConnectionID( + int type, + const QuicCID& cid, + const StatelessResetToken& token) { + switch (type) { + case NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE: + socket_->AssociateStatelessResetToken( + token, + BaseObjectPtr(this)); + break; + case NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE: + socket_->DisassociateStatelessResetToken(token); + break; + } +} + +// Updates the idle timer timeout. If the idle timer fires, the connection +// will be silently closed. It is important to update this as activity +// occurs to keep the idle timer from firing. +void QuicSession::UpdateIdleTimer() { + CHECK_NOT_NULL(idle_); + uint64_t now = uv_hrtime(); + uint64_t expiry = ngtcp2_conn_get_idle_expiry(connection()); + // nano to millis + uint64_t timeout = expiry > now ? (expiry - now) / 1000000ULL : 1; + if (timeout == 0) timeout = 1; + Debug(this, "Updating idle timeout to %" PRIu64, timeout); + idle_->Update(timeout); +} + + +// Write any packets current pending for the ngtcp2 connection based on +// the current state of the QuicSession. If the QuicSession is in the +// closing period, only CONNECTION_CLOSE packets may be written. If the +// QuicSession is in the draining period, no packets may be written. +// +// Packets are flushed to the underlying QuicSocket uv_udp_t as soon +// as they are written. The WritePackets method may cause zero or more +// packets to be serialized. +// +// If there are any acks or retransmissions pending, those will be +// serialized at this point as well. However, WritePackets does not +// serialize stream data that is being sent initially. +bool QuicSession::WritePackets(const char* diagnostic_label) { + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(this)); + CHECK(!is_flag_set(QUICSESSION_FLAG_DESTROYED)); + + // During the draining period, we must not send any frames at all. + if (is_in_draining_period()) + return true; + + // During the closing period, we are only permitted to send + // CONNECTION_CLOSE frames. + if (is_in_closing_period()) { + return SendConnectionClose(); + } + + // Otherwise, serialize and send pending frames + QuicPathStorage path; + for (;;) { + auto packet = QuicPacket::Create(diagnostic_label, max_pktlen_); + // ngtcp2_conn_write_pkt will fill the created QuicPacket up + // as much as possible, and then should be called repeatedly + // until it returns 0 or fatally errors. On each call, it + // will return the number of bytes encoded into the packet. + ssize_t nwrite = + ngtcp2_conn_write_pkt( + connection(), + &path.path, + packet->data(), + max_pktlen_, + uv_hrtime()); + if (nwrite <= 0) { + switch (nwrite) { + case 0: + return true; + case NGTCP2_ERR_PKT_NUM_EXHAUSTED: + // There is a finite number of packets that can be sent + // per connection. Once those are exhausted, there's + // absolutely nothing we can do except immediately + // and silently tear down the QuicSession. This has + // to be silent because we can't even send a + // CONNECTION_CLOSE since even those require a + // packet number. + SilentClose(); + return false; + default: + set_last_error(QUIC_ERROR_SESSION, static_cast(nwrite)); + return false; + } + } + + packet->set_length(nwrite); + UpdateEndpoint(path.path); + UpdateDataStats(); + + if (!SendPacket(std::move(packet))) + return false; + } +} + +void QuicSession::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("crypto_context", crypto_context_.get()); + tracker->TrackField("alpn", alpn_); + tracker->TrackField("hostname", hostname_); + tracker->TrackField("idle", idle_); + tracker->TrackField("retransmit", retransmit_); + tracker->TrackField("streams", streams_); + tracker->TrackField("state", state_); + tracker->TrackFieldWithSize("current_ngtcp2_memory", current_ngtcp2_memory_); + tracker->TrackField("conn_closebuf", conn_closebuf_); + tracker->TrackField("application", application_); + StatsBase::StatsMemoryInfo(tracker); +} + +// Static function to create a new server QuicSession instance +BaseObjectPtr QuicSession::CreateServer( + QuicSocket* socket, + const QuicSessionConfig& config, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn, + uint32_t options, + QlogMode qlog) { + Local obj; + if (!socket->env() + ->quicserversession_instance_template() + ->NewInstance(socket->env()->context()).ToLocal(&obj)) { + return {}; + } + BaseObjectPtr session = + MakeDetachedBaseObject( + socket, + config, + obj, + rcid, + local_addr, + remote_addr, + dcid, + ocid, + version, + alpn, + options, + qlog); + + session->AddToSocket(socket); + return session; +} + +// Initializes a newly created server QuicSession. +void QuicSession::InitServer( + QuicSessionConfig config, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + QlogMode qlog) { + + CHECK_NULL(connection_); + + ExtendMaxStreamsBidi(DEFAULT_MAX_STREAMS_BIDI); + ExtendMaxStreamsUni(DEFAULT_MAX_STREAMS_UNI); + + local_address_ = local_addr; + remote_address_ = remote_addr; + max_pktlen_ = GetMaxPktLen(remote_addr); + + config.set_original_connection_id(ocid); + + connection_id_strategy_(this, scid_.cid(), kScidLen); + + config.GenerateStatelessResetToken(this, scid_); + config.GeneratePreferredAddressToken(connection_id_strategy_, this, &pscid_); + + QuicPath path(local_addr, remote_address_); + + // NOLINTNEXTLINE(readability/pointer_notation) + if (qlog == QlogMode::kEnabled) config.set_qlog({ *ocid, OnQlogWrite }); + + ngtcp2_conn* conn; + CHECK_EQ( + ngtcp2_conn_server_new( + &conn, + dcid.cid(), + scid_.cid(), + &path, + version, + &callbacks[crypto_context_->side()], + &config, + &alloc_info_, + static_cast(this)), 0); + + connection_.reset(conn); + + crypto_context_->Initialize(); + UpdateDataStats(); + UpdateIdleTimer(); +} + +namespace { +// A pointer to this function is passed to the JavaScript side during +// the client hello and is called by user code when the TLS handshake +// should resume. +void QuicSessionOnClientHelloDone(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->crypto_context()->OnClientHelloDone(); +} + +// This callback is invoked by user code after completing handling +// of the 'OCSPRequest' event. The callback is invoked with two +// possible arguments, both of which are optional +// 1. A replacement SecureContext +// 2. An OCSP response +void QuicSessionOnCertDone(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + + Local cons = env->secure_context_constructor_template(); + crypto::SecureContext* context = nullptr; + if (args[0]->IsObject() && cons->HasInstance(args[0])) + context = Unwrap(args[0].As()); + session->crypto_context()->OnOCSPDone( + BaseObjectPtr(context), + args[1]); +} +} // namespace + +// Recovery stats are used to allow user code to keep track of +// important round-trip timing statistics that are updated through +// the lifetime of a connection. Effectively, these communicate how +// much time (from the perspective of the local peer) is being taken +// to exchange data reliably with the remote peer. +void QuicSession::UpdateRecoveryStats() { + const ngtcp2_rcvry_stat* stat = + ngtcp2_conn_get_rcvry_stat(connection()); + SetStat(&QuicSessionStats::min_rtt, stat->min_rtt); + SetStat(&QuicSessionStats::latest_rtt, stat->latest_rtt); + SetStat(&QuicSessionStats::smoothed_rtt, stat->smoothed_rtt); +} + +// Data stats are used to allow user code to keep track of important +// statistics such as amount of data in flight through the lifetime +// of a connection. +void QuicSession::UpdateDataStats() { + if (is_destroyed()) + return; + state_[IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT] = + static_cast(ngtcp2_conn_get_max_data_left(connection())); + size_t bytes_in_flight = ngtcp2_conn_get_bytes_in_flight(connection()); + state_[IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT] = + static_cast(bytes_in_flight); + // The max_bytes_in_flight is a highwater mark that can be used + // in performance analysis operations. + if (bytes_in_flight > GetStat(&QuicSessionStats::max_bytes_in_flight)) + SetStat(&QuicSessionStats::max_bytes_in_flight, bytes_in_flight); +} + +// Static method for creating a new client QuicSession instance. +BaseObjectPtr QuicSession::CreateClient( + QuicSocket* socket, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + Local dcid, + PreferredAddressStrategy preferred_address_strategy, + const std::string& alpn, + const std::string& hostname, + uint32_t options, + QlogMode qlog) { + Local obj; + if (!socket->env() + ->quicclientsession_instance_template() + ->NewInstance(socket->env()->context()).ToLocal(&obj)) { + return {}; + } + + BaseObjectPtr session = + MakeDetachedBaseObject( + socket, + obj, + local_addr, + remote_addr, + secure_context, + early_transport_params, + std::move(early_session_ticket), + dcid, + preferred_address_strategy, + alpn, + hostname, + options, + qlog); + + session->AddToSocket(socket); + + return session; +} + +// Initialize a newly created client QuicSession. +// The early_transport_params and session_ticket are optional to +// perform a 0RTT resumption of a prior session. +// The dcid_value parameter is optional to allow user code the +// ability to provide an explicit dcid (this should be rare) +void QuicSession::InitClient( + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + Local dcid_value, + QlogMode qlog) { + CHECK_NULL(connection_); + + local_address_ = local_addr; + remote_address_ = remote_addr; + Debug(this, "Initializing connection from %s to %s", + local_address_, + remote_address_); + + // The maximum packet length is determined largely + // by the IP version (IPv4 vs IPv6). Packet sizes + // should be limited to the maximum MTU necessary to + // prevent IP fragmentation. + max_pktlen_ = GetMaxPktLen(remote_address_); + + QuicSessionConfig config(env()); + ExtendMaxStreamsBidi(DEFAULT_MAX_STREAMS_BIDI); + ExtendMaxStreamsUni(DEFAULT_MAX_STREAMS_UNI); + + connection_id_strategy_(this, scid_.cid(), NGTCP2_MAX_CIDLEN); + + ngtcp2_cid dcid; + if (dcid_value->IsArrayBufferView()) { + ArrayBufferViewContents sbuf( + dcid_value.As()); + CHECK_LE(sbuf.length(), NGTCP2_MAX_CIDLEN); + CHECK_GE(sbuf.length(), NGTCP2_MIN_CIDLEN); + memcpy(dcid.data, sbuf.data(), sbuf.length()); + dcid.datalen = sbuf.length(); + } else { + connection_id_strategy_(this, &dcid, NGTCP2_MAX_CIDLEN); + } + + QuicPath path(local_address_, remote_address_); + + if (qlog == QlogMode::kEnabled) config.set_qlog({ dcid, OnQlogWrite }); + + ngtcp2_conn* conn; + CHECK_EQ( + ngtcp2_conn_client_new( + &conn, + &dcid, + scid_.cid(), + &path, + NGTCP2_PROTO_VER, + &callbacks[crypto_context_->side()], + &config, + &alloc_info_, + static_cast(this)), 0); + + + connection_.reset(conn); + + crypto_context_->Initialize(); + + CHECK(DeriveAndInstallInitialKey(*this, this->dcid())); + + if (early_transport_params != nullptr) + ngtcp2_conn_set_early_remote_transport_params(conn, early_transport_params); + crypto_context_->set_session(std::move(early_session_ticket)); + + UpdateIdleTimer(); + UpdateDataStats(); +} + +// Static ngtcp2 callbacks are registered when ngtcp2 when a new ngtcp2_conn is +// created. These are static functions that, for the most part, simply defer to +// a QuicSession instance that is passed through as user_data. + +// Called by ngtcp2 upon creation of a new client connection +// to initiate the TLS handshake. This is only emitted on the client side. +int QuicSession::OnClientInitial( + ngtcp2_conn* conn, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return NGTCP2_OK(session->crypto_context()->Receive( + NGTCP2_CRYPTO_LEVEL_INITIAL, + 0, nullptr, 0)) ? 0 : NGTCP2_ERR_CALLBACK_FAILURE; +} + +// Triggered by ngtcp2 when an initial handshake packet has been +// received. This is only invoked on server sessions and it is +// the absolute beginning of the communication between a client +// and a server. +int QuicSession::OnReceiveClientInitial( + ngtcp2_conn* conn, + const ngtcp2_cid* dcid, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + if (!session->ReceiveClientInitial(QuicCID(dcid))) { + Debug(session, "Receiving initial client handshake failed"); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +// Called by ngtcp2 for both client and server connections when +// TLS handshake data has been received and needs to be processed. +// This will be called multiple times during the TLS handshake +// process and may be called during key updates. +int QuicSession::OnReceiveCryptoData( + ngtcp2_conn* conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return static_cast( + session->crypto_context()->Receive(crypto_level, offset, data, datalen)); +} + +// Triggered by ngtcp2 when a RETRY packet has been received. This is +// only emitted on the client side (only a server can send a RETRY). +// +// Per the QUIC specification, a RETRY is essentially a mechanism for +// the server to force path validation at the very start of a connection +// at the cost of a single round trip. The RETRY includes a token that +// the client must use in subsequent requests. When received, the client +// MUST restart the TLS handshake and must include the RETRY token in +// all initial packets. If the initial packets contain a valid RETRY +// token, then the server assumes the path to be validated. Fortunately +// ngtcp2 handles the retry token for us, so all we have to do is +// regenerate the initial keying material and restart the handshake +// and we can ignore the retry parameter. +int QuicSession::OnReceiveRetry( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const ngtcp2_pkt_retry* retry, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + if (!session->ReceiveRetry()) { + Debug(session, "Receiving retry token failed"); + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +// Called by ngtcp2 for both client and server connections +// when a request to extend the maximum number of bidirectional +// streams has been received. +int QuicSession::OnExtendMaxStreamsBidi( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->ExtendMaxStreamsBidi(max_streams); + return 0; +} + +// Called by ngtcp2 for both client and server connections +// when a request to extend the maximum number of unidirectional +// streams has been received +int QuicSession::OnExtendMaxStreamsUni( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->ExtendMaxStreamsUni(max_streams); + return 0; +} + +// Triggered by ngtcp2 when the local peer has received an +// indication from the remote peer indicating that additional +// unidirectional streams may be sent. The max_streams parameter +// identifies the highest unidirectional stream ID that may be +// opened. +int QuicSession::OnExtendMaxStreamsRemoteUni( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->ExtendMaxStreamsRemoteUni(max_streams); + return 0; +} + +// Triggered by ngtcp2 when the local peer has received an +// indication from the remote peer indicating that additional +// bidirectional streams may be sent. The max_streams parameter +// identifies the highest bidirectional stream ID that may be +// opened. +int QuicSession::OnExtendMaxStreamsRemoteBidi( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->ExtendMaxStreamsRemoteUni(max_streams); + return 0; +} + +// Triggered by ngtcp2 when the local peer has received a flow +// control signal from the remote peer indicating that additional +// data can be sent. The max_data parameter identifies the maximum +// data offset that may be sent. That is, a value of 99 means that +// out of a stream of 1000 bytes, only the first 100 may be sent. +// (offsets 0 through 99). +int QuicSession::OnExtendMaxStreamData( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t max_data, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->ExtendMaxStreamData(stream_id, max_data); + return 0; +} + +int QuicSession::OnConnectionIDStatus( + ngtcp2_conn* conn, + int type, + uint64_t seq, + const ngtcp2_cid* cid, + const uint8_t* token, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + + QuicCID qcid(cid); + Debug(session, "Updating connection ID %s (has reset token? %s)", + qcid, + token == nullptr ? "No" : "Yes"); + if (token != nullptr) + session->UpdateConnectionID(type, qcid, StatelessResetToken(token)); + return 0; +} + +// Called by ngtcp2 for both client and server connections +// when ngtcp2 has determined that the TLS handshake has +// been completed. It is important to understand that this +// is only an indication of the local peer's handshake state. +// The remote peer might not yet have completed its part +// of the handshake. +int QuicSession::OnHandshakeCompleted( + ngtcp2_conn* conn, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->HandshakeCompleted(); + return 0; +} + +// Called by ngtcp2 for clients when the handshake has been +// confirmed. Confirmation occurs *after* handshake completion. +int QuicSession::OnHandshakeConfirmed( + ngtcp2_conn* conn, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->HandshakeConfirmed(); + return 0; +} + +// Called by ngtcp2 when a chunk of stream data has been received. +// Currently, ngtcp2 ensures that this callback is always called +// with an offset parameter strictly larger than the previous call's +// offset + datalen (that is, data will never be delivered out of +// order). That behavior may change in the future but only via a +// configuration option. +int QuicSession::OnReceiveStreamData( + ngtcp2_conn* conn, + int64_t stream_id, + int fin, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + return session->ReceiveStreamData(stream_id, fin, data, datalen, offset) ? + 0 : NGTCP2_ERR_CALLBACK_FAILURE; +} + +// Called by ngtcp2 when a new stream has been opened +int QuicSession::OnStreamOpen( + ngtcp2_conn* conn, + int64_t stream_id, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->StreamOpen(stream_id); + return 0; +} + +// Called by ngtcp2 when an acknowledgement for a chunk of +// TLS handshake data has been received by the remote peer. +// This is only an indication that data was received, not that +// it was successfully processed. Acknowledgements are a key +// part of the QUIC reliability mechanism. +int QuicSession::OnAckedCryptoOffset( + ngtcp2_conn* conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + size_t datalen, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->crypto_context()->AcknowledgeCryptoData(crypto_level, datalen); + return 0; +} + +// Called by ngtcp2 when an acknowledgement for a chunk of +// stream data has been received successfully by the remote peer. +// This is only an indication that data was received, not that +// it was successfully processed. Acknowledgements are a key +// part of the QUIC reliability mechanism. +int QuicSession::OnAckedStreamDataOffset( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t offset, + size_t datalen, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->AckedStreamDataOffset(stream_id, offset, datalen); + return 0; +} + +// Called by ngtcp2 for a client connection when the server +// has indicated a preferred address in the transport +// params. +// For now, there are two modes: we can accept the preferred address +// or we can reject it. Later, we may want to implement a callback +// to ask the user if they want to accept the preferred address or +// not. +int QuicSession::OnSelectPreferredAddress( + ngtcp2_conn* conn, + ngtcp2_addr* dest, + const ngtcp2_preferred_addr* paddr, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + + // The paddr parameter contains the server advertised preferred + // address. The dest parameter contains the address that is + // actually being used. If the preferred address is selected, + // then the contents of paddr are copied over to dest. It is + // important to remember that SelectPreferredAddress should + // return true regardless of whether the preferred address was + // selected or not. It should only return false if there was + // an actual failure processing things. Note, however, that + // even in such a failure, we debug log and ignore it. + // If the preferred address is not selected, dest remains + // unchanged. + QuicPreferredAddress preferred_address(session->env(), dest, paddr); + session->SelectPreferredAddress(preferred_address); + return 0; +} + +// Called by ngtcp2 when a stream has been closed. +int QuicSession::OnStreamClose( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->StreamClose(stream_id, app_error_code); + return 0; +} + +// Stream reset means the remote peer will no longer send data +// on the identified stream. It is essentially a premature close. +// The final_size parameter is important here in that it identifies +// exactly how much data the *remote peer* is aware that it sent. +// If there are lost packets, then the local peer's idea of the final +// size might not match. +int QuicSession::OnStreamReset( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t final_size, + uint64_t app_error_code, + void* user_data, + void* stream_user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->StreamReset(stream_id, final_size, app_error_code); + return 0; +} + +// Called by ngtcp2 when it needs to generate some random data. +// We currently do not use it, but the ngtcp2_rand_ctx identifies +// why the random data is necessary. When ctx is equal to +// NGTCP2_RAND_CTX_NONE, it typically means the random data +// is being used during the TLS handshake. When ctx is equal to +// NGTCP2_RAND_CTX_PATH_CHALLENGE, the random data is being +// used to construct a PATH_CHALLENGE. These *might* need more +// secure and robust random number generation given the +// sensitivity of PATH_CHALLENGE operations (an attacker +// could use a compromised PATH_CHALLENGE to trick an endpoint +// into redirecting traffic). +// TODO(@jasnell): In the future, we'll want to explore whether +// we want to handle the different cases of ngtcp2_rand_ctx +int QuicSession::OnRand( + ngtcp2_conn* conn, + uint8_t* dest, + size_t destlen, + ngtcp2_rand_ctx ctx, + void* user_data) { + EntropySource(dest, destlen); + return 0; +} + +// When a new client connection is established, ngtcp2 will call +// this multiple times to generate a pool of connection IDs to use. +int QuicSession::OnGetNewConnectionID( + ngtcp2_conn* conn, + ngtcp2_cid* cid, + uint8_t* token, + size_t cidlen, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + CHECK(!Ngtcp2CallbackScope::InNgtcp2CallbackScope(session)); + return session->GetNewConnectionID(cid, token, cidlen) ? + 0 : NGTCP2_ERR_CALLBACK_FAILURE; +} + +// When a connection is closed, ngtcp2 will call this multiple +// times to retire connection IDs. It's also possible for this +// to be called at times throughout the lifecycle of the connection +// to remove a CID from the availability pool. +int QuicSession::OnRemoveConnectionID( + ngtcp2_conn* conn, + const ngtcp2_cid* cid, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->RemoveConnectionID(QuicCID(cid)); + return 0; +} + +// Called by ngtcp2 to perform path validation. Path validation +// is necessary to ensure that a packet is originating from the +// expected source. If the res parameter indicates success, it +// means that the path specified has been verified as being +// valid. +// +// Validity here means only that there has been a successful +// exchange of PATH_CHALLENGE information between the peers. +// It's critical to understand that the validity of a path +// can change at any timee so this is only an indication of +// validity at a specific point in time. +int QuicSession::OnPathValidation( + ngtcp2_conn* conn, + const ngtcp2_path* path, + ngtcp2_path_validation_result res, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->PathValidation(path, res); + return 0; +} + +// Triggered by ngtcp2 when a version negotiation is received. +// What this means is that the remote peer does not support the +// QUIC version requested. The only thing we can do here (per +// the QUIC specification) is silently discard the connection +// and notify the JavaScript side that a different version of +// QUIC should be used. The sv parameter does list the QUIC +// versions advertised as supported by the remote peer. +int QuicSession::OnVersionNegotiation( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const uint32_t* sv, + size_t nsv, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + QuicSession::Ngtcp2CallbackScope callback_scope(session); + session->VersionNegotiation(sv, nsv); + return 0; +} + +// Triggered by ngtcp2 when a stateless reset is received. What this +// means is that the remote peer might recognize the CID but has lost +// all state necessary to successfully process it. The only thing we +// can do is silently close the connection. For server sessions, this +// means all session state is shut down and discarded, even on the +// JavaScript side. For client sessions, we discard session state at +// the C++ layer but -- at least in the future -- we can retain some +// state at the JavaScript level to allow for automatic session +// resumption. +int QuicSession::OnStatelessReset( + ngtcp2_conn* conn, + const ngtcp2_pkt_stateless_reset* sr, + void* user_data) { + QuicSession* session = static_cast(user_data); + if (UNLIKELY(session->is_destroyed())) + return NGTCP2_ERR_CALLBACK_FAILURE; + session->set_flag(QUICSESSION_FLAG_STATELESS_RESET); + return 0; +} + +void QuicSession::OnQlogWrite(void* user_data, const void* data, size_t len) { + QuicSession* session = static_cast(user_data); + session->listener()->OnQLog(reinterpret_cast(data), len); +} + +const ngtcp2_conn_callbacks QuicSession::callbacks[2] = { + // NGTCP2_CRYPTO_SIDE_CLIENT + { + OnClientInitial, + nullptr, + OnReceiveCryptoData, + OnHandshakeCompleted, + OnVersionNegotiation, + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + OnReceiveStreamData, + OnAckedCryptoOffset, + OnAckedStreamDataOffset, + OnStreamOpen, + OnStreamClose, + OnStatelessReset, + OnReceiveRetry, + OnExtendMaxStreamsBidi, + OnExtendMaxStreamsUni, + OnRand, + OnGetNewConnectionID, + OnRemoveConnectionID, + ngtcp2_crypto_update_key_cb, + OnPathValidation, + OnSelectPreferredAddress, + OnStreamReset, + OnExtendMaxStreamsRemoteBidi, + OnExtendMaxStreamsRemoteUni, + OnExtendMaxStreamData, + OnConnectionIDStatus, + OnHandshakeConfirmed + }, + // NGTCP2_CRYPTO_SIDE_SERVER + { + nullptr, + OnReceiveClientInitial, + OnReceiveCryptoData, + OnHandshakeCompleted, + nullptr, // recv_version_negotiation + ngtcp2_crypto_encrypt_cb, + ngtcp2_crypto_decrypt_cb, + ngtcp2_crypto_hp_mask_cb, + OnReceiveStreamData, + OnAckedCryptoOffset, + OnAckedStreamDataOffset, + OnStreamOpen, + OnStreamClose, + OnStatelessReset, + nullptr, // recv_retry + nullptr, // extend_max_streams_bidi + nullptr, // extend_max_streams_uni + OnRand, + OnGetNewConnectionID, + OnRemoveConnectionID, + ngtcp2_crypto_update_key_cb, + OnPathValidation, + nullptr, // select_preferred_addr + OnStreamReset, + OnExtendMaxStreamsRemoteBidi, + OnExtendMaxStreamsRemoteUni, + OnExtendMaxStreamData, + OnConnectionIDStatus, + nullptr, // handshake_confirmed + } +}; + +// JavaScript API + +namespace { +void QuicSessionSetSocket(const FunctionCallbackInfo& args) { + QuicSession* session; + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As()); + args.GetReturnValue().Set(session->set_socket(socket)); +} + +// Perform an immediate close on the QuicSession, causing a +// CONNECTION_CLOSE frame to be scheduled and sent and starting +// the closing period for this session. The name "ImmediateClose" +// is a bit of an unfortunate misnomer as the session will not +// be immediately shutdown. The naming is pulled from the QUIC +// spec to indicate a state where the session immediately enters +// the closing period, but the session will not be destroyed +// until either the idle timeout fires or destroy is explicitly +// called. +void QuicSessionClose(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->set_last_error(QuicError(env, args[0], args[1])); + session->SendConnectionClose(); +} + +// GracefulClose flips a flag that prevents new local streams +// from being opened and new remote streams from being received. It is +// important to note that this does *NOT* send a CONNECTION_CLOSE packet +// to the peer. Existing streams are permitted to close gracefully. +void QuicSessionGracefulClose(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->StartGracefulClose(); +} + +// Destroying the QuicSession will trigger sending of a CONNECTION_CLOSE +// packet, after which the QuicSession will be immediately torn down. +void QuicSessionDestroy(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->set_last_error(QuicError(env, args[0], args[1])); + session->Destroy(); +} + +void QuicSessionGetEphemeralKeyInfo(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->ephemeral_key().ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionGetPeerCertificate(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->peer_cert(!args[0]->IsTrue()).ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionGetRemoteAddress( + const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Environment* env = session->env(); + CHECK(args[0]->IsObject()); + args.GetReturnValue().Set( + session->remote_address().ToJS(env, args[0].As())); +} + +void QuicSessionGetCertificate( + const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Local ret; + if (session->crypto_context()->cert().ToLocal(&ret)) + args.GetReturnValue().Set(ret); +} + +void QuicSessionPing(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->Ping(); +} + +// Triggers a silent close of a QuicSession. This is currently only used +// (and should ever only be used) for testing purposes... +void QuicSessionSilentClose(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + ProcessEmitWarning( + session->env(), + "Forcing silent close of QuicSession for testing purposes only"); + session->SilentClose(); +} + +// TODO(addaleax): This is a temporary solution for testing and should be +// removed later. +void QuicSessionRemoveFromSocket(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->RemoveFromSocket(); +} + +void QuicSessionUpdateKey(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + // Initiating a key update may fail if it is done too early (either + // before the TLS handshake has been confirmed or while a previous + // key update is being processed). When it fails, InitiateKeyUpdate() + // will return false. + args.GetReturnValue().Set(session->crypto_context()->InitiateKeyUpdate()); +} + +// When a client wishes to resume a prior TLS session, it must specify both +// the remember transport parameters and remembered TLS session ticket. Those +// will each be provided as a TypedArray. The DecodeTransportParams and +// DecodeSessionTicket functions handle those. If the argument is undefined, +// then resumption is not used. + +bool DecodeTransportParams( + Local value, + ngtcp2_transport_params* params) { + if (value->IsUndefined()) + return false; + CHECK(value->IsArrayBufferView()); + ArrayBufferViewContents sbuf(value.As()); + if (sbuf.length() != sizeof(ngtcp2_transport_params)) + return false; + memcpy(params, sbuf.data(), sizeof(ngtcp2_transport_params)); + return true; +} + +crypto::SSLSessionPointer DecodeSessionTicket(Local value) { + if (value->IsUndefined()) + return {}; + CHECK(value->IsArrayBufferView()); + ArrayBufferViewContents sbuf(value.As()); + return crypto::GetTLSSession(sbuf.data(), sbuf.length()); +} + +void QuicSessionStartHandshake(const FunctionCallbackInfo& args) { + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + session->StartHandshake(); +} + +void NewQuicClientSession(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + QuicSocket* socket; + int32_t family; + uint32_t port; + SecureContext* sc; + SocketAddress remote_addr; + int32_t preferred_address_policy; + PreferredAddressStrategy preferred_address_strategy; + uint32_t options = QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY; + std::string alpn(NGTCP2_ALPN_H3); + + enum ARG_IDX : int { + SOCKET, + TYPE, + IP, + PORT, + SECURE_CONTEXT, + SNI, + REMOTE_TRANSPORT_PARAMS, + SESSION_TICKET, + DCID, + PREFERRED_ADDRESS_POLICY, + ALPN, + OPTIONS, + QLOG, + AUTO_START + }; + + CHECK(args[ARG_IDX::SOCKET]->IsObject()); + CHECK(args[ARG_IDX::SECURE_CONTEXT]->IsObject()); + CHECK(args[ARG_IDX::IP]->IsString()); + CHECK(args[ARG_IDX::ALPN]->IsString()); + CHECK(args[ARG_IDX::TYPE]->Int32Value(env->context()).To(&family)); + CHECK(args[ARG_IDX::PORT]->Uint32Value(env->context()).To(&port)); + CHECK(args[ARG_IDX::OPTIONS]->Uint32Value(env->context()).To(&options)); + CHECK(args[ARG_IDX::AUTO_START]->IsBoolean()); + if (!args[ARG_IDX::SNI]->IsUndefined()) + CHECK(args[ARG_IDX::SNI]->IsString()); + + ASSIGN_OR_RETURN_UNWRAP(&socket, args[ARG_IDX::SOCKET].As()); + ASSIGN_OR_RETURN_UNWRAP(&sc, args[ARG_IDX::SECURE_CONTEXT].As()); + + CHECK(args[ARG_IDX::PREFERRED_ADDRESS_POLICY]->Int32Value( + env->context()).To(&preferred_address_policy)); + switch (preferred_address_policy) { + case QUIC_PREFERRED_ADDRESS_ACCEPT: + preferred_address_strategy = QuicSession::UsePreferredAddressStrategy; + break; + default: + preferred_address_strategy = QuicSession::IgnorePreferredAddressStrategy; + } + + node::Utf8Value address(env->isolate(), args[ARG_IDX::IP]); + node::Utf8Value servername(env->isolate(), args[ARG_IDX::SNI]); + + if (!SocketAddress::New(family, *address, port, &remote_addr)) + return args.GetReturnValue().Set(ERR_FAILED_TO_CREATE_SESSION); + + // ALPN is a string prefixed by the length, followed by values + Utf8Value val(env->isolate(), args[ARG_IDX::ALPN]); + alpn = val.length(); + alpn += *val; + + crypto::SSLSessionPointer early_session_ticket = + DecodeSessionTicket(args[ARG_IDX::SESSION_TICKET]); + ngtcp2_transport_params early_transport_params; + bool has_early_transport_params = + DecodeTransportParams( + args[ARG_IDX::REMOTE_TRANSPORT_PARAMS], + &early_transport_params); + + socket->ReceiveStart(); + + BaseObjectPtr session = + QuicSession::CreateClient( + socket, + socket->local_address(), + remote_addr, + BaseObjectPtr(sc), + has_early_transport_params ? &early_transport_params : nullptr, + std::move(early_session_ticket), + args[ARG_IDX::DCID], + preferred_address_strategy, + alpn, + std::string(*servername), + options, + args[ARG_IDX::QLOG]->IsTrue() ? + QlogMode::kEnabled : + QlogMode::kDisabled); + + // Start the TLS handshake if the autoStart option is true + // (which it is by default). + if (args[ARG_IDX::AUTO_START]->BooleanValue(env->isolate())) { + session->StartHandshake(); + // Session was created but was unable to bootstrap properly during + // the start of the TLS handshake. + if (session->is_destroyed()) + return args.GetReturnValue().Set(ERR_FAILED_TO_CREATE_SESSION); + } + + args.GetReturnValue().Set(session->object()); +} + +// Add methods that are shared by both client and server QuicSessions +void AddMethods(Environment* env, Local session) { + env->SetProtoMethod(session, "close", QuicSessionClose); + env->SetProtoMethod(session, "destroy", QuicSessionDestroy); + env->SetProtoMethod(session, "getRemoteAddress", QuicSessionGetRemoteAddress); + env->SetProtoMethod(session, "getCertificate", QuicSessionGetCertificate); + env->SetProtoMethod(session, "getPeerCertificate", + QuicSessionGetPeerCertificate); + env->SetProtoMethod(session, "gracefulClose", QuicSessionGracefulClose); + env->SetProtoMethod(session, "updateKey", QuicSessionUpdateKey); + env->SetProtoMethod(session, "ping", QuicSessionPing); + env->SetProtoMethod(session, "removeFromSocket", QuicSessionRemoveFromSocket); + env->SetProtoMethod(session, "onClientHelloDone", + QuicSessionOnClientHelloDone); + env->SetProtoMethod(session, "onCertDone", QuicSessionOnCertDone); +} +} // namespace + +void QuicSession::Initialize( + Environment* env, + Local target, + Local context) { + { + Local class_name = + FIXED_ONE_BYTE_STRING(env->isolate(), "QuicServerSession"); + Local session = FunctionTemplate::New(env->isolate()); + session->SetClassName(class_name); + session->Inherit(AsyncWrap::GetConstructorTemplate(env)); + Local sessiont = session->InstanceTemplate(); + sessiont->SetInternalFieldCount(1); + sessiont->Set(env->owner_symbol(), Null(env->isolate())); + AddMethods(env, session); + env->set_quicserversession_instance_template(sessiont); + } + + { + Local class_name = + FIXED_ONE_BYTE_STRING(env->isolate(), "QuicClientSession"); + Local session = FunctionTemplate::New(env->isolate()); + session->SetClassName(class_name); + session->Inherit(AsyncWrap::GetConstructorTemplate(env)); + Local sessiont = session->InstanceTemplate(); + sessiont->SetInternalFieldCount(1); + sessiont->Set(env->owner_symbol(), Null(env->isolate())); + AddMethods(env, session); + env->SetProtoMethod(session, + "getEphemeralKeyInfo", + QuicSessionGetEphemeralKeyInfo); + env->SetProtoMethod(session, + "setSocket", + QuicSessionSetSocket); + env->SetProtoMethod(session, "startHandshake", QuicSessionStartHandshake); + env->set_quicclientsession_instance_template(sessiont); + + env->SetMethod(target, "createClientSession", NewQuicClientSession); + env->SetMethod(target, "silentCloseSession", QuicSessionSilentClose); + } +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_session.h b/src/quic/node_quic_session.h new file mode 100644 index 00000000000000..63041f684175de --- /dev/null +++ b/src/quic/node_quic_session.h @@ -0,0 +1,1485 @@ +#ifndef SRC_QUIC_NODE_QUIC_SESSION_H_ +#define SRC_QUIC_NODE_QUIC_SESSION_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "aliased_buffer.h" +#include "async_wrap.h" +#include "env.h" +#include "handle_wrap.h" +#include "node.h" +#include "node_crypto.h" +#include "node_http_common.h" +#include "node_mem.h" +#include "node_quic_buffer-inl.h" +#include "node_quic_crypto.h" +#include "node_quic_util.h" +#include "node_sockaddr.h" +#include "v8.h" +#include "uv.h" + +#include +#include +#include + +#include +#include +#include + +namespace node { +namespace quic { + +using ConnectionPointer = DeleteFnPtr; + +class QuicApplication; +class QuicPacket; +class QuicSocket; +class QuicStream; + +using QuicHeader = NgHeaderImpl; + +using StreamsMap = std::unordered_map>; + +enum class QlogMode { + kDisabled, + kEnabled +}; + +typedef void(*ConnectionIDStrategy)( + QuicSession* session, + ngtcp2_cid* cid, + size_t cidlen); + +typedef void(*PreferredAddressStrategy)( + QuicSession* session, + const QuicPreferredAddress& preferred_address); + +// The QuicSessionConfig class holds the initial transport parameters and +// configuration options set by the JavaScript side when either a +// client or server QuicSession is created. Instances are +// stack created and use a combination of an AliasedBuffer to pass +// the numeric settings quickly (see node_quic_state.h) and passed +// in non-numeric settings (e.g. preferred_addr). +class QuicSessionConfig : public ngtcp2_settings { + public: + QuicSessionConfig() {} + + explicit QuicSessionConfig(Environment* env) { + Set(env); + } + + QuicSessionConfig(const QuicSessionConfig& config) { + initial_ts = uv_hrtime(); + transport_params = config.transport_params; + qlog = config.qlog; + log_printf = config.log_printf; + token = config.token; + } + + void ResetToDefaults(Environment* env); + + // QuicSessionConfig::Set() pulls values out of the AliasedBuffer + // defined in node_quic_state.h and stores the values in settings_. + // If preferred_addr is not nullptr, it is copied into the + // settings_.preferred_addr field + void Set(Environment* env, + const struct sockaddr* preferred_addr = nullptr); + + inline void set_original_connection_id(const QuicCID& ocid); + + // Generates the stateless reset token for the settings_ + inline void GenerateStatelessResetToken( + QuicSession* session, + const QuicCID& cid); + + // If the preferred address is set, generates the associated tokens + inline void GeneratePreferredAddressToken( + ConnectionIDStrategy connection_id_strategy, + QuicSession* session, + QuicCID* pscid); + + inline void set_qlog(const ngtcp2_qlog_settings& qlog); +}; + +// Options to alter the behavior of various functions on the +// server QuicSession. These are set on the QuicSocket when +// the listen() function is called and are passed to the +// constructor of the server QuicSession. +enum QuicServerSessionOptions : uint32_t { + // When set, instructs the server QuicSession to reject + // client authentication certs that cannot be verified. + QUICSERVERSESSION_OPTION_REJECT_UNAUTHORIZED = 0x1, + + // When set, instructs the server QuicSession to request + // a client authentication cert + QUICSERVERSESSION_OPTION_REQUEST_CERT = 0x2 +}; + +// Options to alter the behavior of various functions on the +// client QuicSession. These are set on the client QuicSession +// constructor. +enum QuicClientSessionOptions : uint32_t { + // When set, instructs the client QuicSession to include an + // OCSP request in the initial TLS handshake + QUICCLIENTSESSION_OPTION_REQUEST_OCSP = 0x1, + + // When set, instructs the client QuicSession to verify the + // hostname identity. This is required by QUIC and enabled + // by default. We allow disabling it only for debugging + // purposes. + QUICCLIENTSESSION_OPTION_VERIFY_HOSTNAME_IDENTITY = 0x2, + + // When set, instructs the client QuicSession to perform + // additional checks on TLS session resumption. + QUICCLIENTSESSION_OPTION_RESUME = 0x4 +}; + + +// The QuicSessionState enums are used with the QuicSession's +// private state_ array. This is exposed to JavaScript via an +// aliased buffer and is used to communicate various types of +// state efficiently across the native/JS boundary. +enum QuicSessionState : int { + // Communicates whether a 'keylog' event listener has been + // registered on the JavaScript QuicSession object. The + // value will be either 1 or 0. When set to 1, the native + // code will emit TLS keylog entries to the JavaScript + // side triggering the 'keylog' event once for each line. + IDX_QUIC_SESSION_STATE_KEYLOG_ENABLED, + + // Communicates whether a 'clientHello' event listener has + // been registered on the JavaScript QuicServerSession. + // The value will be either 1 or 0. When set to 1, the + // native code will callout to the JavaScript side causing + // the 'clientHello' event to be emitted. This is only + // used on server QuicSession instances. + IDX_QUIC_SESSION_STATE_CLIENT_HELLO_ENABLED, + + // Communicates whether a 'cert' event listener has been + // registered on the JavaScript QuicSession. The value will + // be either 1 or 0. When set to 1, the native code will + // callout to the JavaScript side causing the 'cert' event + // to be emitted. + IDX_QUIC_SESSION_STATE_CERT_ENABLED, + + // Communicates whether a 'pathValidation' event listener + // has been registered on the JavaScript QuicSession. The + // value will be either 1 or 0. When set to 1, the native + // code will callout to the JavaScript side causing the + // 'pathValidation' event to be emitted + IDX_QUIC_SESSION_STATE_PATH_VALIDATED_ENABLED, + + // Communicates the current max cumulative number of + // bidi and uni streams that may be opened on the session + IDX_QUIC_SESSION_STATE_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_STATE_MAX_STREAMS_UNI, + + // Communicates the current maxinum number of bytes that + // the local endpoint can send in this connection + // (updated immediately after processing sent/received packets) + IDX_QUIC_SESSION_STATE_MAX_DATA_LEFT, + + // Communicates the current total number of bytes in flight + IDX_QUIC_SESSION_STATE_BYTES_IN_FLIGHT, + + // Communicates whether a 'usePreferredAddress' event listener + // has been registered. + IDX_QUIC_SESSION_STATE_USE_PREFERRED_ADDRESS_ENABLED, + + IDX_QUIC_SESSION_STATE_HANDSHAKE_CONFIRMED, + + // Communicates whether a session was closed due to idle timeout + IDX_QUIC_SESSION_STATE_IDLE_TIMEOUT, + + // Just the number of session state enums for use when + // creating the AliasedBuffer. + IDX_QUIC_SESSION_STATE_COUNT +}; + +#define SESSION_STATS(V) \ + V(CREATED_AT, created_at, "Created At") \ + V(HANDSHAKE_START_AT, handshake_start_at, "Handshake Started") \ + V(HANDSHAKE_SEND_AT, handshake_send_at, "Handshke Last Sent") \ + V(HANDSHAKE_CONTINUE_AT, handshake_continue_at, "Handshke Continued") \ + V(HANDSHAKE_COMPLETED_AT, handshake_completed_at, "Handshake Completed") \ + V(HANDSHAKE_CONFIRMED_AT, handshake_confirmed_at, "Handshake Confirmed") \ + V(HANDSHAKE_ACKED_AT, handshake_acked_at, "Handshake Last Acknowledged") \ + V(SENT_AT, sent_at, "Last Sent At") \ + V(RECEIVED_AT, received_at, "Last Received At") \ + V(CLOSING_AT, closing_at, "Closing") \ + V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ + V(BYTES_SENT, bytes_sent, "Bytes Sent") \ + V(BIDI_STREAM_COUNT, bidi_stream_count, "Bidi Stream Count") \ + V(UNI_STREAM_COUNT, uni_stream_count, "Uni Stream Count") \ + V(STREAMS_IN_COUNT, streams_in_count, "Streams In Count") \ + V(STREAMS_OUT_COUNT, streams_out_count, "Streams Out Count") \ + V(KEYUPDATE_COUNT, keyupdate_count, "Key Update Count") \ + V(RETRY_COUNT, retry_count, "Retry Count") \ + V(LOSS_RETRANSMIT_COUNT, loss_retransmit_count, "Loss Retransmit Count") \ + V(ACK_DELAY_RETRANSMIT_COUNT, \ + ack_delay_retransmit_count, \ + "Ack Delay Retransmit Count") \ + V(PATH_VALIDATION_SUCCESS_COUNT, \ + path_validation_success_count, \ + "Path Validation Success Count") \ + V(PATH_VALIDATION_FAILURE_COUNT, \ + path_validation_failure_count, \ + "Path Validation Failure Count") \ + V(MAX_BYTES_IN_FLIGHT, max_bytes_in_flight, "Max Bytes In Flight") \ + V(BLOCK_COUNT, block_count, "Block Count") \ + V(MIN_RTT, min_rtt, "Minimum RTT") \ + V(LATEST_RTT, latest_rtt, "Latest RTT") \ + V(SMOOTHED_RTT, smoothed_rtt, "Smoothed RTT") + +#define V(name, _, __) IDX_QUIC_SESSION_STATS_##name, +enum QuicSessionStatsIdx : int { + SESSION_STATS(V) + IDX_QUIC_SESSION_STATS_COUNT +}; +#undef V + +#define V(_, name, __) uint64_t name; +struct QuicSessionStats { + SESSION_STATS(V) +}; +#undef V + +struct QuicSessionStatsTraits { + using Stats = QuicSessionStats; + using Base = QuicSession; + + template + static void ToString(const Base& ptr, Fn&& add_field); +}; + +class QuicSessionListener { + public: + virtual ~QuicSessionListener(); + + virtual void OnKeylog(const char* str, size_t size); + virtual void OnClientHello( + const char* alpn, + const char* server_name); + virtual void OnCert(const char* server_name); + virtual void OnOCSP(v8::Local ocsp); + virtual void OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id); + virtual void OnStreamClose( + int64_t stream_id, + uint64_t app_error_code); + virtual void OnStreamReset( + int64_t stream_id, + uint64_t app_error_code); + virtual void OnSessionDestroyed(); + virtual void OnSessionClose(QuicError error); + virtual void OnStreamReady(BaseObjectPtr stream); + virtual void OnHandshakeCompleted(); + virtual void OnPathValidation( + ngtcp2_path_validation_result res, + const sockaddr* local, + const sockaddr* remote); + virtual void OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address); + virtual void OnSessionTicket(int size, SSL_SESSION* session); + virtual void OnSessionSilentClose( + bool stateless_reset, + QuicError error); + virtual void OnStreamBlocked(int64_t stream_id); + virtual void OnVersionNegotiation( + uint32_t supported_version, + const uint32_t* versions, + size_t vcnt); + virtual void OnQLog(const uint8_t* data, size_t len); + + QuicSession* session() const { return session_.get(); } + + private: + BaseObjectWeakPtr session_; + QuicSessionListener* previous_listener_ = nullptr; + friend class QuicSession; +}; + +class JSQuicSessionListener : public QuicSessionListener { + public: + void OnKeylog(const char* str, size_t size) override; + void OnClientHello( + const char* alpn, + const char* server_name) override; + void OnCert(const char* server_name) override; + void OnOCSP(v8::Local ocsp) override; + void OnStreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id) override; + void OnStreamClose( + int64_t stream_id, + uint64_t app_error_code) override; + void OnStreamReset( + int64_t stream_id, + uint64_t app_error_code) override; + void OnSessionDestroyed() override; + void OnSessionClose(QuicError error) override; + void OnStreamReady(BaseObjectPtr stream) override; + void OnHandshakeCompleted() override; + void OnPathValidation( + ngtcp2_path_validation_result res, + const sockaddr* local, + const sockaddr* remote) override; + void OnSessionTicket(int size, SSL_SESSION* session) override; + void OnSessionSilentClose(bool stateless_reset, QuicError error) override; + void OnUsePreferredAddress( + int family, + const QuicPreferredAddress& preferred_address) override; + void OnStreamBlocked(int64_t stream_id) override; + void OnVersionNegotiation( + uint32_t supported_version, + const uint32_t* versions, + size_t vcnt) override; + void OnQLog(const uint8_t* data, size_t len) override; + + private: + friend class QuicSession; +}; + +// The QuicCryptoContext class encapsulates all of the crypto/TLS +// handshake details on behalf of a QuicSession. +class QuicCryptoContext : public MemoryRetainer { + public: + inline uint64_t Cancel(); + + // Outgoing crypto data must be retained in memory until it is + // explicitly acknowledged. AcknowledgeCryptoData will be invoked + // when ngtcp2 determines that it has received an acknowledgement + // for crypto data at the specified level. This is our indication + // that the data for that level can be released. + void AcknowledgeCryptoData(ngtcp2_crypto_level level, size_t datalen); + + inline void Initialize(); + + // Enables openssl's TLS tracing mechanism for this session only. + void EnableTrace(); + + // Returns the server's prepared OCSP response for transmission. This + // is not used by client QuicSession instances. + inline v8::MaybeLocal ocsp_response() const; + + // Returns ngtcp2's understanding of the current inbound crypto level + inline ngtcp2_crypto_level read_crypto_level() const; + + // Returns ngtcp2's understanding of the current outbound crypto level + inline ngtcp2_crypto_level write_crypto_level() const; + + inline bool early_data() const; + + bool is_option_set(uint32_t option) const { return options_ & option; } + + // Emits a single keylog line to the JavaScript layer + inline void Keylog(const char* line); + + int OnClientHello(); + + inline void OnClientHelloDone(); + + int OnOCSP(); + + void OnOCSPDone( + BaseObjectPtr secure_context, + v8::Local ocsp_response); + + bool OnSecrets( + ngtcp2_crypto_level level, + const uint8_t* rx_secret, + const uint8_t* tx_secret, + size_t secretlen); + + int OnTLSStatus(); + + // Receives and processes TLS handshake details + int Receive( + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t* data, + size_t datalen); + + // Resumes the TLS handshake following a client hello or + // OCSP callback + inline void ResumeHandshake(); + + inline v8::MaybeLocal cert() const; + inline v8::MaybeLocal cipher_name() const; + inline v8::MaybeLocal cipher_version() const; + inline v8::MaybeLocal ephemeral_key() const; + inline const char* hello_alpn() const; + inline v8::MaybeLocal hello_ciphers() const; + inline const char* hello_servername() const; + inline v8::MaybeLocal peer_cert(bool abbreviated) const; + inline std::string selected_alpn() const; + inline const char* servername() const; + + void set_option(uint32_t option, bool on = true) { + if (on) + options_ |= option; + else + options_ &= ~option; + } + + inline bool set_session(crypto::SSLSessionPointer session); + + inline void set_tls_alert(int err); + + inline bool SetupInitialKey(const QuicCID& dcid); + + ngtcp2_crypto_side side() const { return side_; } + + void WriteHandshake( + ngtcp2_crypto_level level, + const uint8_t* data, + size_t datalen); + + bool InitiateKeyUpdate(); + + int VerifyPeerIdentity(const char* hostname); + + QuicSession* session() const { return session_.get(); } + + void MemoryInfo(MemoryTracker* tracker) const override; + + void handshake_started() { + is_handshake_started_ = true; + } + + bool is_handshake_started() const { return is_handshake_started_; } + + SET_MEMORY_INFO_NAME(QuicCryptoContext) + SET_SELF_SIZE(QuicCryptoContext) + + private: + inline QuicCryptoContext( + QuicSession* session, + BaseObjectPtr secure_context, + ngtcp2_crypto_side side, + uint32_t options); + + bool SetSecrets( + ngtcp2_crypto_level level, + const uint8_t* rx_secret, + const uint8_t* tx_secret, + size_t secretlen); + + BaseObjectWeakPtr session_; + BaseObjectPtr secure_context_; + ngtcp2_crypto_side side_; + crypto::SSLPointer ssl_; + QuicBuffer handshake_[3]; + bool is_handshake_started_ = false; + bool in_tls_callback_ = false; + bool in_key_update_ = false; + bool in_ocsp_request_ = false; + bool in_client_hello_ = false; + bool early_data_ = false; + uint32_t options_; + + v8::Global ocsp_response_; + crypto::BIOPointer bio_trace_; + + class TLSCallbackScope { + public: + explicit TLSCallbackScope(QuicCryptoContext* context) : + context_(context) { + context_->in_tls_callback_ = true; + } + + ~TLSCallbackScope() { + context_->in_tls_callback_ = false; + } + + static bool is_in_callback(QuicCryptoContext* context) { + return context->in_tls_callback_; + } + + private: + QuicCryptoContext* context_; + }; + + class TLSHandshakeScope { + public: + TLSHandshakeScope( + QuicCryptoContext* context, + bool* monitor) : + context_(context), + monitor_(monitor) {} + + ~TLSHandshakeScope() { + if (!is_handshake_suspended()) + return; + + *monitor_ = false; + // Only continue the TLS handshake if we are not currently running + // synchronously within the TLS handshake function. This can happen + // when the callback function passed to the clientHello and cert + // event handlers is called synchronously. If the function is called + // asynchronously, then we have to manually continue the handshake. + if (!TLSCallbackScope::is_in_callback(context_)) + context_->ResumeHandshake(); + } + + private: + bool is_handshake_suspended() const { + return context_->in_ocsp_request_ || context_->in_client_hello_; + } + + + QuicCryptoContext* context_; + bool* monitor_; + }; + + friend class QuicSession; +}; + +// A QuicApplication encapsulates the specific details of +// working with a specific QUIC application (e.g. http/3). +class QuicApplication : public MemoryRetainer, + public mem::NgLibMemoryManagerBase { + public: + inline explicit QuicApplication(QuicSession* session); + virtual ~QuicApplication() = default; + + virtual bool Initialize() = 0; + virtual bool ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) = 0; + virtual void AcknowledgeStreamData( + int64_t stream_id, + uint64_t offset, + size_t datalen) { Acknowledge(stream_id, offset, datalen); } + virtual bool BlockStream(int64_t id) { return true; } + virtual void ExtendMaxStreamsRemoteUni(uint64_t max_streams) {} + virtual void ExtendMaxStreamsRemoteBidi(uint64_t max_streams) {} + virtual void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data) {} + virtual void ResumeStream(int64_t stream_id) {} + virtual void SetSessionTicketAppData(const SessionTicketAppData& app_data) { + // TODO(@jasnell): Different QUIC applications may wish to set some + // application data in the session ticket (e.g. http/3 would set + // server settings in the application data). For now, doing nothing + // as I'm just adding the basic mechanism. + } + virtual SessionTicketAppData::Status GetSessionTicketAppData( + const SessionTicketAppData& app_data, + SessionTicketAppData::Flag flag) { + // TODO(@jasnell): Different QUIC application may wish to set some + // application data in the session ticket (e.g. http/3 would set + // server settings in the application data). For now, doing nothing + // as I'm just adding the basic mechanism. + return flag == SessionTicketAppData::Flag::STATUS_RENEW ? + SessionTicketAppData::Status::TICKET_USE_RENEW : + SessionTicketAppData::Status::TICKET_USE; + } + virtual void StreamHeaders( + int64_t stream_id, + int kind, + const std::vector>& headers, + int64_t push_id = 0); + virtual void StreamClose( + int64_t stream_id, + uint64_t app_error_code); + virtual void StreamOpen(int64_t stream_id); + virtual void StreamReset( + int64_t stream_id, + uint64_t app_error_code); + virtual bool SubmitInformation( + int64_t stream_id, + v8::Local headers) { return false; } + virtual bool SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags) { return false; } + virtual bool SubmitTrailers( + int64_t stream_id, + v8::Local headers) { return false; } + virtual BaseObjectPtr SubmitPush( + int64_t stream_id, + v8::Local headers) { + // By default, push streams are not supported + // by an application. + return {}; + } + + inline Environment* env() const; + + bool SendPendingData(); + size_t max_header_pairs() const { return max_header_pairs_; } + size_t max_header_length() const { return max_header_length_; } + + protected: + QuicSession* session() const { return session_.get(); } + bool needs_init() const { return needs_init_; } + void set_init_done() { needs_init_ = false; } + inline void set_stream_fin(int64_t stream_id); + void set_max_header_pairs(size_t max) { max_header_pairs_ = max; } + void set_max_header_length(size_t max) { max_header_length_ = max; } + inline std::unique_ptr CreateStreamDataPacket(); + + struct StreamData { + size_t count = 0; + size_t remaining = 0; + int64_t id = -1; + int fin = 0; + ngtcp2_vec data[kMaxVectorCount] {}; + ngtcp2_vec* buf = nullptr; + BaseObjectPtr stream; + StreamData() { buf = data; } + }; + + void Acknowledge( + int64_t stream_id, + uint64_t offset, + size_t datalen); + virtual int GetStreamData(StreamData* data) = 0; + virtual bool StreamCommit(StreamData* data, size_t datalen) = 0; + virtual bool ShouldSetFin(const StreamData& data) = 0; + + inline ssize_t WriteVStream( + QuicPathStorage* path, + uint8_t* buf, + ssize_t* ndatalen, + const StreamData& stream_data); + + private: + void MaybeSetFin(const StreamData& stream_data); + BaseObjectWeakPtr session_; + bool needs_init_ = true; + size_t max_header_pairs_ = 0; + size_t max_header_length_ = 0; +}; + +// The QuicSession class is an virtual class that serves as +// the basis for both client and server QuicSession. +// It implements the functionality that is shared for both +// QUIC clients and servers. +// +// QUIC sessions are virtual connections that exchange data +// back and forth between peer endpoints via UDP. Every QuicSession +// has an associated TLS context and all data transfered between +// the peers is always encrypted. Unlike TLS over TCP, however, +// The QuicSession uses a session identifier that is independent +// of both the local *and* peer IP address, allowing a QuicSession +// to persist across changes in the network (one of the key features +// of QUIC). QUIC sessions also support 0RTT, implement error +// correction mechanisms to recover from lost packets, and flow +// control. In other words, there's quite a bit going on within +// a QuicSession object. +class QuicSession : public AsyncWrap, + public mem::NgLibMemoryManager, + public StatsBase { + public: + // The default preferred address strategy is to ignore it + static void IgnorePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address); + + static void UsePreferredAddressStrategy( + QuicSession* session, + const QuicPreferredAddress& preferred_address); + + static void Initialize( + Environment* env, + v8::Local target, + v8::Local context); + + static BaseObjectPtr CreateServer( + QuicSocket* socket, + const QuicSessionConfig& config, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn = NGTCP2_ALPN_H3, + uint32_t options = 0, + QlogMode qlog = QlogMode::kDisabled); + + static BaseObjectPtr CreateClient( + QuicSocket* socket, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + v8::Local dcid, + PreferredAddressStrategy preferred_address_strategy = + IgnorePreferredAddressStrategy, + const std::string& alpn = NGTCP2_ALPN_H3, + const std::string& hostname = "", + uint32_t options = 0, + QlogMode qlog = QlogMode::kDisabled); + + static const int kInitialClientBufferLength = 4096; + + // The QuicSession::CryptoContext encapsulates all details of the + // TLS context on behalf of the QuicSession. + QuicSession( + ngtcp2_crypto_side side, + // The QuicSocket that created this session. Note that + // it is possible to replace this socket later, after + // the TLS handshake has completed. The QuicSession + // should never assume that the socket will always + // remain the same. + QuicSocket* socket, + v8::Local wrap, + BaseObjectPtr secure_context, + AsyncWrap::ProviderType provider_type, + // QUIC is generally just a transport. The ALPN identifier + // is used to specify the application protocol that is + // layered on top. If not specified, this will default + // to the HTTP/3 identifier. For QUIC, the alpn identifier + // is always required. + const std::string& alpn, + const std::string& hostname, + const QuicCID& rcid, + uint32_t options = 0, + PreferredAddressStrategy preferred_address_strategy = + IgnorePreferredAddressStrategy); + + // Server Constructor + QuicSession( + QuicSocket* socket, + const QuicSessionConfig& config, + v8::Local wrap, + const QuicCID& rcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + const std::string& alpn, + uint32_t options, + QlogMode qlog); + + // Client Constructor + QuicSession( + QuicSocket* socket, + v8::Local wrap, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + BaseObjectPtr secure_context, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + v8::Local dcid, + PreferredAddressStrategy preferred_address_strategy, + const std::string& alpn, + const std::string& hostname, + uint32_t options, + QlogMode qlog); + + ~QuicSession() override; + + std::string diagnostic_name() const override; + inline QuicCID dcid() const; + + // When a client QuicSession is created, if the autoStart + // option is true, the handshake will be immediately started. + // If autoStart is false, the start of the handshake will be + // deferred until the start handshake method is called; + inline void StartHandshake(); + + QuicApplication* application() const { return application_.get(); } + + QuicCryptoContext* crypto_context() const { return crypto_context_.get(); } + + QuicSessionListener* listener() const { return listener_; } + + BaseObjectPtr CreateStream(int64_t id); + BaseObjectPtr FindStream(int64_t id) const; + inline bool HasStream(int64_t id) const; + + inline bool allow_early_data() const; + + // Returns true if StartGracefulClose() has been called and the + // QuicSession is currently in the process of a graceful close. + inline bool is_gracefully_closing() const; + + // Returns true if Destroy() has been called and the + // QuicSession is no longer usable. + inline bool is_destroyed() const; + + inline bool is_stateless_reset() const; + + // Returns true if the QuicSession has entered the + // closing period following a call to ImmediateClose. + // While true, the QuicSession is only permitted to + // transmit CONNECTION_CLOSE frames until either the + // idle timeout period elapses or until the QuicSession + // is explicitly destroyed. + inline bool is_in_closing_period() const; + + // Returns true if the QuicSession has received a + // CONNECTION_CLOSE frame from the peer. Once in + // the draining period, the QuicSession is not + // permitted to send any frames to the peer. The + // QuicSession will be silently closed after either + // the idle timeout period elapses or until the + // QuicSession is explicitly destroyed. + inline bool is_in_draining_period() const; + + inline bool is_server() const; + + // Starting a GracefulClose disables the ability to open or accept + // new streams for this session. Existing streams are allowed to + // close naturally on their own. Once called, the QuicSession will + // be immediately closed once there are no remaining streams. Note + // that no notification is given to the connecting peer that we're + // in a graceful closing state. A CONNECTION_CLOSE will be sent only + // once ImmediateClose() is called. + inline void StartGracefulClose(); + + QuicError last_error() const { return last_error_; } + + size_t max_packet_length() const { return max_pktlen_; } + + // Get the ALPN protocol identifier configured for this QuicSession. + // For server sessions, this will be compared against the client requested + // ALPN identifier to determine if there is a protocol match. + const std::string& alpn() const { return alpn_; } + + // Get the hostname configured for this QuicSession. This is generally + // only used by client sessions. + const std::string& hostname() const { return hostname_; } + + // Returns the associated peer's address. Note that this + // value can change over the lifetime of the QuicSession. + // The fact that the session is not tied intrinsically to + // a single address is one of the benefits of QUIC. + const SocketAddress& remote_address() const { return remote_address_; } + + inline QuicSocket* socket() const; + + ngtcp2_conn* connection() const { return connection_.get(); } + + void AddStream(BaseObjectPtr stream); + void AddToSocket(QuicSocket* socket); + + // Immediately discards the state of the QuicSession + // and renders the QuicSession instance completely + // unusable. + void Destroy(); + + // Extends the QUIC stream flow control window. This is + // called after received data has been consumed and we + // want to allow the peer to send more data. + inline void ExtendStreamOffset(int64_t stream_id, size_t amount); + + // Extends the QUIC session flow control window + inline void ExtendOffset(size_t amount); + + // Retrieve the local transport parameters established for + // this ngtcp2_conn + inline void GetLocalTransportParams(ngtcp2_transport_params* params); + + // The QUIC version that has been negotiated for this session + inline uint32_t negotiated_version() const; + + // True only if ngtcp2 considers the TLS handshake to be completed + inline bool is_handshake_completed() const; + + // Checks to see if data needs to be retransmitted + void MaybeTimeout(); + + // Called when the session has been determined to have been + // idle for too long and needs to be torn down. + inline void OnIdleTimeout(); + + bool OpenBidirectionalStream(int64_t* stream_id); + bool OpenUnidirectionalStream(int64_t* stream_id); + + // Ping causes the QuicSession to serialize any currently + // pending frames in it's queue, including any necessary + // PROBE packets. This is a best attempt, fire-and-forget + // type of operation. There is no way to listen for a ping + // response. The main intent of using Ping is to either keep + // the connection from becoming idle or to update RTT stats. + void Ping(); + + // Receive and process a QUIC packet received from the peer + bool Receive( + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags); + + // Receive a chunk of QUIC stream data received from the peer + bool ReceiveStreamData( + int64_t stream_id, + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset); + + void RemoveStream(int64_t stream_id); + void RemoveFromSocket(); + + // Causes pending ngtcp2 frames to be serialized and sent + void SendPendingData(); + + inline bool SendPacket( + std::unique_ptr packet, + const ngtcp2_path_storage& path); + + inline uint64_t max_data_left() const; + inline uint64_t max_local_streams_uni() const; + + inline void set_last_error( + QuicError error = { + uint32_t{QUIC_ERROR_SESSION}, + uint64_t{NGTCP2_NO_ERROR} + }); + inline void set_last_error(int32_t family, uint64_t error_code); + inline void set_last_error(int32_t family, int error_code); + + inline void set_remote_transport_params(); + bool set_socket(QuicSocket* socket, bool nat_rebinding = false); + int set_session(SSL_SESSION* session); + + const StreamsMap& streams() const { return streams_; } + + // ResetStream will cause ngtcp2 to queue a + // RESET_STREAM and STOP_SENDING frame, as appropriate, + // for the given stream_id. For a locally-initiated + // unidirectional stream, only a RESET_STREAM frame + // will be scheduled and the stream will be immediately + // closed. For a bi-directional stream, a STOP_SENDING + // frame will be sent. + // + // It is important to note that the QuicStream is + // not destroyed immediately following ShutdownStream. + // The sending QuicSession will not close the stream + // until the RESET_STREAM is acknowledged. + // + // Once the RESET_STREAM is sent, the QuicSession + // should not send any new frames for the stream, + // and all inbound stream frames should be discarded. + // Once ngtcp2 receives the appropriate notification + // that the RESET_STREAM has been acknowledged, the + // stream will be closed. + // + // Once the stream has been closed, it will be + // destroyed and memory will be freed. User code + // can request that a stream be immediately and + // abruptly destroyed without calling ShutdownStream. + // Likewise, an idle timeout may cause the stream + // to be silently destroyed without calling + // ShutdownStream. + void ResetStream( + int64_t stream_id, + uint64_t error_code = NGTCP2_APP_NOERROR); + + void ResumeStream(int64_t stream_id); + + // Submits informational headers to the QUIC Application + // implementation. If headers are not supported, false + // will be returned. Otherwise, returns true + inline bool SubmitInformation( + int64_t stream_id, + v8::Local headers); + + // Submits initial headers to the QUIC Application + // implementation. If headers are not supported, false + // will be returned. Otherwise, returns true + inline bool SubmitHeaders( + int64_t stream_id, + v8::Local headers, + uint32_t flags); + + // Submits trailing headers to the QUIC Application + // implementation. If headers are not supported, false + // will be returned. Otherwise, returns true + inline bool SubmitTrailers( + int64_t stream_id, + v8::Local headers); + + inline BaseObjectPtr SubmitPush( + int64_t stream_id, + v8::Local headers); + + // Error handling for the QuicSession. client and server + // instances will do different things here, but ultimately + // an error means that the QuicSession + // should be torn down. + void HandleError(); + + bool SendConnectionClose(); + bool IsResetToken( + const QuicCID& cid, + const uint8_t* data, + size_t datalen); + + // Implementation for mem::NgLibMemoryManager + inline void CheckAllocatedSize(size_t previous_size) const; + inline void IncreaseAllocatedSize(size_t size); + inline void DecreaseAllocatedSize(size_t size); + + // Immediately close the QuicSession. All currently open + // streams are implicitly reset and closed with RESET_STREAM + // and STOP_SENDING frames transmitted as necessary. A + // CONNECTION_CLOSE frame will be sent and the session + // will enter the closing period until either the idle + // timeout period elapses or until the QuicSession is + // explicitly destroyed. During the closing period, + // the only frames that may be transmitted to the peer + // are repeats of the already sent CONNECTION_CLOSE. + // + // The CONNECTION_CLOSE will use the error code set using + // the most recent call to set_last_error() + void ImmediateClose(); + + // Silently, and immediately close the QuicSession. This is + // generally only done during an idle timeout. That is, per + // the QUIC specification, if the session remains idle for + // longer than both the advertised idle timeout and three + // times the current probe timeout (PTO). In such cases, all + // currently open streams are implicitly reset and closed + // without sending corresponding RESET_STREAM and + // STOP_SENDING frames, the connection state is + // discarded, and the QuicSession is destroyed without + // sending a CONNECTION_CLOSE frame. + // + // Silent close may also be used to explicitly destroy + // a QuicSession that has either already entered the + // closing or draining periods; or in response to user + // code requests to forcefully terminate a QuicSession + // without transmitting any additional frames to the + // peer. + void SilentClose(); + + void PushListener(QuicSessionListener* listener); + void RemoveListener(QuicSessionListener* listener); + + inline void set_connection_id_strategy( + ConnectionIDStrategy strategy); + inline void set_preferred_address_strategy( + PreferredAddressStrategy strategy); + + inline void SetSessionTicketAppData( + const SessionTicketAppData& app_data); + inline SessionTicketAppData::Status GetSessionTicketAppData( + const SessionTicketAppData& app_data, + SessionTicketAppData::Flag flag); + + inline void SelectPreferredAddress( + const QuicPreferredAddress& preferred_address); + + // Report that the stream data is flow control blocked + inline void StreamDataBlocked(int64_t stream_id); + + // SendSessionScope triggers SendPendingData() when not executing + // within the context of an ngtcp2 callback. When within an ngtcp2 + // callback, SendPendingData will always be called when the callbacks + // complete. + class SendSessionScope { + public: + explicit SendSessionScope( + QuicSession* session, + bool wait_for_handshake = false) + : session_(session), + wait_for_handshake_(wait_for_handshake) { + CHECK(session_); + } + + ~SendSessionScope() { + if (!Ngtcp2CallbackScope::InNgtcp2CallbackScope(session_.get()) && + (!wait_for_handshake_ || + session_->crypto_context()->is_handshake_started())) + session_->SendPendingData(); + } + + private: + BaseObjectPtr session_; + bool wait_for_handshake_ = false; + }; + + // Tracks whether or not we are currently within an ngtcp2 callback + // function. Certain ngtcp2 APIs are not supposed to be called when + // within a callback. We use this as a gate to check. + class Ngtcp2CallbackScope { + public: + explicit Ngtcp2CallbackScope(QuicSession* session) : session_(session) { + CHECK(session_); + CHECK(!InNgtcp2CallbackScope(session)); + session_->set_flag(QUICSESSION_FLAG_NGTCP2_CALLBACK); + } + + ~Ngtcp2CallbackScope() { + session_->set_flag(QUICSESSION_FLAG_NGTCP2_CALLBACK, false); + } + + static bool InNgtcp2CallbackScope(QuicSession* session) { + return session->is_flag_set(QUICSESSION_FLAG_NGTCP2_CALLBACK); + } + + private: + BaseObjectPtr session_; + }; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicSession) + SET_SELF_SIZE(QuicSession) + + private: + static void RandomConnectionIDStrategy( + QuicSession* session, + ngtcp2_cid* cid, + size_t cidlen); + + // Initialize the QuicSession as a server + void InitServer( + QuicSessionConfig config, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + const QuicCID& dcid, + const QuicCID& ocid, + uint32_t version, + QlogMode qlog); + + // Initialize the QuicSession as a client + void InitClient( + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + ngtcp2_transport_params* early_transport_params, + crypto::SSLSessionPointer early_session_ticket, + v8::Local dcid, + QlogMode qlog); + + inline void InitApplication(); + + void AckedStreamDataOffset( + int64_t stream_id, + uint64_t offset, + size_t datalen); + + inline void AssociateCID(const QuicCID& cid); + inline void DisassociateCID(const QuicCID& cid); + inline void ExtendMaxStreamData(int64_t stream_id, uint64_t max_data); + void ExtendMaxStreams(bool bidi, uint64_t max_streams); + inline void ExtendMaxStreamsUni(uint64_t max_streams); + inline void ExtendMaxStreamsBidi(uint64_t max_streams); + inline void ExtendMaxStreamsRemoteUni(uint64_t max_streams); + inline void ExtendMaxStreamsRemoteBidi(uint64_t max_streams); + bool GetNewConnectionID(ngtcp2_cid* cid, uint8_t* token, size_t cidlen); + inline void GetConnectionCloseInfo(); + inline void HandshakeCompleted(); + inline void HandshakeConfirmed(); + void PathValidation( + const ngtcp2_path* path, + ngtcp2_path_validation_result res); + bool ReceiveClientInitial(const QuicCID& dcid); + bool ReceivePacket(ngtcp2_path* path, const uint8_t* data, ssize_t nread); + bool ReceiveRetry(); + inline void RemoveConnectionID(const QuicCID& cid); + void ScheduleRetransmit(); + bool SendPacket(std::unique_ptr packet); + inline void set_local_address(const ngtcp2_addr* addr); + void StreamClose(int64_t stream_id, uint64_t app_error_code); + void StreamOpen(int64_t stream_id); + void StreamReset( + int64_t stream_id, + uint64_t final_size, + uint64_t app_error_code); + bool WritePackets(const char* diagnostic_label = nullptr); + void UpdateRecoveryStats(); + void UpdateConnectionID( + int type, + const QuicCID& cid, + const StatelessResetToken& token); + void UpdateDataStats(); + inline void UpdateEndpoint(const ngtcp2_path& path); + + inline void VersionNegotiation(const uint32_t* sv, size_t nsv); + + // static ngtcp2 callbacks + static int OnClientInitial( + ngtcp2_conn* conn, + void* user_data); + static int OnReceiveClientInitial( + ngtcp2_conn* conn, + const ngtcp2_cid* dcid, + void* user_data); + static int OnReceiveCryptoData( + ngtcp2_conn* conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data); + static int OnHandshakeCompleted( + ngtcp2_conn* conn, + void* user_data); + static int OnHandshakeConfirmed( + ngtcp2_conn* conn, + void* user_data); + static int OnReceiveStreamData( + ngtcp2_conn* conn, + int64_t stream_id, + int fin, + uint64_t offset, + const uint8_t* data, + size_t datalen, + void* user_data, + void* stream_user_data); + static int OnReceiveRetry( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const ngtcp2_pkt_retry* retry, + void* user_data); + static int OnAckedCryptoOffset( + ngtcp2_conn* conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + size_t datalen, + void* user_data); + static int OnAckedStreamDataOffset( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t offset, + size_t datalen, + void* user_data, + void* stream_user_data); + static int OnSelectPreferredAddress( + ngtcp2_conn* conn, + ngtcp2_addr* dest, + const ngtcp2_preferred_addr* paddr, + void* user_data); + static int OnStreamClose( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t app_error_code, + void* user_data, + void* stream_user_data); + static int OnStreamOpen( + ngtcp2_conn* conn, + int64_t stream_id, + void* user_data); + static int OnStreamReset( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t final_size, + uint64_t app_error_code, + void* user_data, + void* stream_user_data); + static int OnRand( + ngtcp2_conn* conn, + uint8_t* dest, + size_t destlen, + ngtcp2_rand_ctx ctx, + void* user_data); + static int OnGetNewConnectionID( + ngtcp2_conn* conn, + ngtcp2_cid* cid, + uint8_t* token, + size_t cidlen, + void* user_data); + static int OnRemoveConnectionID( + ngtcp2_conn* conn, + const ngtcp2_cid* cid, + void* user_data); + static int OnPathValidation( + ngtcp2_conn* conn, + const ngtcp2_path* path, + ngtcp2_path_validation_result res, + void* user_data); + static int OnExtendMaxStreamsUni( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data); + static int OnExtendMaxStreamsBidi( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data); + static int OnExtendMaxStreamData( + ngtcp2_conn* conn, + int64_t stream_id, + uint64_t max_data, + void* user_data, + void* stream_user_data); + static int OnVersionNegotiation( + ngtcp2_conn* conn, + const ngtcp2_pkt_hd* hd, + const uint32_t* sv, + size_t nsv, + void* user_data); + static int OnStatelessReset( + ngtcp2_conn* conn, + const ngtcp2_pkt_stateless_reset* sr, + void* user_data); + static int OnExtendMaxStreamsRemoteUni( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data); + static int OnExtendMaxStreamsRemoteBidi( + ngtcp2_conn* conn, + uint64_t max_streams, + void* user_data); + static int OnConnectionIDStatus( + ngtcp2_conn* conn, + int type, + uint64_t seq, + const ngtcp2_cid* cid, + const uint8_t* token, + void* user_data); + static void OnQlogWrite(void* user_data, const void* data, size_t len); + + void UpdateIdleTimer(); + inline void UpdateRetransmitTimer(uint64_t timeout); + inline void StopRetransmitTimer(); + inline void StopIdleTimer(); + bool StartClosingPeriod(); + + enum QuicSessionFlags : uint32_t { + // Initial state when a QuicSession is created but nothing yet done. + QUICSESSION_FLAG_INITIAL = 0x1, + + // Set while the QuicSession is in the process of an Immediate + // or silent close. + QUICSESSION_FLAG_CLOSING = 0x2, + + // Set while the QuicSession is in the process of a graceful close. + QUICSESSION_FLAG_GRACEFUL_CLOSING = 0x4, + + // Set when the QuicSession has been destroyed (but not + // yet freed) + QUICSESSION_FLAG_DESTROYED = 0x8, + + QUICSESSION_FLAG_HAS_TRANSPORT_PARAMS = 0x10, + + // Set while the QuicSession is executing an ngtcp2 callback + QUICSESSION_FLAG_NGTCP2_CALLBACK = 0x100, + + // Set if the QuicSession is in the middle of a silent close + // (that is, a CONNECTION_CLOSE should not be sent) + QUICSESSION_FLAG_SILENT_CLOSE = 0x200, + + QUICSESSION_FLAG_HANDSHAKE_RX = 0x400, + QUICSESSION_FLAG_HANDSHAKE_TX = 0x800, + QUICSESSION_FLAG_HANDSHAKE_KEYS = + QUICSESSION_FLAG_HANDSHAKE_RX | + QUICSESSION_FLAG_HANDSHAKE_TX, + QUICSESSION_FLAG_SESSION_RX = 0x1000, + QUICSESSION_FLAG_SESSION_TX = 0x2000, + QUICSESSION_FLAG_SESSION_KEYS = + QUICSESSION_FLAG_SESSION_RX | + QUICSESSION_FLAG_SESSION_TX, + + // Set if the QuicSession was closed due to stateless reset + QUICSESSION_FLAG_STATELESS_RESET = 0x4000 + }; + + void set_flag(QuicSessionFlags flag, bool on = true) { + if (on) + flags_ |= flag; + else + flags_ &= ~flag; + } + + bool is_flag_set(QuicSessionFlags flag) const { + return (flags_ & flag) == flag; + } + + void IncrementConnectionCloseAttempts() { + if (connection_close_attempts_ < kMaxSizeT) + connection_close_attempts_++; + } + + bool ShouldAttemptConnectionClose() { + if (connection_close_attempts_ == connection_close_limit_) { + if (connection_close_limit_ * 2 <= kMaxSizeT) + connection_close_limit_ *= 2; + else + connection_close_limit_ = kMaxSizeT; + return true; + } + return false; + } + + typedef ssize_t(*ngtcp2_close_fn)( + ngtcp2_conn* conn, + ngtcp2_path* path, + uint8_t* dest, + size_t destlen, + uint64_t error_code, + ngtcp2_tstamp ts); + + static inline ngtcp2_close_fn SelectCloseFn(uint32_t family) { + return family == QUIC_ERROR_APPLICATION ? + ngtcp2_conn_write_application_close : + ngtcp2_conn_write_connection_close; + } + + // Select the QUIC Application based on the configured ALPN identifier + QuicApplication* SelectApplication(QuicSession* session); + + ngtcp2_mem alloc_info_; + std::unique_ptr crypto_context_; + std::unique_ptr application_; + BaseObjectWeakPtr socket_; + std::string alpn_; + std::string hostname_; + QuicError last_error_ = { + uint32_t{QUIC_ERROR_SESSION}, + uint64_t{NGTCP2_NO_ERROR} + }; + ConnectionPointer connection_; + SocketAddress local_address_{}; + SocketAddress remote_address_{}; + uint32_t flags_ = 0; + size_t max_pktlen_ = 0; + size_t current_ngtcp2_memory_ = 0; + size_t connection_close_attempts_ = 0; + size_t connection_close_limit_ = 1; + + ConnectionIDStrategy connection_id_strategy_ = nullptr; + PreferredAddressStrategy preferred_address_strategy_ = nullptr; + + QuicSessionListener* listener_ = nullptr; + JSQuicSessionListener default_listener_; + + TimerPointer idle_; + TimerPointer retransmit_; + + QuicCID scid_; + QuicCID rcid_; + QuicCID pscid_; + ngtcp2_transport_params transport_params_; + + std::unique_ptr conn_closebuf_; + + StreamsMap streams_; + + AliasedFloat64Array state_; + + struct RemoteTransportParamsDebug { + QuicSession* session; + explicit RemoteTransportParamsDebug(QuicSession* session_) + : session(session_) {} + std::string ToString() const; + }; + + static const ngtcp2_conn_callbacks callbacks[2]; + + friend class QuicCryptoContext; + friend class QuicSessionListener; + friend class JSQuicSessionListener; +}; + +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS +#endif // SRC_QUIC_NODE_QUIC_SESSION_H_ diff --git a/src/quic/node_quic_socket-inl.h b/src/quic/node_quic_socket-inl.h new file mode 100644 index 00000000000000..64ffeebc48148d --- /dev/null +++ b/src/quic/node_quic_socket-inl.h @@ -0,0 +1,227 @@ +#ifndef SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ +#define SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_quic_socket.h" +#include "node_sockaddr.h" +#include "node_quic_session.h" +#include "node_crypto.h" +#include "debug_utils-inl.h" + +namespace node { + +using crypto::EntropySource; + +namespace quic { + +std::unique_ptr QuicPacket::Create( + const char* diagnostic_label, + size_t len) { + return std::make_unique(diagnostic_label, len); +} + +std::unique_ptr QuicPacket::Copy( + const std::unique_ptr& other) { + return std::make_unique(*other.get()); +} + +void QuicPacket::set_length(size_t len) { + CHECK_LE(len, data_.size()); + data_.resize(len); +} + +int QuicEndpoint::Send( + uv_buf_t* buf, + size_t len, + const sockaddr* addr) { + int ret = static_cast(udp_->Send(buf, len, addr)); + if (ret == 0) + IncrementPendingCallbacks(); + return ret; +} + +int QuicEndpoint::ReceiveStart() { + return udp_->RecvStart(); +} + +int QuicEndpoint::ReceiveStop() { + return udp_->RecvStop(); +} + +void QuicEndpoint::WaitForPendingCallbacks() { + if (!has_pending_callbacks()) { + listener_->OnEndpointDone(this); + return; + } + waiting_for_callbacks_ = true; +} + +void QuicSocket::AssociateCID( + const QuicCID& cid, + const QuicCID& scid) { + if (cid && scid) + dcid_to_scid_[cid] = scid; +} + +void QuicSocket::DisassociateCID(const QuicCID& cid) { + if (cid) { + Debug(this, "Removing association for cid %s", cid); + dcid_to_scid_.erase(cid); + } +} + +void QuicSocket::AssociateStatelessResetToken( + const StatelessResetToken& token, + BaseObjectPtr session) { + Debug(this, "Associating stateless reset token %s", token); + token_map_[token] = session; +} + +void QuicSocket::DisassociateStatelessResetToken( + const StatelessResetToken& token) { + Debug(this, "Removing stateless reset token %s", token); + token_map_.erase(token); +} + +// StopListening is called when the QuicSocket is no longer +// accepting new server connections. Typically, this is called +// when the QuicSocket enters a graceful closing state where +// existing sessions are allowed to close naturally but new +// sessions are rejected. +void QuicSocket::StopListening() { + if (is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) { + Debug(this, "Stop listening"); + set_flag(QUICSOCKET_FLAGS_SERVER_LISTENING, false); + // It is important to not call ReceiveStop here as there + // is ongoing traffic being exchanged by the peers. + } +} + +void QuicSocket::ReceiveStart() { + for (const auto& endpoint : endpoints_) + CHECK_EQ(endpoint->ReceiveStart(), 0); +} + +void QuicSocket::ReceiveStop() { + for (const auto& endpoint : endpoints_) + CHECK_EQ(endpoint->ReceiveStop(), 0); +} + +void QuicSocket::RemoveSession( + const QuicCID& cid, + const SocketAddress& addr) { + DecrementSocketAddressCounter(addr); + sessions_.erase(cid); +} + +void QuicSocket::ReportSendError(int error) { + listener_->OnError(error); +} + +void QuicSocket::IncrementStatelessResetCounter(const SocketAddress& addr) { + reset_counts_[addr]++; +} + +void QuicSocket::IncrementSocketAddressCounter(const SocketAddress& addr) { + addr_counts_[addr]++; +} + +void QuicSocket::DecrementSocketAddressCounter(const SocketAddress& addr) { + auto it = addr_counts_.find(addr); + if (it == std::end(addr_counts_)) + return; + it->second--; + // Remove the address if the counter reaches zero again. + if (it->second == 0) { + addr_counts_.erase(addr); + reset_counts_.erase(addr); + } +} + +size_t QuicSocket::GetCurrentSocketAddressCounter(const SocketAddress& addr) { + auto it = addr_counts_.find(addr); + return it == std::end(addr_counts_) ? 0 : it->second; +} + +size_t QuicSocket::GetCurrentStatelessResetCounter(const SocketAddress& addr) { + auto it = reset_counts_.find(addr); + return it == std::end(reset_counts_) ? 0 : it->second; +} + +void QuicSocket::set_server_busy(bool on) { + Debug(this, "Turning Server Busy Response %s", on ? "on" : "off"); + set_flag(QUICSOCKET_FLAGS_SERVER_BUSY, on); + listener_->OnServerBusy(on); +} + +bool QuicSocket::is_diagnostic_packet_loss(double prob) const { + if (LIKELY(prob == 0.0)) return false; + unsigned char c = 255; + EntropySource(&c, 1); + return (static_cast(c) / 255) < prob; +} + +void QuicSocket::set_diagnostic_packet_loss(double rx, double tx) { + rx_loss_ = rx; + tx_loss_ = tx; +} + +bool QuicSocket::ToggleStatelessReset() { + set_flag( + QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET, + !is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET)); + return !is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); +} + +void QuicSocket::set_validated_address(const SocketAddress& addr) { + if (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU)) { + // Remove the oldest item if we've hit the LRU limit + validated_addrs_.push_back(SocketAddress::Hash()(addr)); + if (validated_addrs_.size() > kMaxValidateAddressLru) + validated_addrs_.pop_front(); + } +} + +bool QuicSocket::is_validated_address(const SocketAddress& addr) const { + if (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU)) { + auto res = std::find(std::begin(validated_addrs_), + std::end(validated_addrs_), + SocketAddress::Hash()(addr)); + return res != std::end(validated_addrs_); + } + return false; +} + +void QuicSocket::AddSession( + const QuicCID& cid, + BaseObjectPtr session) { + sessions_[cid] = session; + IncrementSocketAddressCounter(session->remote_address()); + IncrementStat( + session->is_server() ? + &QuicSocketStats::server_sessions : + &QuicSocketStats::client_sessions); +} + +void QuicSocket::AddEndpoint( + BaseObjectPtr endpoint_, + bool preferred) { + Debug(this, "Adding %sendpoint", preferred ? "preferred " : ""); + if (preferred || endpoints_.empty()) + preferred_endpoint_ = endpoint_; + endpoints_.emplace_back(endpoint_); + if (is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) + endpoint_->ReceiveStart(); +} + +void QuicSocket::SessionReady(BaseObjectPtr session) { + listener_->OnSessionReady(session); +} + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_SOCKET_INL_H_ diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc new file mode 100644 index 00000000000000..157dee2e8c49c0 --- /dev/null +++ b/src/quic/node_quic_socket.cc @@ -0,0 +1,1178 @@ +#include "node_quic_socket-inl.h" // NOLINT(build/include) +#include "async_wrap-inl.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "memory_tracker-inl.h" +#include "nghttp2/nghttp2.h" +#include "node.h" +#include "node_buffer.h" +#include "node_crypto.h" +#include "node_internals.h" +#include "node_mem-inl.h" +#include "node_quic_crypto.h" +#include "node_quic_session-inl.h" +#include "node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "req_wrap-inl.h" +#include "util.h" +#include "uv.h" +#include "v8.h" + +#include + +namespace node { + +using crypto::EntropySource; +using crypto::SecureContext; + +using v8::ArrayBufferView; +using v8::Boolean; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::HandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Number; +using v8::Object; +using v8::ObjectTemplate; +using v8::PropertyAttribute; +using v8::String; +using v8::Value; + +namespace quic { + +namespace { +// The reserved version is a mechanism QUIC endpoints +// can use to ensure correct handling of version +// negotiation. It is defined by the QUIC spec in +// https://tools.ietf.org/html/draft-ietf-quic-transport-24#section-6.3 +// Specifically, any version that follows the pattern +// 0x?a?a?a?a may be used to force version negotiation. +inline uint32_t GenerateReservedVersion( + const SocketAddress& addr, + uint32_t version) { + socklen_t addrlen = addr.length(); + uint32_t h = 0x811C9DC5u; + const uint8_t* p = addr.raw(); + const uint8_t* ep = p + addrlen; + for (; p != ep; ++p) { + h ^= *p; + h *= 0x01000193u; + } + version = htonl(version); + p = reinterpret_cast(&version); + ep = p + sizeof(version); + for (; p != ep; ++p) { + h ^= *p; + h *= 0x01000193u; + } + h &= 0xf0f0f0f0u; + h |= 0x0a0a0a0au; + return h; +} + +bool IsShortHeader( + uint32_t version, + const uint8_t* pscid, + size_t pscidlen) { + return version == NGTCP2_PROTO_VER && + pscid == nullptr && + pscidlen == 0; +} +} // namespace + +QuicPacket::QuicPacket(const char* diagnostic_label, size_t len) : + data_(len), + diagnostic_label_(diagnostic_label) { + CHECK_LE(len, NGTCP2_MAX_PKT_SIZE); +} + +QuicPacket::QuicPacket(const QuicPacket& other) : + QuicPacket(other.diagnostic_label_, other.data_.size()) { + memcpy(data_.data(), other.data_.data(), other.data_.size()); +} + +const char* QuicPacket::diagnostic_label() const { + return diagnostic_label_ != nullptr ? + diagnostic_label_ : "unspecified"; +} + +void QuicPacket::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("data", data_); +} + +QuicSocketListener::~QuicSocketListener() { + if (socket_) + socket_->RemoveListener(this); +} + +void QuicSocketListener::OnError(ssize_t code) { + if (previous_listener_ != nullptr) + previous_listener_->OnError(code); +} + +void QuicSocketListener::OnSessionReady(BaseObjectPtr session) { + if (previous_listener_ != nullptr) + previous_listener_->OnSessionReady(session); +} + +void QuicSocketListener::OnServerBusy(bool busy) { + if (previous_listener_ != nullptr) + previous_listener_->OnServerBusy(busy); +} + +void QuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { + if (previous_listener_ != nullptr) + previous_listener_->OnEndpointDone(endpoint); +} + +void QuicSocketListener::OnDestroy() { + if (previous_listener_ != nullptr) + previous_listener_->OnDestroy(); +} + +void JSQuicSocketListener::OnError(ssize_t code) { + Environment* env = socket()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local arg = Number::New(env->isolate(), static_cast(code)); + socket()->MakeCallback(env->quic_on_socket_error_function(), 1, &arg); +} + +void JSQuicSocketListener::OnSessionReady(BaseObjectPtr session) { + Environment* env = socket()->env(); + Local arg = session->object(); + Context::Scope context_scope(env->context()); + socket()->MakeCallback(env->quic_on_session_ready_function(), 1, &arg); +} + +void JSQuicSocketListener::OnServerBusy(bool busy) { + Environment* env = socket()->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local arg = Boolean::New(env->isolate(), busy); + socket()->MakeCallback(env->quic_on_socket_server_busy_function(), 1, &arg); +} + +void JSQuicSocketListener::OnEndpointDone(QuicEndpoint* endpoint) { + Environment* env = socket()->env(); + HandleScope scope(env->isolate()); + Context::Scope context_scope(env->context()); + MakeCallback( + env->isolate(), + endpoint->object(), + env->ondone_string(), + 0, nullptr); +} + +void JSQuicSocketListener::OnDestroy() { + // Do nothing here. +} + +QuicEndpoint::QuicEndpoint( + Environment* env, + Local wrap, + QuicSocket* listener, + Local udp_wrap) : + BaseObject(env, wrap), + listener_(listener) { + MakeWeak(); + udp_ = static_cast( + udp_wrap->GetAlignedPointerFromInternalField( + UDPWrapBase::kUDPWrapBaseField)); + CHECK_NOT_NULL(udp_); + udp_->set_listener(this); + strong_ptr_.reset(udp_->GetAsyncWrap()); +} + +void QuicEndpoint::MemoryInfo(MemoryTracker* tracker) const {} + +uv_buf_t QuicEndpoint::OnAlloc(size_t suggested_size) { + return env()->AllocateManaged(suggested_size).release(); +} + +void QuicEndpoint::OnRecv( + ssize_t nread, + const uv_buf_t& buf_, + const sockaddr* addr, + unsigned int flags) { + AllocatedBuffer buf(env(), buf_); + + if (nread <= 0) { + if (nread < 0) + listener_->OnError(this, nread); + return; + } + + listener_->OnReceive( + nread, + std::move(buf), + local_address(), + SocketAddress(addr), + flags); +} + +ReqWrap* QuicEndpoint::CreateSendWrap(size_t msg_size) { + return listener_->OnCreateSendWrap(msg_size); +} + +void QuicEndpoint::OnSendDone(ReqWrap* wrap, int status) { + DecrementPendingCallbacks(); + listener_->OnSendDone(wrap, status); + if (!has_pending_callbacks() && waiting_for_callbacks_) + listener_->OnEndpointDone(this); +} + +void QuicEndpoint::OnAfterBind() { + listener_->OnBind(this); +} + +QuicSocket::QuicSocket( + Environment* env, + Local wrap, + uint64_t retry_token_expiration, + size_t max_connections, + size_t max_connections_per_host, + size_t max_stateless_resets_per_host, + uint32_t options, + QlogMode qlog, + const uint8_t* session_reset_secret, + bool disable_stateless_reset) + : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_QUICSOCKET), + StatsBase(env, wrap), + alloc_info_(MakeAllocator()), + options_(options), + max_connections_(max_connections), + max_connections_per_host_(max_connections_per_host), + max_stateless_resets_per_host_(max_stateless_resets_per_host), + retry_token_expiration_(retry_token_expiration), + qlog_(qlog), + server_alpn_(NGTCP2_ALPN_H3) { + MakeWeak(); + PushListener(&default_listener_); + + Debug(this, "New QuicSocket created"); + + EntropySource(token_secret_, kTokenSecretLen); + + if (disable_stateless_reset) + set_flag(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); + + // Set the session reset secret to the one provided or random. + // Note that a random secret is going to make it exceedingly + // difficult for the session reset token to be useful. + if (session_reset_secret != nullptr) { + memcpy(reset_token_secret_, + session_reset_secret, + NGTCP2_STATELESS_RESET_TOKENLEN); + } else { + EntropySource(reset_token_secret_, NGTCP2_STATELESS_RESET_TOKENLEN); + } +} + +QuicSocket::~QuicSocket() { + QuicSocketListener* listener = listener_; + listener_->OnDestroy(); + if (listener == listener_) + RemoveListener(listener_); + + DebugStats(); +} + +template +void QuicSocketStatsTraits::ToString(const QuicSocket& ptr, Fn&& add_field) { +#define V(_n, name, label) \ + add_field(label, ptr.GetStat(&QuicSocketStats::name)); + SOCKET_STATS(V) +#undef V +} + +void QuicSocket::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("endpoints", endpoints_); + tracker->TrackField("sessions", sessions_); + tracker->TrackField("dcid_to_scid", dcid_to_scid_); + tracker->TrackField("addr_counts", addr_counts_); + tracker->TrackField("reset_counts", reset_counts_); + tracker->TrackField("token_map", token_map_); + tracker->TrackField("validated_addrs", validated_addrs_); + StatsBase::StatsMemoryInfo(tracker); + tracker->TrackFieldWithSize( + "current_ngtcp2_memory", + current_ngtcp2_memory_); +} + +void QuicSocket::Listen( + BaseObjectPtr sc, + const sockaddr* preferred_address, + const std::string& alpn, + uint32_t options) { + CHECK(sc); + CHECK(!server_secure_context_); + CHECK(!is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)); + Debug(this, "Starting to listen"); + server_session_config_.Set(env(), preferred_address); + server_secure_context_ = sc; + server_alpn_ = alpn; + server_options_ = options; + set_flag(QUICSOCKET_FLAGS_SERVER_LISTENING); + RecordTimestamp(&QuicSocketStats::listen_at); + ReceiveStart(); +} + +void QuicSocket::OnError(QuicEndpoint* endpoint, ssize_t error) { + Debug(this, "Reading data from UDP socket failed. Error %" PRId64, error); + listener_->OnError(error); +} + +ReqWrap* QuicSocket::OnCreateSendWrap(size_t msg_size) { + HandleScope handle_scope(env()->isolate()); + Local obj; + if (!env()->quicsocketsendwrap_instance_template() + ->NewInstance(env()->context()).ToLocal(&obj)) return nullptr; + return last_created_send_wrap_ = new SendWrap(env(), obj, msg_size); +} + +void QuicSocket::OnEndpointDone(QuicEndpoint* endpoint) { + Debug(this, "Endpoint has no pending callbacks"); + listener_->OnEndpointDone(endpoint); +} + +void QuicSocket::OnBind(QuicEndpoint* endpoint) { + const SocketAddress& local_address = endpoint->local_address(); + bound_endpoints_[local_address] = + BaseObjectWeakPtr(endpoint); + Debug(this, "Endpoint %s bound", local_address); + RecordTimestamp(&QuicSocketStats::bound_at); +} + +BaseObjectPtr QuicSocket::FindSession(const QuicCID& cid) { + BaseObjectPtr session; + auto session_it = sessions_.find(cid); + if (session_it == std::end(sessions_)) { + auto scid_it = dcid_to_scid_.find(cid); + if (scid_it != std::end(dcid_to_scid_)) { + session_it = sessions_.find(scid_it->second); + CHECK_NE(session_it, std::end(sessions_)); + session = session_it->second; + } + } else { + session = session_it->second; + } + return session; +} + +// When a received packet contains a QUIC short header but cannot be +// matched to a known QuicSession, it is either (a) garbage, +// (b) a valid packet for a connection we no longer have state +// for, or (c) a stateless reset. Because we do not yet know if +// we are going to process the packet, we need to try to quickly +// determine -- with as little cost as possible -- whether the +// packet contains a reset token. We do so by checking the final +// NGTCP2_STATELESS_RESET_TOKENLEN bytes in the packet to see if +// they match one of the known reset tokens previously given by +// the remote peer. If there's a match, then it's a reset token, +// if not, we move on the to the next check. It is very important +// that this check be as inexpensive as possible to avoid a DOS +// vector. +bool QuicSocket::MaybeStatelessReset( + const QuicCID& dcid, + const QuicCID& scid, + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) { + if (UNLIKELY(is_stateless_reset_disabled() || nread < 16)) + return false; + StatelessResetToken possible_token( + data + nread - NGTCP2_STATELESS_RESET_TOKENLEN); + Debug(this, "Possible stateless reset token: %s", possible_token); + auto it = token_map_.find(possible_token); + if (it == token_map_.end()) + return false; + Debug(this, "Received a stateless reset token %s", possible_token); + return it->second->Receive(nread, data, local_addr, remote_addr, flags); +} + +// When a packet is received here, we do not yet know if we can +// process it successfully as a QUIC packet or not. Given the +// nature of UDP, we may receive a great deal of garbage here +// so it is extremely important not to commit resources until +// we're certain we can process the data we received as QUIC +// packet. +// Any packet we choose not to process must be ignored. +void QuicSocket::OnReceive( + ssize_t nread, + AllocatedBuffer buf, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) { + Debug(this, "Receiving %d bytes from the UDP socket", nread); + + // When diagnostic packet loss is enabled, the packet will be randomly + // dropped based on the rx_loss_ probability. + if (UNLIKELY(is_diagnostic_packet_loss(rx_loss_))) { + Debug(this, "Simulating received packet loss"); + return; + } + + IncrementStat(&QuicSocketStats::bytes_received, nread); + + const uint8_t* data = reinterpret_cast(buf.data()); + + uint32_t pversion; + const uint8_t* pdcid; + size_t pdcidlen; + const uint8_t* pscid; + size_t pscidlen; + + // This is our first check to see if the received data can be + // processed as a QUIC packet. If this fails, then the QUIC packet + // header is invalid and cannot be processed; all we can do is ignore + // it. It's questionable whether we should even increment the + // packets_ignored statistic here but for now we do. If it succeeds, + // we have a valid QUIC header but there's still no guarantee that + // the packet can be successfully processed. + if (ngtcp2_pkt_decode_version_cid( + &pversion, + &pdcid, + &pdcidlen, + &pscid, + &pscidlen, + data, nread, kScidLen) < 0) { + IncrementStat(&QuicSocketStats::packets_ignored); + return; + } + + // QUIC currently requires CID lengths of max NGTCP2_MAX_CIDLEN. The + // ngtcp2 API allows non-standard lengths, and we may want to allow + // non-standard lengths later. But for now, we're going to ignore any + // packet with a non-standard CID length. + if (pdcidlen > NGTCP2_MAX_CIDLEN || pscidlen > NGTCP2_MAX_CIDLEN) { + IncrementStat(&QuicSocketStats::packets_ignored); + return; + } + + QuicCID dcid(pdcid, pdcidlen); + QuicCID scid(pscid, pscidlen); + + // TODO(@jasnell): It would be fantastic if Debug() could be + // modified to accept objects with a ToString-like capability + // similar to what we can do with TraceEvents... that would + // allow us to pass the QuicCID directly to Debug and have it + // converted to hex only if the category is enabled so we can + // skip committing resources here. + std::string dcid_hex = dcid.ToString(); + Debug(this, "Received a QUIC packet for dcid %s", dcid_hex.c_str()); + + BaseObjectPtr session = FindSession(dcid); + + // If a session is not found, there are four possible reasons: + // 1. The session has not been created yet + // 2. The session existed once but we've lost the local state for it + // 3. The packet is a stateless reset sent by the peer + // 4. This is a malicious or malformed packet. + if (!session) { + Debug(this, "There is no existing session for dcid %s", dcid_hex.c_str()); + bool is_short_header = IsShortHeader(pversion, pscid, pscidlen); + + // Handle possible reception of a stateless reset token... + // If it is a stateless reset, the packet will be handled with + // no additional action necessary here. We want to return immediately + // without committing any further resources. + if (is_short_header && + MaybeStatelessReset( + dcid, + scid, + nread, + data, + local_addr, + remote_addr, + flags)) { + Debug(this, "Handled stateless reset"); + return; + } + + // AcceptInitialPacket will first validate that the packet can be + // accepted, then create a new server QuicSession instance if able + // to do so. If a new instance cannot be created (for any reason), + // the session BaseObjectPtr will be empty on return. + session = AcceptInitialPacket( + pversion, + dcid, + scid, + nread, + data, + local_addr, + remote_addr, + flags); + + // There are many reasons why a server QuicSession could not be + // created. The most common will be invalid packets or incorrect + // QUIC version. In any of these cases, however, to prevent a + // potential attacker from causing us to consume resources, + // we're just going to ignore the packet. It is possible that + // the AcceptInitialPacket sent a version negotiation packet, + // or a CONNECTION_CLOSE packet. + if (!session) { + Debug(this, "Unable to create a new server QuicSession"); + // If the packet contained a short header, we might need to send + // a stateless reset. The stateless reset contains a token derived + // from the received destination connection ID. + // + // TODO(@jasnell): Stateless resets are generated programmatically + // using HKDF with the sender provided dcid and a locally provided + // secret as input. It is entirely possible that a malicious + // peer could send multiple stateless reset eliciting packets + // with the specific intent of using the returned stateless + // reset to guess the stateless reset token secret used by + // the server. Once guessed, the malicious peer could use + // that secret as a DOS vector against other peers. We currently + // implement some mitigations for this by limiting the number + // of stateless resets that can be sent to a specific remote + // address but there are other possible mitigations, such as + // including the remote address as input in the generation of + // the stateless token. + if (is_short_header && + SendStatelessReset(dcid, local_addr, remote_addr, nread)) { + Debug(this, "Sent stateless reset"); + IncrementStat(&QuicSocketStats::stateless_reset_count); + return; + } + IncrementStat(&QuicSocketStats::packets_ignored); + return; + } + } + + CHECK(session); + + // If the packet could not successfully processed for any reason (possibly + // due to being malformed or malicious in some way) we mark it ignored. + if (!session->Receive(nread, data, local_addr, remote_addr, flags)) { + IncrementStat(&QuicSocketStats::packets_ignored); + return; + } + + IncrementStat(&QuicSocketStats::packets_received); +} + +// Generates and sends a version negotiation packet. This is +// terminal for the connection and is sent only when a QUIC +// packet is received for an unsupported Node.js version. +// It is possible that a malicious packet triggered this +// so we need to be careful not to commit too many resources. +// Currently, we only support one QUIC version at a time. +void QuicSocket::SendVersionNegotiation( + uint32_t version, + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr) { + uint32_t sv[2]; + sv[0] = GenerateReservedVersion(remote_addr, version); + sv[1] = NGTCP2_PROTO_VER; + + uint8_t unused_random; + EntropySource(&unused_random, 1); + + size_t pktlen = dcid.length() + scid.length() + (sizeof(sv)) + 7; + + auto packet = QuicPacket::Create("version negotiation", pktlen); + ssize_t nwrite = ngtcp2_pkt_write_version_negotiation( + packet->data(), + NGTCP2_MAX_PKTLEN_IPV6, + unused_random, + dcid.data(), + dcid.length(), + scid.data(), + scid.length(), + sv, + arraysize(sv)); + if (nwrite <= 0) + return; + packet->set_length(nwrite); + SocketAddress remote_address(remote_addr); + SendPacket(local_addr, remote_address, std::move(packet)); +} + +// Possible generates and sends a stateless reset packet. +// This is terminal for the connection. It is possible +// that a malicious packet triggered this so we need to +// be careful not to commit too many resources. +bool QuicSocket::SendStatelessReset( + const QuicCID& cid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + size_t source_len) { + if (UNLIKELY(is_stateless_reset_disabled())) + return false; + constexpr static size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5; + constexpr static size_t kMinStatelessResetLen = 41; + uint8_t random[kRandlen]; + + // Per the QUIC spec, we need to protect against sending too + // many stateless reset tokens to an endpoint to prevent + // endless looping. + if (GetCurrentStatelessResetCounter(remote_addr) >= + max_stateless_resets_per_host_) { + return false; + } + // Per the QUIC spec, a stateless reset token must be strictly + // smaller than the packet that triggered it. This is one of the + // mechanisms to prevent infinite looping exchange of stateless + // tokens with the peer. + // An endpoint should never send a stateless reset token smaller than + // 41 bytes per the QUIC spec. The reason is that packets less than + // 41 bytes may allow an observer to determine that it's a stateless + // reset. + size_t pktlen = source_len - 1; + if (pktlen < kMinStatelessResetLen) + return false; + + StatelessResetToken token(reset_token_secret_, cid); + EntropySource(random, kRandlen); + + auto packet = QuicPacket::Create("stateless reset", pktlen); + ssize_t nwrite = + ngtcp2_pkt_write_stateless_reset( + packet->data(), + NGTCP2_MAX_PKTLEN_IPV4, + const_cast(token.data()), + random, + kRandlen); + if (nwrite < static_cast(kMinStatelessResetLen)) + return false; + packet->set_length(nwrite); + SocketAddress remote_address(remote_addr); + IncrementStatelessResetCounter(remote_address); + return SendPacket(local_addr, remote_address, std::move(packet)) == 0; +} + +// Generates and sends a retry packet. This is terminal +// for the connection. Retry packets are used to force +// explicit path validation by issuing a token to the +// peer that it must thereafter include in all subsequent +// initial packets. Upon receiving a retry packet, the +// peer must termination it's initial attempt to +// establish a connection and start a new attempt. +// +// TODO(@jasnell): Retry packets will only ever be +// generated by QUIC servers, and only if the QuicSocket +// is configured for explicit path validation. There is +// no way for a client to force a retry packet to be created. +// However, once a client determines that explicit +// path validation is enabled, it could attempt to +// DOS by sending a large number of malicious +// initial packets to intentionally ellicit retry +// packets (It can do so by intentionally sending +// initial packets that ignore the retry token). +// To help mitigate that risk, we should limit the number +// of retries we send to a given remote endpoint. +bool QuicSocket::SendRetry( + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr) { + std::unique_ptr packet = + GenerateRetryPacket(token_secret_, dcid, scid, local_addr, remote_addr); + return packet ? + SendPacket(local_addr, remote_addr, std::move(packet)) == 0 : false; +} + +// Shutdown a connection prematurely, before a QuicSession is created. +void QuicSocket::ImmediateConnectionClose( + const QuicCID& scid, + const QuicCID& dcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + int64_t reason) { + Debug(this, "Sending stateless connection close to %s", scid); + auto packet = QuicPacket::Create("immediate connection close"); + ssize_t nwrite = ngtcp2_crypto_write_connection_close( + packet->data(), + packet->length(), + scid.cid(), + dcid.cid(), + reason); + if (nwrite > 0) { + packet->set_length(nwrite); + SendPacket(local_addr, remote_addr, std::move(packet)); + } +} + +// Inspects the packet and possibly accepts it as a new +// initial packet creating a new QuicSession instance. +// If the packet is not acceptable, it is very important +// not to commit resources. +BaseObjectPtr QuicSocket::AcceptInitialPacket( + uint32_t version, + const QuicCID& dcid, + const QuicCID& scid, + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) { + HandleScope handle_scope(env()->isolate()); + Context::Scope context_scope(env()->context()); + ngtcp2_pkt_hd hd; + QuicCID ocid; + + // If the QuicSocket is not listening, the paket will be ignored. + if (!is_flag_set(QUICSOCKET_FLAGS_SERVER_LISTENING)) { + Debug(this, "QuicSocket is not listening"); + return {}; + } + + switch (ngtcp2_accept(&hd, data, static_cast(nread))) { + case 1: + // Send Version Negotiation + SendVersionNegotiation(version, dcid, scid, local_addr, remote_addr); + // Fall through + case -1: + // Either a version negotiation packet was sent or the packet is + // an invalid initial packet. Either way, there's nothing more we + // can do here. + return {}; + } + + // If the server is busy, new connections will be shut down immediately + // after the initial keys are installed. The busy state is controlled + // entirely by local user code. It is important to understand that + // a QuicSession is created and resources are committed even though + // the QuicSession will be torn down as quickly as possible. + // Else, check to see if the number of connections total for this QuicSocket + // has been exceeded. If the count has been exceeded, shutdown the connection + // immediately after the initial keys are installed. + if (UNLIKELY(is_flag_set(QUICSOCKET_FLAGS_SERVER_BUSY)) || + sessions_.size() >= max_connections_ || + GetCurrentSocketAddressCounter(remote_addr) >= + max_connections_per_host_) { + Debug(this, "QuicSocket is busy or connection count exceeded"); + IncrementStat(&QuicSocketStats::server_busy_count); + ImmediateConnectionClose( + QuicCID(hd.scid), + QuicCID(hd.dcid), + local_addr, + remote_addr, + NGTCP2_SERVER_BUSY); + return {}; + } + + // QUIC has address validation built in to the handshake but allows for + // an additional explicit validation request using RETRY frames. If we + // are using explicit validation, we check for the existence of a valid + // retry token in the packet. If one does not exist, we send a retry with + // a new token. If it does exist, and if it's valid, we grab the original + // cid and continue. + if (!is_validated_address(remote_addr)) { + switch (hd.type) { + case NGTCP2_PKT_INITIAL: + if (is_option_set(QUICSOCKET_OPTIONS_VALIDATE_ADDRESS) || + hd.tokenlen > 0) { + Debug(this, "Performing explicit address validation"); + if (hd.tokenlen == 0) { + Debug(this, "No retry token was detected. Generating one"); + SendRetry(dcid, scid, local_addr, remote_addr); + // Sending a retry token terminates this connection attempt. + return {}; + } + if (InvalidRetryToken( + hd, + remote_addr, + &ocid, + token_secret_, + retry_token_expiration_)) { + Debug(this, "Invalid retry token was detected. Failing"); + ImmediateConnectionClose( + QuicCID(hd.scid), + QuicCID(hd.dcid), + local_addr, + remote_addr); + return {}; + } + } + break; + case NGTCP2_PKT_0RTT: + SendRetry(dcid, scid, local_addr, remote_addr); + return {}; + } + } + + BaseObjectPtr session = + QuicSession::CreateServer( + this, + server_session_config_, + dcid, + local_addr, + remote_addr, + scid, + ocid, + version, + server_alpn_, + server_options_, + qlog_); + CHECK(session); + + listener_->OnSessionReady(session); + + return session; +} + +QuicSocket::SendWrap::SendWrap( + Environment* env, + Local req_wrap_obj, + size_t total_length) + : ReqWrap(env, req_wrap_obj, PROVIDER_QUICSOCKET), + total_length_(total_length) { +} + +std::string QuicSocket::SendWrap::MemoryInfoName() const { + return "QuicSendWrap"; +} + +void QuicSocket::SendWrap::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("session", session_); + tracker->TrackField("packet", packet_); +} + +int QuicSocket::SendPacket( + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + std::unique_ptr packet, + BaseObjectPtr session) { + // If the packet is empty, there's nothing to do + if (packet->length() == 0) + return 0; + + Debug(this, "Sending %" PRIu64 " bytes to %s from %s (label: %s)", + packet->length(), + remote_addr, + local_addr, + packet->diagnostic_label()); + + // If DiagnosticPacketLoss returns true, it will call Done() internally + if (UNLIKELY(is_diagnostic_packet_loss(tx_loss_))) { + Debug(this, "Simulating transmitted packet loss"); + return 0; + } + + last_created_send_wrap_ = nullptr; + uv_buf_t buf = packet->buf(); + + auto endpoint = bound_endpoints_.find(local_addr); + CHECK_NE(endpoint, bound_endpoints_.end()); + int err = endpoint->second->Send(&buf, 1, remote_addr.data()); + + if (err != 0) { + if (err > 0) err = 0; + OnSend(err, packet.get()); + } else { + CHECK_NOT_NULL(last_created_send_wrap_); + last_created_send_wrap_->set_packet(std::move(packet)); + if (session) + last_created_send_wrap_->set_session(session); + } + return err; +} + +void QuicSocket::OnSend(int status, QuicPacket* packet) { + if (status == 0) { + Debug(this, "Sent %" PRIu64 " bytes (label: %s)", + packet->length(), + packet->diagnostic_label()); + IncrementStat(&QuicSocketStats::bytes_sent, packet->length()); + IncrementStat(&QuicSocketStats::packets_sent); + } else { + Debug(this, "Failed to send %" PRIu64 " bytes (status: %d, label: %s)", + packet->length(), + status, + packet->diagnostic_label()); + } +} + +void QuicSocket::OnSendDone(ReqWrap* wrap, int status) { + std::unique_ptr req_wrap(static_cast(wrap)); + OnSend(status, req_wrap->packet()); +} + +void QuicSocket::CheckAllocatedSize(size_t previous_size) const { + CHECK_GE(current_ngtcp2_memory_, previous_size); +} + +void QuicSocket::IncreaseAllocatedSize(size_t size) { + current_ngtcp2_memory_ += size; +} + +void QuicSocket::DecreaseAllocatedSize(size_t size) { + current_ngtcp2_memory_ -= size; +} + +void QuicSocket::PushListener(QuicSocketListener* listener) { + CHECK_NOT_NULL(listener); + CHECK(!listener->socket_); + + listener->previous_listener_ = listener_; + listener->socket_.reset(this); + + listener_ = listener; +} + +void QuicSocket::RemoveListener(QuicSocketListener* listener) { + CHECK_NOT_NULL(listener); + + QuicSocketListener* previous; + QuicSocketListener* current; + + for (current = listener_, previous = nullptr; + /* No loop condition because we want a crash if listener is not found */ + ; previous = current, current = current->previous_listener_) { + CHECK_NOT_NULL(current); + if (current == listener) { + if (previous != nullptr) + previous->previous_listener_ = current->previous_listener_; + else + listener_ = listener->previous_listener_; + break; + } + } + + listener->socket_.reset(); + listener->previous_listener_ = nullptr; +} + +// JavaScript API +namespace { +void NewQuicEndpoint(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args[0].As()); + CHECK(args[1]->IsObject()); + CHECK_GE(args[1].As()->InternalFieldCount(), + UDPWrapBase::kUDPWrapBaseField); + new QuicEndpoint(env, args.This(), socket, args[1].As()); +} + +void NewQuicSocket(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args.IsConstructCall()); + + uint32_t options; + uint32_t retry_token_expiration; + uint32_t max_connections; + uint32_t max_connections_per_host; + uint32_t max_stateless_resets_per_host; + + if (!args[0]->Uint32Value(env->context()).To(&options) || + !args[1]->Uint32Value(env->context()).To(&retry_token_expiration) || + !args[2]->Uint32Value(env->context()).To(&max_connections) || + !args[3]->Uint32Value(env->context()).To(&max_connections_per_host) || + !args[4]->Uint32Value(env->context()) + .To(&max_stateless_resets_per_host)) { + return; + } + CHECK_GE(retry_token_expiration, MIN_RETRYTOKEN_EXPIRATION); + CHECK_LE(retry_token_expiration, MAX_RETRYTOKEN_EXPIRATION); + + const uint8_t* session_reset_secret = nullptr; + if (args[6]->IsArrayBufferView()) { + ArrayBufferViewContents buf(args[6].As()); + CHECK_EQ(buf.length(), kTokenSecretLen); + session_reset_secret = buf.data(); + } + + new QuicSocket( + env, + args.This(), + retry_token_expiration, + max_connections, + max_connections_per_host, + max_stateless_resets_per_host, + options, + args[5]->IsTrue() ? QlogMode::kEnabled : QlogMode::kDisabled, + session_reset_secret, + args[7]->IsTrue()); +} + +void QuicSocketAddEndpoint(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + CHECK(args[0]->IsObject()); + QuicEndpoint* endpoint; + ASSIGN_OR_RETURN_UNWRAP(&endpoint, args[0].As()); + socket->AddEndpoint( + BaseObjectPtr(endpoint), + args[1]->IsTrue()); +} + +// Enabling diagnostic packet loss enables a mode where the QuicSocket +// instance will randomly ignore received packets in order to simulate +// packet loss. This is not an API that should be enabled in production +// but is useful when debugging and diagnosing performance issues. +// Diagnostic packet loss is enabled by setting either the tx or rx +// arguments to a value between 0.0 and 1.0. Setting both values to 0.0 +// disables the mechanism. +void QuicSocketSetDiagnosticPacketLoss( + const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + double rx, tx; + if (!args[0]->NumberValue(env->context()).To(&rx) || + !args[1]->NumberValue(env->context()).To(&tx)) return; + CHECK_GE(rx, 0.0f); + CHECK_GE(tx, 0.0f); + CHECK_LE(rx, 1.0f); + CHECK_LE(tx, 1.0f); + socket->set_diagnostic_packet_loss(rx, tx); +} + +void QuicSocketDestroy(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + socket->ReceiveStop(); +} + +void QuicSocketListen(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder(), + args.GetReturnValue().Set(UV_EBADF)); + CHECK(args[0]->IsObject() && + env->secure_context_constructor_template()->HasInstance(args[0])); + SecureContext* sc; + ASSIGN_OR_RETURN_UNWRAP(&sc, args[0].As(), + args.GetReturnValue().Set(UV_EBADF)); + + sockaddr_storage preferred_address_storage; + const sockaddr* preferred_address = nullptr; + if (args[1]->IsString()) { + node::Utf8Value preferred_address_host(args.GetIsolate(), args[1]); + int32_t preferred_address_family; + uint32_t preferred_address_port; + if (!args[2]->Int32Value(env->context()).To(&preferred_address_family) || + !args[3]->Uint32Value(env->context()).To(&preferred_address_port)) + return; + if (SocketAddress::ToSockAddr( + preferred_address_family, + *preferred_address_host, + preferred_address_port, + &preferred_address_storage)) { + preferred_address = + reinterpret_cast(&preferred_address_storage); + } + } + + std::string alpn(NGTCP2_ALPN_H3); + if (args[4]->IsString()) { + Utf8Value val(env->isolate(), args[4]); + alpn = val.length(); + alpn += *val; + } + + uint32_t options = 0; + if (!args[5]->Uint32Value(env->context()).To(&options)) return; + + socket->Listen( + BaseObjectPtr(sc), + preferred_address, + alpn, + options); +} + +void QuicSocketStopListening(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + socket->StopListening(); +} + +void QuicSocketset_server_busy(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + CHECK_EQ(args.Length(), 1); + socket->set_server_busy(args[0]->IsTrue()); +} + +void QuicSocketToggleStatelessReset(const FunctionCallbackInfo& args) { + QuicSocket* socket; + ASSIGN_OR_RETURN_UNWRAP(&socket, args.Holder()); + args.GetReturnValue().Set(socket->ToggleStatelessReset()); +} + +void QuicEndpointWaitForPendingCallbacks( + const FunctionCallbackInfo& args) { + QuicEndpoint* endpoint; + ASSIGN_OR_RETURN_UNWRAP(&endpoint, args.Holder()); + endpoint->WaitForPendingCallbacks(); +} + +} // namespace + +void QuicEndpoint::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicEndpoint"); + Local endpoint = env->NewFunctionTemplate(NewQuicEndpoint); + endpoint->SetClassName(class_name); + endpoint->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(endpoint, + "waitForPendingCallbacks", + QuicEndpointWaitForPendingCallbacks); + endpoint->InstanceTemplate()->Set(env->owner_symbol(), Null(isolate)); + + target->Set( + context, + class_name, + endpoint->GetFunction(context).ToLocalChecked()) + .FromJust(); +} + +void QuicSocket::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicSocket"); + Local socket = env->NewFunctionTemplate(NewQuicSocket); + socket->SetClassName(class_name); + socket->InstanceTemplate()->SetInternalFieldCount(1); + socket->InstanceTemplate()->Set(env->owner_symbol(), Null(isolate)); + env->SetProtoMethod(socket, + "addEndpoint", + QuicSocketAddEndpoint); + env->SetProtoMethod(socket, + "destroy", + QuicSocketDestroy); + env->SetProtoMethod(socket, + "listen", + QuicSocketListen); + env->SetProtoMethod(socket, + "setDiagnosticPacketLoss", + QuicSocketSetDiagnosticPacketLoss); + env->SetProtoMethod(socket, + "setServerBusy", + QuicSocketset_server_busy); + env->SetProtoMethod(socket, + "stopListening", + QuicSocketStopListening); + env->SetProtoMethod(socket, + "toggleStatelessReset", + QuicSocketToggleStatelessReset); + socket->Inherit(HandleWrap::GetConstructorTemplate(env)); + target->Set(context, class_name, + socket->GetFunction(env->context()).ToLocalChecked()).FromJust(); + + // TODO(addaleax): None of these templates actually are constructor templates. + Local sendwrap_template = ObjectTemplate::New(isolate); + sendwrap_template->SetInternalFieldCount(1); + env->set_quicsocketsendwrap_instance_template(sendwrap_template); +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_socket.h b/src/quic/node_quic_socket.h new file mode 100644 index 00000000000000..bb54187b050ca0 --- /dev/null +++ b/src/quic/node_quic_socket.h @@ -0,0 +1,577 @@ +#ifndef SRC_QUIC_NODE_QUIC_SOCKET_H_ +#define SRC_QUIC_NODE_QUIC_SOCKET_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object.h" +#include "node.h" +#include "node_crypto.h" +#include "node_internals.h" +#include "ngtcp2/ngtcp2.h" +#include "node_quic_session.h" +#include "node_quic_util.h" +#include "node_sockaddr.h" +#include "env.h" +#include "udp_wrap.h" +#include "v8.h" +#include "uv.h" + +#include +#include +#include +#include + +namespace node { + +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::Object; +using v8::Value; + +namespace quic { + +enum QuicSocketOptions : uint32_t { + // When enabled the QuicSocket will validate the address + // using a RETRY packet to the peer. + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS = 0x1, + + // When enabled, and the VALIDATE_ADDRESS option is also + // set, the QuicSocket will use an LRU cache to track + // validated addresses. Address validation will be skipped + // if the address is currently in the cache. + QUICSOCKET_OPTIONS_VALIDATE_ADDRESS_LRU = 0x2, +}; + +#define SOCKET_STATS(V) \ + V(CREATED_AT, created_at, "Created At") \ + V(BOUND_AT, bound_at, "Bound At") \ + V(LISTEN_AT, listen_at, "Listen At") \ + V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ + V(BYTES_SENT, bytes_sent, "Bytes Sent") \ + V(PACKETS_RECEIVED, packets_received, "Packets Received") \ + V(PACKETS_IGNORED, packets_ignored, "Packets Ignored") \ + V(PACKETS_SENT, packets_sent, "Packets Sent") \ + V(SERVER_SESSIONS, server_sessions, "Server Sessions") \ + V(CLIENT_SESSIONS, client_sessions, "Client Sessions") \ + V(STATELESS_RESET_COUNT, stateless_reset_count, "Stateless Reset Count") \ + V(SERVER_BUSY_COUNT, server_busy_count, "Server Busy Count") + +#define V(name, _, __) IDX_QUIC_SOCKET_STATS_##name, +enum QuicSocketStatsIdx : int { + SOCKET_STATS(V) + IDX_QUIC_SOCKET_STATS_COUNT +}; +#undef V + +#define V(_, name, __) uint64_t name; +struct QuicSocketStats { + SOCKET_STATS(V) +}; +#undef V + +struct QuicSocketStatsTraits { + using Stats = QuicSocketStats; + using Base = QuicSocket; + + template + static void ToString(const Base& ptr, Fn&& add_field); +}; + +class QuicSocket; +class QuicEndpoint; + +// This is the generic interface for objects that control QuicSocket +// instances. The default `JSQuicSocketListener` emits events to +// JavaScript +class QuicSocketListener { + public: + virtual ~QuicSocketListener(); + + virtual void OnError(ssize_t code); + virtual void OnSessionReady(BaseObjectPtr session); + virtual void OnServerBusy(bool busy); + virtual void OnEndpointDone(QuicEndpoint* endpoint); + virtual void OnDestroy(); + + QuicSocket* socket() { return socket_.get(); } + + private: + BaseObjectWeakPtr socket_; + QuicSocketListener* previous_listener_ = nullptr; + friend class QuicSocket; +}; + +class JSQuicSocketListener : public QuicSocketListener { + public: + void OnError(ssize_t code) override; + void OnSessionReady(BaseObjectPtr session) override; + void OnServerBusy(bool busy) override; + void OnEndpointDone(QuicEndpoint* endpoint) override; + void OnDestroy() override; +}; + +// A serialized QuicPacket to be sent by a QuicSocket instance. +class QuicPacket : public MemoryRetainer { + public: + // Creates a new QuicPacket. By default the packet will be + // stack allocated with a max size of NGTCP2_MAX_PKTLEN_IPV4. + // If a larger packet size is specified, it will be heap + // allocated. Generally speaking, a QUIC packet should never + // be larger than the current MTU to avoid IP fragmentation. + // + // The content of a QuicPacket is provided by ngtcp2. The + // typical use pattern is to create a QuicPacket instance + // and then pass a pointer to it's internal buffer and max + // size in to an ngtcp2 function that serializes the data. + // ngtcp2 will fill the buffer as much as possible then return + // the number of bytes serialized. User code is then responsible + // for calling set_length() to set the final length of the + // QuicPacket prior to sending it off to the QuicSocket. + // + // The diagnostic label is used in NODE_DEBUG_NATIVE output + // to differentiate send operations. This should always be + // a statically allocated string or nullptr (in which case + // the value "unspecified" is used in the debug output). + // + // Instances of std::unique_ptr are moved through + // QuicSocket and ultimately become the responsibility of the + // SendWrap instance. When the SendWrap is cleaned up, the + // QuicPacket instance will be freed. + static inline std::unique_ptr Create( + const char* diagnostic_label = nullptr, + size_t len = NGTCP2_MAX_PKTLEN_IPV4); + + // Copy the data of the QuicPacket to a new one. Currently, + // this is only used when retransmitting close connection + // packets from a QuicServer. + static inline std::unique_ptr Copy( + const std::unique_ptr& other); + + QuicPacket(const char* diagnostic_label, size_t len); + QuicPacket(const QuicPacket& other); + uint8_t* data() { return data_.data(); } + size_t length() const { return data_.size(); } + uv_buf_t buf() const { + return uv_buf_init( + const_cast(reinterpret_cast(data_.data())), + length()); + } + inline void set_length(size_t len); + const char* diagnostic_label() const; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicPacket); + SET_SELF_SIZE(QuicPacket); + + private: + std::vector data_; + const char* diagnostic_label_ = nullptr; +}; + +// QuicEndpointListener listens to events generated by a QuicEndpoint. +class QuicEndpointListener { + public: + virtual void OnError(QuicEndpoint* endpoint, ssize_t error) = 0; + virtual void OnReceive( + ssize_t nread, + AllocatedBuffer buf, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) = 0; + virtual ReqWrap* OnCreateSendWrap(size_t msg_size) = 0; + virtual void OnSendDone(ReqWrap* wrap, int status) = 0; + virtual void OnBind(QuicEndpoint* endpoint) = 0; + virtual void OnEndpointDone(QuicEndpoint* endpoint) = 0; +}; + +// A QuicEndpoint wraps a UDPBaseWrap. A single QuicSocket may +// have multiple QuicEndpoints, the lifecycles of which are +// attached to the QuicSocket. +class QuicEndpoint : public BaseObject, + public UDPListener { + public: + static void Initialize( + Environment* env, + Local target, + Local context); + + QuicEndpoint( + Environment* env, + Local wrap, + QuicSocket* listener, + Local udp_wrap); + + const SocketAddress& local_address() const { + local_address_ = udp_->GetSockName(); + return local_address_; + } + + // Implementation for UDPListener + uv_buf_t OnAlloc(size_t suggested_size) override; + + void OnRecv(ssize_t nread, + const uv_buf_t& buf, + const sockaddr* addr, + unsigned int flags) override; + + ReqWrap* CreateSendWrap(size_t msg_size) override; + + void OnSendDone(ReqWrap* wrap, int status) override; + + void OnAfterBind() override; + + inline int ReceiveStart(); + + inline int ReceiveStop(); + + inline int Send( + uv_buf_t* buf, + size_t len, + const sockaddr* addr); + + void IncrementPendingCallbacks() { pending_callbacks_++; } + void DecrementPendingCallbacks() { pending_callbacks_--; } + bool has_pending_callbacks() { return pending_callbacks_ > 0; } + inline void WaitForPendingCallbacks(); + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicEndpoint) + SET_SELF_SIZE(QuicEndpoint) + + private: + mutable SocketAddress local_address_; + BaseObjectWeakPtr listener_; + UDPWrapBase* udp_; + BaseObjectPtr strong_ptr_; + size_t pending_callbacks_ = 0; + bool waiting_for_callbacks_ = false; +}; + +// QuicSocket manages the flow of data from the UDP socket to the +// QuicSession. It is responsible for managing the lifecycle of the +// UDP sockets, listening for new server QuicSession instances, and +// passing data two and from the remote peer. +class QuicSocket : public AsyncWrap, + public QuicEndpointListener, + public mem::NgLibMemoryManager, + public StatsBase { + public: + static void Initialize( + Environment* env, + Local target, + Local context); + + QuicSocket( + Environment* env, + Local wrap, + // A retry token should only be valid for a small window of time. + // The retry_token_expiration specifies the number of seconds a + // retry token is permitted to be valid. + uint64_t retry_token_expiration, + // To prevent malicious clients from opening too many concurrent + // connections, we limit the maximum number per remote sockaddr. + size_t max_connections, + size_t max_connections_per_host, + size_t max_stateless_resets_per_host + = DEFAULT_MAX_STATELESS_RESETS_PER_HOST, + uint32_t options = 0, + QlogMode qlog = QlogMode::kDisabled, + const uint8_t* session_reset_secret = nullptr, + bool disable_session_reset = false); + ~QuicSocket() override; + + // Returns the default/preferred local address. Additional + // QuicEndpoint instances may be associated with the + // QuicSocket bound to other local addresses. + const SocketAddress& local_address() { + CHECK(preferred_endpoint_); + return preferred_endpoint_->local_address(); + } + + void MaybeClose(); + + inline void AddSession( + const QuicCID& cid, + BaseObjectPtr session); + inline void AssociateCID( + const QuicCID& cid, + const QuicCID& scid); + inline void DisassociateCID( + const QuicCID& cid); + inline void AssociateStatelessResetToken( + const StatelessResetToken& token, + BaseObjectPtr session); + inline void DisassociateStatelessResetToken( + const StatelessResetToken& token); + void Listen( + BaseObjectPtr context, + const sockaddr* preferred_address = nullptr, + const std::string& alpn = NGTCP2_ALPN_H3, + uint32_t options = 0); + inline void ReceiveStart(); + inline void ReceiveStop(); + inline void RemoveSession( + const QuicCID& cid, + const SocketAddress& addr); + inline void ReportSendError(int error); + int SendPacket( + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + std::unique_ptr packet, + BaseObjectPtr session = BaseObjectPtr()); + inline void SessionReady(BaseObjectPtr session); + inline void set_server_busy(bool on); + inline void set_diagnostic_packet_loss(double rx = 0.0, double tx = 0.0); + inline void StopListening(); + + // Toggles whether or not stateless reset is enabled or not. + // Returns true if stateless reset is enabled, false if it + // is not. + inline bool ToggleStatelessReset(); + + BaseObjectPtr server_secure_context() const { + return server_secure_context_; + } + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicSocket) + SET_SELF_SIZE(QuicSocket) + + // Implementation for mem::NgLibMemoryManager + void CheckAllocatedSize(size_t previous_size) const; + void IncreaseAllocatedSize(size_t size); + void DecreaseAllocatedSize(size_t size); + + const uint8_t* session_reset_secret() { + return reset_token_secret_; + } + + // Implementation for QuicListener + ReqWrap* OnCreateSendWrap(size_t msg_size) override; + void OnSendDone(ReqWrap* wrap, int status) override; + void OnBind(QuicEndpoint* endpoint) override; + void OnReceive( + ssize_t nread, + AllocatedBuffer buf, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags) override; + void OnError(QuicEndpoint* endpoint, ssize_t error) override; + void OnEndpointDone(QuicEndpoint* endpoint) override; + + // Serializes and transmits a RETRY packet to the connected peer. + bool SendRetry( + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr); + + // Serializes and transmits a Stateless Reset to the connected peer. + bool SendStatelessReset( + const QuicCID& cid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + size_t source_len); + + // Serializes and transmits a Version Negotiation packet to the + // connected peer. + void SendVersionNegotiation( + uint32_t version, + const QuicCID& dcid, + const QuicCID& scid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr); + + void PushListener(QuicSocketListener* listener); + void RemoveListener(QuicSocketListener* listener); + + inline void AddEndpoint( + BaseObjectPtr endpoint, + bool preferred = false); + + void ImmediateConnectionClose( + const QuicCID& scid, + const QuicCID& dcid, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + int64_t reason = NGTCP2_INVALID_TOKEN); + + private: + static void OnAlloc( + uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf); + + void OnSend(int status, QuicPacket* packet); + + inline void set_validated_address(const SocketAddress& addr); + inline bool is_validated_address(const SocketAddress& addr) const; + + bool MaybeStatelessReset( + const QuicCID& dcid, + const QuicCID& scid, + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags); + + BaseObjectPtr AcceptInitialPacket( + uint32_t version, + const QuicCID& dcid, + const QuicCID& scid, + ssize_t nread, + const uint8_t* data, + const SocketAddress& local_addr, + const SocketAddress& remote_addr, + unsigned int flags); + + BaseObjectPtr FindSession(const QuicCID& cid); + + inline void IncrementSocketAddressCounter(const SocketAddress& addr); + inline void DecrementSocketAddressCounter(const SocketAddress& addr); + inline void IncrementStatelessResetCounter(const SocketAddress& addr); + inline size_t GetCurrentSocketAddressCounter(const SocketAddress& addr); + inline size_t GetCurrentStatelessResetCounter(const SocketAddress& addr); + + // Returns true if, and only if, diagnostic packet loss is enabled + // and the current packet should be artificially considered lost. + inline bool is_diagnostic_packet_loss(double prob) const; + + bool is_stateless_reset_disabled() { + return is_flag_set(QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET); + } + + enum QuicSocketFlags : uint32_t { + QUICSOCKET_FLAGS_NONE = 0x0, + + // Indicates that the QuicSocket has entered a graceful + // closing phase, indicating that no additional + QUICSOCKET_FLAGS_GRACEFUL_CLOSE = 0x1, + QUICSOCKET_FLAGS_WAITING_FOR_CALLBACKS = 0x2, + QUICSOCKET_FLAGS_SERVER_LISTENING = 0x4, + QUICSOCKET_FLAGS_SERVER_BUSY = 0x8, + QUICSOCKET_FLAGS_DISABLE_STATELESS_RESET = 0x10 + }; + + void set_flag(QuicSocketFlags flag, bool on = true) { + if (on) + flags_ |= flag; + else + flags_ &= ~flag; + } + + bool is_flag_set(QuicSocketFlags flag) const { + return flags_ & flag; + } + + void set_option(QuicSocketOptions option, bool on = true) { + if (on) + options_ |= option; + else + options_ &= ~option; + } + + bool is_option_set(QuicSocketOptions option) const { + return options_ & option; + } + + ngtcp2_mem alloc_info_; + + std::vector> endpoints_; + SocketAddress::Map> bound_endpoints_; + BaseObjectWeakPtr preferred_endpoint_; + + uint32_t flags_ = QUICSOCKET_FLAGS_NONE; + uint32_t options_; + uint32_t server_options_; + + size_t max_connections_ = DEFAULT_MAX_CONNECTIONS; + size_t max_connections_per_host_ = DEFAULT_MAX_CONNECTIONS_PER_HOST; + size_t current_ngtcp2_memory_ = 0; + size_t max_stateless_resets_per_host_ = DEFAULT_MAX_STATELESS_RESETS_PER_HOST; + + uint64_t retry_token_expiration_; + + // Used to specify diagnostic packet loss probabilities + double rx_loss_ = 0.0; + double tx_loss_ = 0.0; + + QuicSocketListener* listener_; + JSQuicSocketListener default_listener_; + QuicSessionConfig server_session_config_; + QlogMode qlog_ = QlogMode::kDisabled; + BaseObjectPtr server_secure_context_; + std::string server_alpn_; + QuicCID::Map> sessions_; + QuicCID::Map dcid_to_scid_; + + uint8_t token_secret_[kTokenSecretLen]; + uint8_t reset_token_secret_[NGTCP2_STATELESS_RESET_TOKENLEN]; + + // Counts the number of active connections per remote + // address. A custom std::hash specialization for + // sockaddr instances is used. Values are incremented + // when a QuicSession is added to the socket, and + // decremented when the QuicSession is removed. If the + // value reaches the value of max_connections_per_host_, + // attempts to create new connections will be ignored + // until the value falls back below the limit. + SocketAddress::Map addr_counts_; + + // Counts the number of stateless resets sent per + // remote address. + // TODO(@jasnell): this counter persists through the + // lifetime of the QuicSocket, and therefore can become + // a possible risk. Specifically, a malicious peer could + // attempt the local peer to count an increasingly large + // number of remote addresses. Need to mitigate the + // potential risk. + SocketAddress::Map reset_counts_; + + // Counts the number of retry attempts sent per + // remote address. + + StatelessResetToken::Map token_map_; + + // The validated_addrs_ vector is used as an LRU cache for + // validated addresses only when the VALIDATE_ADDRESS_LRU + // option is set. + typedef size_t SocketAddressHash; + std::deque validated_addrs_; + + class SendWrap : public ReqWrap { + public: + SendWrap(Environment* env, + v8::Local req_wrap_obj, + size_t total_length_); + + void set_packet(std::unique_ptr packet) { + packet_ = std::move(packet); + } + QuicPacket* packet() { return packet_.get(); } + void set_session(BaseObjectPtr session) { session_ = session; } + size_t total_length() const { return total_length_; } + + SET_SELF_SIZE(SendWrap); + std::string MemoryInfoName() const override; + void MemoryInfo(MemoryTracker* tracker) const override; + + private: + BaseObjectPtr session_; + std::unique_ptr packet_; + size_t total_length_; + }; + + SendWrap* last_created_send_wrap_ = nullptr; + + friend class QuicSocketListener; +}; + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_SOCKET_H_ diff --git a/src/quic/node_quic_state.h b/src/quic/node_quic_state.h new file mode 100644 index 00000000000000..a2097f0d55f0a5 --- /dev/null +++ b/src/quic/node_quic_state.h @@ -0,0 +1,72 @@ +#ifndef SRC_QUIC_NODE_QUIC_STATE_H_ +#define SRC_QUIC_NODE_QUIC_STATE_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "aliased_buffer.h" + +namespace node { + +enum QuicSessionConfigIndex : int { + IDX_QUIC_SESSION_ACTIVE_CONNECTION_ID_LIMIT, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_LOCAL, + IDX_QUIC_SESSION_MAX_STREAM_DATA_BIDI_REMOTE, + IDX_QUIC_SESSION_MAX_STREAM_DATA_UNI, + IDX_QUIC_SESSION_MAX_DATA, + IDX_QUIC_SESSION_MAX_STREAMS_BIDI, + IDX_QUIC_SESSION_MAX_STREAMS_UNI, + IDX_QUIC_SESSION_MAX_IDLE_TIMEOUT, + IDX_QUIC_SESSION_MAX_PACKET_SIZE, + IDX_QUIC_SESSION_ACK_DELAY_EXPONENT, + IDX_QUIC_SESSION_DISABLE_MIGRATION, + IDX_QUIC_SESSION_MAX_ACK_DELAY, + IDX_QUIC_SESSION_CONFIG_COUNT +}; + +enum Http3ConfigIndex : int { + IDX_HTTP3_QPACK_MAX_TABLE_CAPACITY, + IDX_HTTP3_QPACK_BLOCKED_STREAMS, + IDX_HTTP3_MAX_HEADER_LIST_SIZE, + IDX_HTTP3_MAX_PUSHES, + IDX_HTTP3_MAX_HEADER_PAIRS, + IDX_HTTP3_MAX_HEADER_LENGTH, + IDX_HTTP3_CONFIG_COUNT +}; + +class QuicState { + public: + explicit QuicState(v8::Isolate* isolate) : + root_buffer( + isolate, + sizeof(quic_state_internal)), + quicsessionconfig_buffer( + isolate, + offsetof(quic_state_internal, quicsessionconfig_buffer), + IDX_QUIC_SESSION_CONFIG_COUNT + 1, + root_buffer), + http3config_buffer( + isolate, + offsetof(quic_state_internal, http3config_buffer), + IDX_HTTP3_CONFIG_COUNT +1, + root_buffer) { + } + + AliasedUint8Array root_buffer; + AliasedFloat64Array quicsessionconfig_buffer; + AliasedFloat64Array http3config_buffer; + + bool warn_trace_tls = true; + + private: + struct quic_state_internal { + // doubles first so that they are always sizeof(double)-aligned + double quicsessionconfig_buffer[IDX_QUIC_SESSION_CONFIG_COUNT + 1]; + double http3config_buffer[IDX_HTTP3_CONFIG_COUNT + 1]; + }; +}; + +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_STATE_H_ diff --git a/src/quic/node_quic_stream-inl.h b/src/quic/node_quic_stream-inl.h new file mode 100644 index 00000000000000..3da0f5fb3b57cf --- /dev/null +++ b/src/quic/node_quic_stream-inl.h @@ -0,0 +1,158 @@ +#ifndef SRC_QUIC_NODE_QUIC_STREAM_INL_H_ +#define SRC_QUIC_NODE_QUIC_STREAM_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "debug_utils-inl.h" +#include "node_quic_session.h" +#include "node_quic_stream.h" +#include "node_quic_buffer-inl.h" + +namespace node { +namespace quic { + +QuicStreamDirection QuicStream::direction() const { + return stream_id_ & 0b10 ? + QUIC_STREAM_UNIDIRECTIONAL : + QUIC_STREAM_BIRECTIONAL; +} + +QuicStreamOrigin QuicStream::origin() const { + return stream_id_ & 0b01 ? + QUIC_STREAM_SERVER : + QUIC_STREAM_CLIENT; +} + +bool QuicStream::is_flag_set(int32_t flag) const { + return flags_ & (1 << flag); +} + +void QuicStream::set_flag(int32_t flag, bool on) { + if (on) + flags_ |= (1 << flag); + else + flags_ &= ~(1 << flag); +} + +void QuicStream::set_final_size(uint64_t final_size) { + CHECK_EQ(GetStat(&QuicStreamStats::final_size), 0); + SetStat(&QuicStreamStats::final_size, final_size); +} + +bool QuicStream::is_destroyed() const { + return is_flag_set(QUICSTREAM_FLAG_DESTROYED); +} + +bool QuicStream::was_ever_writable() const { + if (direction() == QUIC_STREAM_UNIDIRECTIONAL) { + return session_->is_server() ? + origin() == QUIC_STREAM_SERVER : + origin() == QUIC_STREAM_CLIENT; + } + return true; +} + +bool QuicStream::is_writable() const { + return was_ever_writable() && !streambuf_.is_ended(); +} + +bool QuicStream::was_ever_readable() const { + if (direction() == QUIC_STREAM_UNIDIRECTIONAL) { + return session_->is_server() ? + origin() == QUIC_STREAM_CLIENT : + origin() == QUIC_STREAM_SERVER; + } + + return true; +} + +bool QuicStream::is_readable() const { + return was_ever_readable() && !is_flag_set(QUICSTREAM_FLAG_READ_CLOSED); +} + +void QuicStream::set_fin_sent() { + CHECK(!is_writable()); + set_flag(QUICSTREAM_FLAG_FIN_SENT); +} + +bool QuicStream::is_write_finished() const { + return is_flag_set(QUICSTREAM_FLAG_FIN_SENT) && + streambuf_.length() == 0; +} + +bool QuicStream::SubmitInformation(v8::Local headers) { + return session_->SubmitInformation(stream_id_, headers); +} + +bool QuicStream::SubmitHeaders(v8::Local headers, uint32_t flags) { + return session_->SubmitHeaders(stream_id_, headers, flags); +} + +bool QuicStream::SubmitTrailers(v8::Local headers) { + return session_->SubmitTrailers(stream_id_, headers); +} + +BaseObjectPtr QuicStream::SubmitPush( + v8::Local headers) { + return session_->SubmitPush(stream_id_, headers); +} + +void QuicStream::EndHeaders(int64_t push_id) { + Debug(this, "End Headers"); + // Upon completion of a block of headers, convert the + // vector of Header objects into an array of name+value + // pairs, then call the on_stream_headers function. + session()->application()->StreamHeaders( + stream_id_, + headers_kind_, + headers_, + push_id); + headers_.clear(); +} + +void QuicStream::set_headers_kind(QuicStreamHeadersKind kind) { + headers_kind_ = kind; +} + +void QuicStream::BeginHeaders(QuicStreamHeadersKind kind) { + Debug(this, "Beginning Headers"); + // Upon start of a new block of headers, ensure that any + // previously collected ones are cleaned up. + headers_.clear(); + set_headers_kind(kind); +} + +void QuicStream::Commit(size_t amount) { + CHECK(!is_destroyed()); + streambuf_.Seek(amount); +} + +void QuicStream::ResetStream(uint64_t app_error_code) { + // On calling shutdown, the stream will no longer be + // readable or writable, all any pending data in the + // streambuf_ will be canceled, and all data pending + // to be acknowledged at the ngtcp2 level will be + // abandoned. + BaseObjectPtr ptr(session_); + set_flag(QUICSTREAM_FLAG_READ_CLOSED); + session_->ResetStream(stream_id_, app_error_code); + streambuf_.Cancel(); + streambuf_.End(); +} + +void QuicStream::Schedule(Queue* queue) { + if (!stream_queue_.IsEmpty()) // Already scheduled? + return; + queue->PushBack(this); +} + +void QuicStream::Unschedule() { + stream_queue_.Remove(); +} + +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_STREAM_INL_H_ diff --git a/src/quic/node_quic_stream.cc b/src/quic/node_quic_stream.cc new file mode 100644 index 00000000000000..c4f3cdb3991e7c --- /dev/null +++ b/src/quic/node_quic_stream.cc @@ -0,0 +1,513 @@ +#include "node_quic_stream-inl.h" // NOLINT(build/include) +#include "async_wrap-inl.h" +#include "debug_utils-inl.h" +#include "env-inl.h" +#include "node.h" +#include "node_buffer.h" +#include "node_internals.h" +#include "stream_base-inl.h" +#include "node_sockaddr-inl.h" +#include "node_http_common-inl.h" +#include "node_quic_session-inl.h" +#include "node_quic_socket-inl.h" +#include "node_quic_util-inl.h" +#include "v8.h" +#include "uv.h" + +#include +#include +#include +#include + +namespace node { + +using v8::Array; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::ObjectTemplate; +using v8::String; +using v8::Value; + +namespace quic { + +QuicStream::QuicStream( + QuicSession* sess, + Local wrap, + int64_t stream_id, + int64_t push_id) + : AsyncWrap(sess->env(), wrap, AsyncWrap::PROVIDER_QUICSTREAM), + StreamBase(sess->env()), + StatsBase(sess->env(), wrap, + HistogramOptions::ACK | + HistogramOptions::RATE | + HistogramOptions::SIZE), + session_(sess), + stream_id_(stream_id), + push_id_(push_id) { + CHECK_NOT_NULL(sess); + Debug(this, "Created"); + StreamBase::AttachToObject(GetObject()); + ngtcp2_transport_params params; + ngtcp2_conn_get_local_transport_params(session()->connection(), ¶ms); + IncrementStat(&QuicStreamStats::max_offset, params.initial_max_data); +} + +QuicStream::~QuicStream() { + DebugStats(); +} + +template +void QuicStreamStatsTraits::ToString(const QuicStream& ptr, Fn&& add_field) { +#define V(_n, name, label) \ + add_field(label, ptr.GetStat(&QuicStreamStats::name)); + STREAM_STATS(V) +#undef V +} + +// Acknowledge is called when ngtcp2 has received an acknowledgement +// for one or more stream frames for this QuicStream. This will cause +// data stored in the streambuf_ outbound queue to be consumed and may +// result in the JavaScript callback for the write to be invoked. +void QuicStream::Acknowledge(uint64_t offset, size_t datalen) { + if (is_destroyed()) + return; + + // ngtcp2 guarantees that offset must always be greater + // than the previously received offset, but let's just + // make sure that holds. + CHECK_GE(offset, GetStat(&QuicStreamStats::max_offset_ack)); + SetStat(&QuicStreamStats::max_offset_ack, offset); + + Debug(this, "Acknowledging %d bytes", datalen); + + // Consumes the given number of bytes in the buffer. This may + // have the side-effect of causing the onwrite callback to be + // invoked if a complete chunk of buffered data has been acknowledged. + streambuf_.Consume(datalen); + + RecordAck(&QuicStreamStats::acked_at); +} + +// While not all QUIC applications will support headers, QuicStream +// includes basic, generic support for storing them. +bool QuicStream::AddHeader(std::unique_ptr header) { + size_t len = header->length(); + QuicApplication* app = session()->application(); + // We cannot add the header if we've either reached + // * the max number of header pairs or + // * the max number of header bytes + if (headers_.size() == app->max_header_pairs() || + current_headers_length_ + len > app->max_header_length()) { + return false; + } + + current_headers_length_ += header->length(); + Debug(this, "Header - %s", *header); + headers_.emplace_back(std::move(header)); + return true; +} + +std::string QuicStream::diagnostic_name() const { + return std::string("QuicStream ") + std::to_string(stream_id_) + + " (" + std::to_string(static_cast(get_async_id())) + + ", " + session_->diagnostic_name() + ")"; +} + +void QuicStream::Destroy() { + if (is_destroyed()) + return; + set_flag(QUICSTREAM_FLAG_DESTROYED); + set_flag(QUICSTREAM_FLAG_READ_CLOSED); + streambuf_.End(); + + // If there is data currently buffered in the streambuf_, + // then cancel will call out to invoke an arbitrary + // JavaScript callback (the on write callback). Within + // that callback, however, the QuicStream will no longer + // be usable to send or receive data. + streambuf_.Cancel(); + CHECK_EQ(streambuf_.length(), 0); + + // The QuicSession maintains a map of std::unique_ptrs to + // QuicStream instances. Removing this here will cause + // this QuicStream object to be deconstructed, so the + // QuicStream object will no longer exist after this point. + session_->RemoveStream(stream_id_); +} + +// Do shutdown is called when the JS stream writable side is closed. +// If we're not within an ngtcp2 callback, this will trigger the +// QuicSession to send any pending data. Any time after this is +// called, a final stream frame will be sent for this QuicStream, +// but it may not be sent right away. +int QuicStream::DoShutdown(ShutdownWrap* req_wrap) { + if (is_destroyed()) + return UV_EPIPE; + + QuicSession::SendSessionScope send_scope(session(), true); + + if (is_writable()) { + Debug(this, "Shutdown writable side"); + RecordTimestamp(&QuicStreamStats::closing_at); + streambuf_.End(); + session()->ResumeStream(stream_id_); + } + + return 1; +} + +int QuicStream::DoWrite( + WriteWrap* req_wrap, + uv_buf_t* bufs, + size_t nbufs, + uv_stream_t* send_handle) { + CHECK_NULL(send_handle); + + // A write should not have happened if we've been destroyed or + // the QuicStream is no longer (or was never) writable. + if (is_destroyed() || !is_writable()) { + req_wrap->Done(UV_EPIPE); + return 0; + } + + // Nothing to write. + size_t length = get_length(bufs, nbufs); + if (length == 0) { + req_wrap->Done(0); + return 0; + } + + QuicSession::SendSessionScope send_scope(session(), true); + + Debug(this, "Queuing %" PRIu64 " bytes of data from %d buffers", + length, nbufs); + IncrementStat(&QuicStreamStats::bytes_sent, static_cast(length)); + + BaseObjectPtr strong_ref{req_wrap->GetAsyncWrap()}; + // The list of buffers will be appended onto streambuf_ without + // copying. Those will remain in the buffer until the serialized + // stream frames are acknowledged. + // This callback function will be invoked once this + // complete batch of buffers has been acknowledged + // by the peer. This will have the side effect of + // blocking additional pending writes from the + // javascript side, so writing data to the stream + // will be throttled by how quickly the peer is + // able to acknowledge stream packets. This is good + // in the sense of providing back-pressure, but + // also means that writes will be significantly + // less performant unless written in batches. + streambuf_.Push( + bufs, + nbufs, + [req_wrap, strong_ref](int status) { + req_wrap->Done(status); + }); + + session()->ResumeStream(stream_id_); + + return 0; +} + +bool QuicStream::IsAlive() { + return !is_destroyed() && !IsClosing(); +} + +bool QuicStream::IsClosing() { + return !is_writable() && !is_readable(); +} + +int QuicStream::ReadStart() { + CHECK(!is_destroyed()); + CHECK(is_readable()); + set_flag(QUICSTREAM_FLAG_READ_STARTED); + set_flag(QUICSTREAM_FLAG_READ_PAUSED, false); + IncrementStat( + &QuicStreamStats::max_offset, + inbound_consumed_data_while_paused_); + session_->ExtendStreamOffset(id(), inbound_consumed_data_while_paused_); + return 0; +} + +int QuicStream::ReadStop() { + CHECK(!is_destroyed()); + CHECK(is_readable()); + set_flag(QUICSTREAM_FLAG_READ_PAUSED); + return 0; +} + +void QuicStream::IncrementStats(size_t datalen) { + uint64_t len = static_cast(datalen); + IncrementStat(&QuicStreamStats::bytes_received, len); + RecordRate(&QuicStreamStats::received_at); + RecordSize(len); +} + +void QuicStream::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("buffer", &streambuf_); + StatsBase::StatsMemoryInfo(tracker); + tracker->TrackField("headers", headers_); +} + +BaseObjectPtr QuicStream::New( + QuicSession* session, + int64_t stream_id, + int64_t push_id) { + Local obj; + if (!session->env() + ->quicserverstream_instance_template() + ->NewInstance(session->env()->context()).ToLocal(&obj)) { + return {}; + } + BaseObjectPtr stream = + MakeDetachedBaseObject( + session, + obj, + stream_id, + push_id); + CHECK(stream); + session->AddStream(stream); + return stream; +} + +// Passes chunks of data on to the JavaScript side as soon as they are +// received but only if we're still readable. The caller of this must have a +// HandleScope. +// +// Note that this is pushing data to the JS side regardless of whether +// anything is listening. For flow-control, we only send window updates +// to the sending peer if the stream is in flowing mode, so the sender +// should not be sending too much data. +void QuicStream::ReceiveData( + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset) { + CHECK(!is_destroyed()); + Debug(this, "Receiving %d bytes. Final? %s. Readable? %s", + datalen, + fin ? "yes" : "no", + is_readable() ? "yes" : "no"); + + // If the QuicStream is not (or was never) readable, just ignore the chunk. + if (!is_readable()) + return; + + // ngtcp2 guarantees that datalen will only be 0 if fin is set. + // Let's just make sure. + CHECK(datalen > 0 || fin == 1); + + // ngtcp2 guarantees that offset is always greater than the previously + // received offset. Let's just make sure. + CHECK_GE(offset, GetStat(&QuicStreamStats::max_offset_received)); + SetStat(&QuicStreamStats::max_offset_received, offset); + + if (datalen > 0) { + // IncrementStats will update the data_rx_rate_ and data_rx_size_ + // histograms. These will provide data necessary to detect and + // prevent Slow Send DOS attacks specifically by allowing us to + // see if a connection is sending very small chunks of data at very + // slow speeds. It is important to emphasize, however, that slow send + // rates may be perfectly legitimate so we cannot simply take blanket + // action when slow rates are detected. Nor can we reliably define what + // a slow rate even is! Will will need to determine some reasonable + // default and allow user code to change the default as well as determine + // what action to take. The current strategy will be to trigger an event + // on the stream when data transfer rates are likely to be considered too + // slow. + IncrementStats(datalen); + + while (datalen > 0) { + uv_buf_t buf = EmitAlloc(datalen); + size_t avail = std::min(static_cast(buf.len), datalen); + + // For now, we're allocating and copying. Once we determine if we can + // safely switch to a non-allocated mode like we do with http2 streams, + // we can make this branch more efficient by using the LIKELY + // optimization. The way ngtcp2 currently works, however, we have + // to memcpy here. + if (UNLIKELY(buf.base == nullptr)) + buf.base = reinterpret_cast(const_cast(data)); + else + memcpy(buf.base, data, avail); + data += avail; + datalen -= avail; + // Capture read_paused before EmitRead in case user code callbacks + // alter the state when EmitRead is called. + bool read_paused = is_flag_set(QUICSTREAM_FLAG_READ_PAUSED); + EmitRead(avail, buf); + // Reading can be paused while we are processing. If that's + // the case, we still want to acknowledge the current bytes + // so that pausing does not throw off our flow control. + if (read_paused) { + inbound_consumed_data_while_paused_ += avail; + } else { + IncrementStat(&QuicStreamStats::max_offset, avail); + session_->ExtendStreamOffset(id(), avail); + } + } + } + + // When fin != 0, we've received that last chunk of data for this + // stream, indicating that the stream will no longer be readable. + if (fin) { + set_flag(QUICSTREAM_FLAG_FIN); + set_final_size(offset + datalen); + EmitRead(UV_EOF); + } +} + +int QuicStream::DoPull( + bob::Next next, + int options, + ngtcp2_vec* data, + size_t count, + size_t max_count_hint) { + return streambuf_.Pull( + std::move(next), + options, + data, + count, + max_count_hint); +} + +// JavaScript API +namespace { +void QuicStreamGetID(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + args.GetReturnValue().Set(static_cast(stream->id())); +} + +void OpenUnidirectionalStream(const FunctionCallbackInfo& args) { + CHECK(!args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + + int64_t stream_id; + if (!session->OpenUnidirectionalStream(&stream_id)) + return; + + BaseObjectPtr stream = QuicStream::New(session, stream_id); + args.GetReturnValue().Set(stream->object()); +} + +void OpenBidirectionalStream(const FunctionCallbackInfo& args) { + CHECK(!args.IsConstructCall()); + CHECK(args[0]->IsObject()); + QuicSession* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As()); + + int64_t stream_id; + if (!session->OpenBidirectionalStream(&stream_id)) + return; + + BaseObjectPtr stream = QuicStream::New(session, stream_id); + args.GetReturnValue().Set(stream->object()); +} + +void QuicStreamDestroy(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + stream->Destroy(); +} + +void QuicStreamReset(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + + QuicError error(env, args[0], args[1], QUIC_ERROR_APPLICATION); + + stream->ResetStream( + error.family == QUIC_ERROR_APPLICATION ? + error.code : static_cast(NGTCP2_NO_ERROR)); +} + +// Requests transmission of a block of informational headers. Not all +// QUIC Applications will support headers. If headers are not supported, +// This will set the return value to false, otherwise the return value +// is set to true +void QuicStreamSubmitInformation(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + args.GetReturnValue().Set(stream->SubmitInformation(args[0].As())); +} + +// Requests transmission of a block of initial headers. Not all +// QUIC Applications will support headers. If headers are not supported, +// this will set the return value to false, otherwise the return value +// is set to true. For http/3, these may be request or response headers. +void QuicStreamSubmitHeaders(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + uint32_t flags = QUICSTREAM_HEADER_FLAGS_NONE; + CHECK(args[1]->Uint32Value(stream->env()->context()).To(&flags)); + args.GetReturnValue().Set(stream->SubmitHeaders(args[0].As(), flags)); +} + +// Requests transmission of a block of trailing headers. Not all +// QUIC Applications will support headers. If headers are not supported, +// this will set the return value to false, otherwise the return value +// is set to true. +void QuicStreamSubmitTrailers(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + args.GetReturnValue().Set(stream->SubmitTrailers(args[0].As())); +} + +// Requests creation of a push stream. Not all QUIC Applications will +// support push streams. If pushes are not supported, the return value +// will be undefined, otherwise the return value will be the created +// QuicStream representing the push. +void QuicStreamSubmitPush(const FunctionCallbackInfo& args) { + QuicStream* stream; + ASSIGN_OR_RETURN_UNWRAP(&stream, args.Holder()); + CHECK(args[0]->IsArray()); + BaseObjectPtr push_stream = + stream->SubmitPush(args[0].As()); + if (push_stream) + args.GetReturnValue().Set(push_stream->object()); +} + +} // namespace + +void QuicStream::Initialize( + Environment* env, + Local target, + Local context) { + Isolate* isolate = env->isolate(); + Local class_name = FIXED_ONE_BYTE_STRING(isolate, "QuicStream"); + Local stream = FunctionTemplate::New(env->isolate()); + stream->SetClassName(class_name); + stream->Inherit(AsyncWrap::GetConstructorTemplate(env)); + StreamBase::AddMethods(env, stream); + Local streamt = stream->InstanceTemplate(); + streamt->SetInternalFieldCount(StreamBase::kInternalFieldCount); + streamt->Set(env->owner_symbol(), Null(env->isolate())); + env->SetProtoMethod(stream, "destroy", QuicStreamDestroy); + env->SetProtoMethod(stream, "resetStream", QuicStreamReset); + env->SetProtoMethod(stream, "id", QuicStreamGetID); + env->SetProtoMethod(stream, "submitInformation", QuicStreamSubmitInformation); + env->SetProtoMethod(stream, "submitHeaders", QuicStreamSubmitHeaders); + env->SetProtoMethod(stream, "submitTrailers", QuicStreamSubmitTrailers); + env->SetProtoMethod(stream, "submitPush", QuicStreamSubmitPush); + env->set_quicserverstream_instance_template(streamt); + target->Set(env->context(), + class_name, + stream->GetFunction(env->context()).ToLocalChecked()).FromJust(); + + env->SetMethod(target, "openBidirectionalStream", OpenBidirectionalStream); + env->SetMethod(target, "openUnidirectionalStream", OpenUnidirectionalStream); +} + +} // namespace quic +} // namespace node diff --git a/src/quic/node_quic_stream.h b/src/quic/node_quic_stream.h new file mode 100644 index 00000000000000..14c4c3c1e56e48 --- /dev/null +++ b/src/quic/node_quic_stream.h @@ -0,0 +1,385 @@ +#ifndef SRC_QUIC_NODE_QUIC_STREAM_H_ +#define SRC_QUIC_NODE_QUIC_STREAM_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "memory_tracker-inl.h" +#include "async_wrap.h" +#include "env.h" +#include "node_http_common.h" +#include "node_quic_util.h" +#include "stream_base-inl.h" +#include "util-inl.h" +#include "v8.h" + +#include +#include + +namespace node { +namespace quic { + +class QuicSession; +class QuicStream; +class QuicApplication; + +using QuicHeader = NgHeaderImpl; + +enum QuicStreamHeaderFlags : uint32_t { + // No flags + QUICSTREAM_HEADER_FLAGS_NONE = 0, + + // Set if the initial headers are considered + // terminal (that is, the stream should be closed + // after transmitting the headers). If headers are + // not supported by the QUIC Application, flag is + // ignored. + QUICSTREAM_HEADER_FLAGS_TERMINAL = 1 +}; + +enum QuicStreamHeadersKind : int { + QUICSTREAM_HEADERS_KIND_NONE = 0, + QUICSTREAM_HEADERS_KIND_INFORMATIONAL, + QUICSTREAM_HEADERS_KIND_INITIAL, + QUICSTREAM_HEADERS_KIND_TRAILING, + QUICSTREAM_HEADERS_KIND_PUSH +}; + +#define STREAM_STATS(V) \ + V(CREATED_AT, created_at, "Created At") \ + V(RECEIVED_AT, received_at, "Last Received At") \ + V(ACKED_AT, acked_at, "Last Acknowledged At") \ + V(CLOSING_AT, closing_at, "Closing At") \ + V(BYTES_RECEIVED, bytes_received, "Bytes Received") \ + V(BYTES_SENT, bytes_sent, "Bytes Sent") \ + V(MAX_OFFSET, max_offset, "Max Offset") \ + V(MAX_OFFSET_ACK, max_offset_ack, "Max Acknowledged Offset") \ + V(MAX_OFFSET_RECV, max_offset_received, "Max Received Offset") \ + V(FINAL_SIZE, final_size, "Final Size") + +#define V(name, _, __) IDX_QUIC_STREAM_STATS_##name, +enum QuicStreamStatsIdx : int { + STREAM_STATS(V) + IDX_QUIC_STREAM_STATS_COUNT +}; +#undef V + +#define V(_, name, __) uint64_t name; +struct QuicStreamStats { + STREAM_STATS(V) +}; +#undef V + +struct QuicStreamStatsTraits { + using Stats = QuicStreamStats; + using Base = QuicStream; + + template + static void ToString(const Base& ptr, Fn&& add_field); +}; + +enum QuicStreamStates : uint32_t { + // QuicStream is fully open. Readable and Writable + QUICSTREAM_FLAG_INITIAL = 0, + + // QuicStream Read State is closed because a final stream frame + // has been received from the peer or the QuicStream is unidirectional + // outbound only (i.e. it was never readable) + QUICSTREAM_FLAG_READ_CLOSED, + + // JavaScript side has switched into flowing mode (Readable side) + QUICSTREAM_FLAG_READ_STARTED, + + // JavaScript side has paused the flow of data (Readable side) + QUICSTREAM_FLAG_READ_PAUSED, + + // QuicStream has received a final stream frame (Readable side) + QUICSTREAM_FLAG_FIN, + + // QuicStream has sent a final stream frame (Writable side) + QUICSTREAM_FLAG_FIN_SENT, + + // QuicStream has been destroyed + QUICSTREAM_FLAG_DESTROYED +}; + +enum QuicStreamDirection { + // The QuicStream is readable and writable in both directions + QUIC_STREAM_BIRECTIONAL, + + // The QuicStream is writable and readable in only one direction. + // The direction depends on the QuicStreamOrigin. + QUIC_STREAM_UNIDIRECTIONAL +}; + +enum QuicStreamOrigin { + // The QuicStream was created by the server. + QUIC_STREAM_SERVER, + + // The QuicStream was created by the client. + QUIC_STREAM_CLIENT +}; + +// QuicStream's are simple data flows that, fortunately, do not +// require much. They may be: +// +// * Bidirectional or Unidirectional +// * Server or Client Initiated +// +// The flow direction and origin of the stream are important in +// determining the write and read state (Open or Closed). Specifically: +// +// A Unidirectional stream originating with the Server is: +// +// * Server Writable (Open) but not Client Writable (Closed) +// * Client Readable (Open) but not Server Readable (Closed) +// +// Likewise, a Unidirectional stream originating with the +// Client is: +// +// * Client Writable (Open) but not Server Writable (Closed) +// * Server Readable (Open) but not Client Readable (Closed) +// +// Bidirectional Stream States +// +------------+--------------+--------------------+---------------------+ +// | | Initiated By | Initial Read State | Initial Write State | +// +------------+--------------+--------------------+---------------------+ +// | On Server | Server | Open | Open | +// +------------+--------------+--------------------+---------------------+ +// | On Server | Client | Open | Open | +// +------------+--------------+--------------------+---------------------+ +// | On Client | Server | Open | Open | +// +------------+--------------+--------------------+---------------------+ +// | On Client | Client | Open | Open | +// +------------+--------------+--------------------+---------------------+ +// +// Unidirectional Stream States +// +------------+--------------+--------------------+---------------------+ +// | | Initiated By | Initial Read State | Initial Write State | +// +------------+--------------+--------------------+---------------------+ +// | On Server | Server | Closed | Open | +// +------------+--------------+--------------------+---------------------+ +// | On Server | Client | Open | Closed | +// +------------+--------------+--------------------+---------------------+ +// | On Client | Server | Open | Closed | +// +------------+--------------+--------------------+---------------------+ +// | On Client | Client | Closed | Open | +// +------------+--------------+--------------------+---------------------+ +// +// All data sent via the QuicStream is buffered internally until either +// receipt is acknowledged from the peer or attempts to send are abandoned. +// +// A QuicStream may be in a fully Closed (Read and Write) state but still +// have unacknowledged data in it's outbound queue. +// +// A QuicStream is gracefully closed when (a) both Read and Write states +// are Closed, (b) all queued data has been acknowledged. +// +// The JavaScript Writable side of the QuicStream may be shutdown before +// all pending queued data has been serialized to frames. During this state, +// no additional data may be queued to send. +// +// The Write state of a QuicStream will not be closed while there is still +// pending writes on the JavaScript side. +// +// The QuicStream may be forcefully closed immediately using destroy(err). +// This causes all queued data and pending JavaScript writes to be +// abandoned, and causes the QuicStream to be immediately closed at the +// ngtcp2 level. +class QuicStream : public AsyncWrap, + public bob::SourceImpl, + public StreamBase, + public StatsBase { + public: + static void Initialize( + Environment* env, + v8::Local target, + v8::Local context); + + static BaseObjectPtr New( + QuicSession* session, + int64_t stream_id, + int64_t push_id = 0); + + QuicStream( + QuicSession* session, + v8::Local target, + int64_t stream_id, + int64_t push_id = 0); + + ~QuicStream() override; + + std::string diagnostic_name() const override; + + int64_t id() const { return stream_id_; } + int64_t push_id() const { return push_id_; } + + QuicSession* session() const { return session_.get(); } + + // A QuicStream can be either uni- or bi-directional. + inline QuicStreamDirection direction() const; + + // A QuicStream can be initiated by either the client + // or the server. + inline QuicStreamOrigin origin() const; + + // The QuicStream has been destroyed and is no longer usable. + inline bool is_destroyed() const; + + // A QuicStream will not be writable if: + // - The streambuf_ is ended + // - It is a Unidirectional stream originating from the peer + inline bool is_writable() const; + + // A QuicStream will not be readable if: + // - The QUICSTREAM_FLAG_READ_CLOSED flag is set or + // - It is a Unidirectional stream originating from the local peer. + inline bool is_readable() const; + + // Records the fact that a final stream frame has been + // serialized and sent to the peer. There still may be + // unacknowledged data in the outbound queue, but no + // additional frames may be sent for the stream other + // than reset stream. + inline void set_fin_sent(); + + // IsWriteFinished will return true if a final stream frame + // has been sent and all data has been acknowledged (the + // send buffer is empty). + inline bool is_write_finished() const; + + // Specifies the kind of headers currently being processed. + inline void set_headers_kind(QuicStreamHeadersKind kind); + + // Set the final size for the QuicStream + inline void set_final_size(uint64_t final_size); + + // The final size is the maximum amount of data that has been + // acknowleged to have been received for a QuicStream. + uint64_t final_size() const { + return GetStat(&QuicStreamStats::final_size); + } + + // Marks the given data range as having been acknowledged. + // This means that the data range may be released from + // memory. + void Acknowledge(uint64_t offset, size_t datalen); + + // Destroy the QuicStream and render it no longer usable. + void Destroy(); + + // Buffers chunks of data to be written to the QUIC connection. + int DoWrite( + WriteWrap* req_wrap, + uv_buf_t* bufs, + size_t nbufs, + uv_stream_t* send_handle) override; + + // Returns false if the header cannot be added. This will + // typically only happen if a maximimum number of headers + // has been reached. + bool AddHeader(std::unique_ptr header); + + // Some QUIC applications support headers, others do not. + // The following methods allow consistent handling of + // headers at the QuicStream level regardless of the + // protocol. For applications that do not support headers, + // these are simply not used. + inline void BeginHeaders( + QuicStreamHeadersKind kind = QUICSTREAM_HEADERS_KIND_NONE); + + // Indicates an amount of unacknowledged data that has been + // submitted to the QUIC connection. + inline void Commit(size_t amount); + + inline void EndHeaders(int64_t push_id = 0); + + // Passes a chunk of data on to the QuicStream listener. + void ReceiveData( + int fin, + const uint8_t* data, + size_t datalen, + uint64_t offset); + + // Resets the QUIC stream, sending a signal to the peer that + // no additional data will be transmitted for this stream. + inline void ResetStream(uint64_t app_error_code = 0); + + // Submits informational headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitInformation(v8::Local headers); + + // Submits initial headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitHeaders(v8::Local headers, uint32_t flags); + + // Submits trailing headers. Returns false if headers are not + // supported on the underlying QuicApplication. + inline bool SubmitTrailers(v8::Local headers); + + inline BaseObjectPtr SubmitPush(v8::Local headers); + + // Required for StreamBase + bool IsAlive() override; + bool IsClosing() override; + int ReadStart() override; + int ReadStop() override; + int DoShutdown(ShutdownWrap* req_wrap) override; + + AsyncWrap* GetAsyncWrap() override { return this; } + + // Required for MemoryRetainer + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicStream) + SET_SELF_SIZE(QuicStream) + + protected: + int DoPull( + bob::Next next, + int options, + ngtcp2_vec* data, + size_t count, + size_t max_count_hint) override; + + private: + inline bool is_flag_set(int32_t flag) const; + inline void set_flag(int32_t flag, bool on = true); + + // WasEverWritable returns true if it is a bidirectional stream, + // or a Unidirectional stream originating from the local peer. + // If was_ever_writable() is false, then no stream frames should + // ever be sent from the local peer, including final stream frames. + inline bool was_ever_writable() const; + + // WasEverReadable returns true if it is a bidirectional stream, + // or a Unidirectional stream originating from the remote + // peer. + inline bool was_ever_readable() const; + + void IncrementStats(size_t datalen); + + BaseObjectWeakPtr session_; + QuicBuffer streambuf_; + + int64_t stream_id_ = 0; + int64_t push_id_ = 0; + uint32_t flags_ = QUICSTREAM_FLAG_INITIAL; + size_t inbound_consumed_data_while_paused_ = 0; + + std::vector> headers_; + QuicStreamHeadersKind headers_kind_; + size_t current_headers_length_ = 0; + + ListNode stream_queue_; + public: + // Linked List of QuicStream objects + using Queue = ListHead; + inline void Schedule(Queue* queue); + inline void Unschedule(); +}; + +} // namespace quic +} // namespace node + +#endif // NODE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_STREAM_H_ diff --git a/src/quic/node_quic_util-inl.h b/src/quic/node_quic_util-inl.h new file mode 100644 index 00000000000000..71b5ceab06fbc2 --- /dev/null +++ b/src/quic/node_quic_util-inl.h @@ -0,0 +1,460 @@ +#ifndef SRC_QUIC_NODE_QUIC_UTIL_INL_H_ +#define SRC_QUIC_NODE_QUIC_UTIL_INL_H_ + +#include "debug_utils-inl.h" +#include "node_internals.h" +#include "node_quic_crypto.h" +#include "node_quic_util.h" +#include "memory_tracker-inl.h" +#include "env-inl.h" +#include "histogram-inl.h" +#include "string_bytes.h" +#include "util-inl.h" +#include "uv.h" + +#include + +namespace node { + +namespace quic { + +QuicPath::QuicPath( + const SocketAddress& local, + const SocketAddress& remote) { + ngtcp2_addr_init( + &this->local, + local.data(), + local.length(), + const_cast(&local)); + ngtcp2_addr_init( + &this->remote, + remote.data(), + remote.length(), + const_cast(&remote)); +} + +size_t QuicCID::Hash::operator()(const QuicCID& token) const { + size_t hash = 0; + for (size_t n = 0; n < token->datalen; n++) { + hash ^= std::hash{}(token->data[n]) + 0x9e3779b9 + + (hash << 6) + (hash >> 2); + } + return hash; +} + +bool QuicCID::operator==(const QuicCID& other) const { + return memcmp(cid()->data, other.cid()->data, cid()->datalen) == 0; +} + +bool QuicCID::operator!=(const QuicCID& other) const { + return !(*this == other); +} + +std::string QuicCID::ToString() const { + std::vector dest(ptr_->datalen * 2 + 1); + dest[dest.size() - 1] = '\0'; + size_t written = StringBytes::hex_encode( + reinterpret_cast(ptr_->data), + ptr_->datalen, + dest.data(), + dest.size()); + return std::string(dest.data(), written); +} + +size_t GetMaxPktLen(const SocketAddress& addr) { + return addr.family() == AF_INET6 ? + NGTCP2_MAX_PKTLEN_IPV6 : + NGTCP2_MAX_PKTLEN_IPV4; +} + +Timer::Timer(Environment* env, std::function fn) + : env_(env), + fn_(fn) { + uv_timer_init(env_->event_loop(), &timer_); + timer_.data = this; +} + +void Timer::Stop() { + if (stopped_) + return; + stopped_ = true; + + if (timer_.data == this) { + uv_timer_stop(&timer_); + timer_.data = nullptr; + } +} + +// If the timer is not currently active, interval must be either 0 or greater. +// If the timer is already active, interval is ignored. +void Timer::Update(uint64_t interval) { + if (stopped_) + return; + uv_timer_start(&timer_, OnTimeout, interval, interval); + uv_unref(reinterpret_cast(&timer_)); +} + +void Timer::Free(Timer* timer) { + timer->env_->CloseHandle( + reinterpret_cast(&timer->timer_), + [&](uv_handle_t* timer) { + Timer* t = ContainerOf( + &Timer::timer_, + reinterpret_cast(timer)); + delete t; + }); +} + +void Timer::OnTimeout(uv_timer_t* timer) { + Timer* t = ContainerOf(&Timer::timer_, timer); + t->fn_(); +} + +QuicError::QuicError( + int32_t family_, + uint64_t code_) : + family(family_), + code(code_) {} + +QuicError::QuicError( + int32_t family_, + int code_) : + family(family_) { + switch (family) { + case QUIC_ERROR_CRYPTO: + code_ |= NGTCP2_CRYPTO_ERROR; + // Fall-through... + case QUIC_ERROR_SESSION: + code = ngtcp2_err_infer_quic_transport_error_code(code_); + break; + case QUIC_ERROR_APPLICATION: + code = code_; + break; + default: + UNREACHABLE(); + } +} + +QuicError::QuicError(ngtcp2_connection_close_error_code ccec) : + family(QUIC_ERROR_SESSION), + code(ccec.error_code) { + switch (ccec.type) { + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION: + family = QUIC_ERROR_APPLICATION; + break; + case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT: + if (code & NGTCP2_CRYPTO_ERROR) + family = QUIC_ERROR_CRYPTO; + break; + default: + UNREACHABLE(); + } +} + +QuicError::QuicError( + Environment* env, + v8::Local codeArg, + v8::Local familyArg, + int32_t family_) : + family(family_), + code(NGTCP2_NO_ERROR) { + if (codeArg->IsBigInt()) { + code = codeArg.As()->Int64Value(); + } else if (codeArg->IsNumber()) { + double num = 0; + CHECK(codeArg->NumberValue(env->context()).To(&num)); + code = static_cast(num); + } + if (familyArg->IsNumber()) { + CHECK(familyArg->Int32Value(env->context()).To(&family)); + } +} + +const char* QuicError::family_name() { + switch (family) { + case QUIC_ERROR_SESSION: + return "Session"; + case QUIC_ERROR_APPLICATION: + return "Application"; + case QUIC_ERROR_CRYPTO: + return "Crypto"; + default: + UNREACHABLE(); + } +} + +const ngtcp2_cid* QuicPreferredAddress::cid() const { + return &paddr_->cid; +} + +const uint8_t* QuicPreferredAddress::stateless_reset_token() const { + return paddr_->stateless_reset_token; +} + +std::string QuicPreferredAddress::preferred_ipv6_address() const { + char host[NI_MAXHOST]; + // Return an empty string if unable to convert... + if (uv_inet_ntop(AF_INET6, paddr_->ipv6_addr, host, sizeof(host)) != 0) + return std::string(); + + return std::string(host); +} +std::string QuicPreferredAddress::preferred_ipv4_address() const { + char host[NI_MAXHOST]; + // Return an empty string if unable to convert... + if (uv_inet_ntop(AF_INET, paddr_->ipv4_addr, host, sizeof(host)) != 0) + return std::string(); + + return std::string(host); +} + +int16_t QuicPreferredAddress::preferred_ipv6_port() const { + return paddr_->ipv6_port; +} +int16_t QuicPreferredAddress::preferred_ipv4_port() const { + return paddr_->ipv4_port; +} + +bool QuicPreferredAddress::Use(int family) const { + uv_getaddrinfo_t req; + + if (!ResolvePreferredAddress(family, &req)) + return false; + + dest_->addrlen = req.addrinfo->ai_addrlen; + memcpy(dest_->addr, req.addrinfo->ai_addr, req.addrinfo->ai_addrlen); + uv_freeaddrinfo(req.addrinfo); + return true; +} + +bool QuicPreferredAddress::ResolvePreferredAddress( + int local_address_family, + uv_getaddrinfo_t* req) const { + int af; + const uint8_t* binaddr; + uint16_t port; + switch (local_address_family) { + case AF_INET: + if (paddr_->ipv4_port > 0) { + af = AF_INET; + binaddr = paddr_->ipv4_addr; + port = paddr_->ipv4_port; + break; + } + return false; + case AF_INET6: + if (paddr_->ipv6_port > 0) { + af = AF_INET6; + binaddr = paddr_->ipv6_addr; + port = paddr_->ipv6_port; + break; + } + return false; + default: + UNREACHABLE(); + } + + char host[NI_MAXHOST]; + if (uv_inet_ntop(af, binaddr, host, sizeof(host)) != 0) + return false; + + addrinfo hints{}; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + + // Unfortunately ngtcp2 requires the selection of the + // preferred address to be synchronous, which means we + // have to do a sync resolve using uv_getaddrinfo here. + return + uv_getaddrinfo( + env_->event_loop(), + req, + nullptr, + host, + std::to_string(port).c_str(), + &hints) == 0 && + req->addrinfo != nullptr; +} + +StatelessResetToken::StatelessResetToken( + uint8_t* token, + const uint8_t* secret, + const QuicCID& cid) : token_(token) { + GenerateResetToken(token, secret, cid); +} + +StatelessResetToken::StatelessResetToken( + const uint8_t* secret, + const QuicCID& cid) + : token_(buf_) { + GenerateResetToken(buf_, secret, cid); +} + +std::string StatelessResetToken::ToString() const { + std::vector dest(NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1); + dest[dest.size() - 1] = '\0'; + size_t written = StringBytes::hex_encode( + reinterpret_cast(token_), + NGTCP2_STATELESS_RESET_TOKENLEN, + dest.data(), + dest.size()); + return std::string(dest.data(), written); +} + +size_t StatelessResetToken::Hash::operator()( + const StatelessResetToken& token) const { + size_t hash = 0; + for (size_t n = 0; n < NGTCP2_STATELESS_RESET_TOKENLEN; n++) + hash ^= std::hash{}(token.token_[n]) + 0x9e3779b9 + + (hash << 6) + (hash >> 2); + return hash; +} + +bool StatelessResetToken::operator==(const StatelessResetToken& other) const { + return memcmp(data(), other.data(), NGTCP2_STATELESS_RESET_TOKENLEN) == 0; +} + +bool StatelessResetToken::operator!=(const StatelessResetToken& other) const { + return !(*this == other); +} + +template +StatsBase::StatsBase( + Environment* env, + v8::Local wrap, + int options) + : stats_buffer_( + env->isolate(), + sizeof(typename T::Stats) / sizeof(uint64_t), + reinterpret_cast(&stats_)) { + static constexpr uint64_t kMax = std::numeric_limits::max(); + stats_.created_at = uv_hrtime(); + + // TODO(@jasnell): The follow are checks instead of handling + // the error. Before this code moves out of experimental, + // these should be change to properly handle the error. + + wrap->DefineOwnProperty( + env->context(), + env->stats_string(), + stats_buffer_.GetJSArray(), + v8::PropertyAttribute::ReadOnly).Check(); + + if (options & HistogramOptions::ACK) { + ack_ = HistogramBase::New(env, 1, kMax); + wrap->DefineOwnProperty( + env->context(), + env->ack_string(), + ack_->object(), + v8::PropertyAttribute::ReadOnly).Check(); + } + + if (options & HistogramOptions::RATE) { + rate_ = HistogramBase::New(env, 1, kMax); + wrap->DefineOwnProperty( + env->context(), + env->rate_string(), + rate_->object(), + v8::PropertyAttribute::ReadOnly).Check(); + } + + if (options & HistogramOptions::SIZE) { + size_ = HistogramBase::New(env, 1, kMax); + wrap->DefineOwnProperty( + env->context(), + env->size_string(), + size_->object(), + v8::PropertyAttribute::ReadOnly).Check(); + } +} + +template +void StatsBase::IncrementStat(uint64_t T::Stats::*member, uint64_t amount) { + static constexpr uint64_t kMax = std::numeric_limits::max(); + stats_.*member += std::min(amount, kMax - stats_.*member); +} + +template +void StatsBase::SetStat(uint64_t T::Stats::*member, uint64_t value) { + stats_.*member = value; +} + +template +void StatsBase::RecordTimestamp(uint64_t T::Stats::*member) { + stats_.*member = uv_hrtime(); +} + +template +uint64_t StatsBase::GetStat(uint64_t T::Stats::*member) const { + return stats_.*member; +} + +template +inline void StatsBase::RecordRate(uint64_t T::Stats::*member) { + CHECK(rate_); + uint64_t received_at = GetStat(member); + uint64_t now = uv_hrtime(); + if (received_at > 0) + rate_->Record(now - received_at); + SetStat(member, now); +} + +template +inline void StatsBase::RecordSize(uint64_t val) { + CHECK(size_); + size_->Record(val); +} + +template +inline void StatsBase::RecordAck(uint64_t T::Stats::*member) { + CHECK(ack_); + uint64_t acked_at = GetStat(member); + uint64_t now = uv_hrtime(); + if (acked_at > 0) + ack_->Record(now - acked_at); + SetStat(member, now); +} + +template +void StatsBase::StatsMemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("stats_buffer", stats_buffer_); + tracker->TrackField("rate_histogram", rate_); + tracker->TrackField("size_histogram", size_); + tracker->TrackField("ack_histogram", ack_); +} + +template +void StatsBase::DebugStats() { + StatsDebug stats_debug(static_cast(this)); + Debug(static_cast(this), "Destroyed. %s", stats_debug); +} + +template +std::string StatsBase::StatsDebug::ToString() const { + std::string out = "Statistics:\n"; + auto add_field = [&out](const char* name, uint64_t val) { + out += " "; + out += std::string(name); + out += ": "; + out += std::to_string(val); + out += "\n"; + }; + add_field("Duration", uv_hrtime() - ptr->GetStat(&T::Stats::created_at)); + T::ToString(*ptr, add_field); + return out; +} + +template +size_t get_length(const T* vec, size_t count) { + CHECK_NOT_NULL(vec); + size_t len = 0; + for (size_t n = 0; n < count; n++) + len += vec[n].len; + return len; +} + +} // namespace quic +} // namespace node + +#endif // SRC_QUIC_NODE_QUIC_UTIL_INL_H_ diff --git a/src/quic/node_quic_util.h b/src/quic/node_quic_util.h new file mode 100644 index 00000000000000..54a5696605f631 --- /dev/null +++ b/src/quic/node_quic_util.h @@ -0,0 +1,392 @@ +#ifndef SRC_QUIC_NODE_QUIC_UTIL_H_ +#define SRC_QUIC_NODE_QUIC_UTIL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node.h" +#include "node_sockaddr.h" +#include "uv.h" +#include "v8.h" +#include "histogram.h" +#include "memory_tracker.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace node { +namespace quic { + +// k-constants are used internally, all-caps constants +// are exposed to javascript as constants (see node_quic.cc) + +constexpr size_t kMaxSizeT = std::numeric_limits::max(); +constexpr size_t kMaxValidateAddressLru = 10; +constexpr size_t kMinInitialQuicPktSize = 1200; +constexpr size_t kScidLen = NGTCP2_MAX_CIDLEN; +constexpr size_t kTokenRandLen = 16; +constexpr size_t kTokenSecretLen = 16; + +constexpr uint64_t DEFAULT_MAX_CONNECTIONS = + std::min(kMaxSizeT, kMaxSafeJsInteger); +constexpr uint64_t DEFAULT_MAX_CONNECTIONS_PER_HOST = 100; +constexpr uint64_t NGTCP2_APP_NOERROR = 0xff00; +constexpr uint64_t MIN_RETRYTOKEN_EXPIRATION = 1; +constexpr uint64_t MAX_RETRYTOKEN_EXPIRATION = 60; +constexpr uint64_t DEFAULT_ACTIVE_CONNECTION_ID_LIMIT = 2; +constexpr uint64_t DEFAULT_MAX_STREAM_DATA_BIDI_LOCAL = 256 * 1024; +constexpr uint64_t DEFAULT_MAX_STREAM_DATA_BIDI_REMOTE = 256 * 1024; +constexpr uint64_t DEFAULT_MAX_STREAM_DATA_UNI = 256 * 1024; +constexpr uint64_t DEFAULT_MAX_DATA = 1 * 1024 * 1024; +constexpr uint64_t DEFAULT_MAX_STATELESS_RESETS_PER_HOST = 10; +constexpr uint64_t DEFAULT_MAX_STREAMS_BIDI = 100; +constexpr uint64_t DEFAULT_MAX_STREAMS_UNI = 3; +constexpr uint64_t DEFAULT_MAX_IDLE_TIMEOUT = 10; +constexpr uint64_t DEFAULT_RETRYTOKEN_EXPIRATION = 10ULL; + +constexpr int ERR_FAILED_TO_CREATE_SESSION = -1; + +// The preferred address policy determines how a client QuicSession +// handles a server-advertised preferred address. As suggested, the +// preferred address is the address the server would prefer the +// client to use for subsequent communication for a QuicSession. +// The client may choose to ignore the preference but really shouldn't. +// We currently only support two options in the Node.js implementation, +// but additional options may be added later. +enum SelectPreferredAddressPolicy : int { + // Ignore the server-provided preferred address + QUIC_PREFERRED_ADDRESS_IGNORE, + // Accept the server-provided preferred address + QUIC_PREFERRED_ADDRESS_ACCEPT +}; + +// QUIC error codes generally fall into two distinct namespaces: +// Connection Errors and Application Errors. Connection errors +// are further subdivided into Crypto and non-Crypto. Application +// errors are entirely specific to the QUIC application being +// used. An easy rule of thumb is that Application errors are +// semantically associated with the ALPN identifier negotiated +// for the QuicSession. So, if a connection is closed with +// family: QUIC_ERROR_APPLICATION and code: 123, you have to +// look at the ALPN identifier to determine exactly what it +// means. Connection (Session) and Crypto errors, on the other +// hand, share the same meaning regardless of the ALPN. +enum QuicErrorFamily : int32_t { + QUIC_ERROR_SESSION, + QUIC_ERROR_CRYPTO, + QUIC_ERROR_APPLICATION +}; + +template class StatsBase; + +template +struct StatsTraits { + using Stats = T; + using Base = Q; + + template + static void ToString(const Q& ptr, Fn&& add_field); +}; + +// StatsBase is a utility help for classes (like QuicSession) +// that record performance statistics. The template takes a +// single Traits argument (see QuicStreamStatsTraits in +// node_quic_stream.h as an example). When the StatsBase +// is deconstructed, collected statistics are output to +// Debug automatically. +template +class StatsBase { + public: + // A StatsBase instance may have one of three histogram + // instances. One that records rate of data flow, one + // that records size of data chunk, and one that records + // rate of data ackwowledgement. These may be used in + // slightly different ways of different StatsBase + // instances or may be turned off entirely. + enum HistogramOptions { + NONE = 0, + RATE = 1, + SIZE = 2, + ACK = 4 + }; + + inline StatsBase( + Environment* env, + v8::Local wrap, + int options = HistogramOptions::NONE); + + inline ~StatsBase() = default; + + // The StatsDebug utility is used when StatsBase is destroyed + // to output statistical information to Debug. It is designed + // to only incur a performance cost constructing the debug + // output when Debug output is enabled. + struct StatsDebug { + typename T::Base* ptr; + explicit StatsDebug(typename T::Base* ptr_) : ptr(ptr_) {} + std::string ToString() const; + }; + + // Increments the given stat field by the given amount or 1 if + // no amount is specified. + inline void IncrementStat(uint64_t T::Stats::*member, uint64_t amount = 1); + + // Sets an entirely new value for the given stat field + inline void SetStat(uint64_t T::Stats::*member, uint64_t value); + + // Sets the given stat field to the current uv_hrtime() + inline void RecordTimestamp(uint64_t T::Stats::*member); + + // Gets the current value of the given stat field + inline uint64_t GetStat(uint64_t T::Stats::*member) const; + + // If the rate histogram is used, records the time elapsed + // between now and the timestamp specified by the member + // field. + inline void RecordRate(uint64_t T::Stats::*member); + + // If the size histogram is used, records the given size. + inline void RecordSize(uint64_t val); + + // If the ack rate histogram is used, records the time + // elapsed between now and the timestamp specified by + // the member field. + inline void RecordAck(uint64_t T::Stats::*member); + + inline void StatsMemoryInfo(MemoryTracker* tracker) const; + + inline void DebugStats(); + + private: + typename T::Stats stats_{}; + BaseObjectPtr rate_; + BaseObjectPtr size_; + BaseObjectPtr ack_; + AliasedBigUint64Array stats_buffer_; +}; + +// QuicPreferredAddress is a helper class used only when a +// client QuicSession receives an advertised preferred address +// from a server. The helper provides information about the +// preferred address. The Use() function is used to let +// ngtcp2 know to use the preferred address for the given family. +class QuicPreferredAddress { + public: + QuicPreferredAddress( + Environment* env, + ngtcp2_addr* dest, + const ngtcp2_preferred_addr* paddr) : + env_(env), + dest_(dest), + paddr_(paddr) {} + + inline const ngtcp2_cid* cid() const; + inline std::string preferred_ipv6_address() const; + inline std::string preferred_ipv4_address() const; + inline int16_t preferred_ipv6_port() const; + inline int16_t preferred_ipv4_port() const; + inline const uint8_t* stateless_reset_token() const; + + inline bool Use(int family = AF_INET) const; + + private: + inline bool ResolvePreferredAddress( + int local_address_family, + uv_getaddrinfo_t* req) const; + + Environment* env_; + mutable ngtcp2_addr* dest_; + const ngtcp2_preferred_addr* paddr_; +}; + +// QuicError is a helper class used to encapsulate basic +// details about a QUIC protocol error. There are three +// basic types of errors (see QuicErrorFamily) +struct QuicError { + int32_t family; + uint64_t code; + inline QuicError( + int32_t family_ = QUIC_ERROR_SESSION, + int code_ = NGTCP2_NO_ERROR); + inline QuicError( + int32_t family_ = QUIC_ERROR_SESSION, + uint64_t code_ = NGTCP2_NO_ERROR); + explicit inline QuicError(ngtcp2_connection_close_error_code code); + inline QuicError( + Environment* env, + v8::Local codeArg, + v8::Local familyArg = v8::Local(), + int32_t family_ = QUIC_ERROR_SESSION); + inline const char* family_name(); +}; + +// Helper function that returns the maximum QUIC packet size for +// the given socket address. +inline size_t GetMaxPktLen(const SocketAddress& addr); + +// QuicPath is a utility class that wraps ngtcp2_path to adapt +// it to work with SocketAddress +struct QuicPath : public ngtcp2_path { + inline QuicPath(const SocketAddress& local, const SocketAddress& remote); +}; + +struct QuicPathStorage : public ngtcp2_path_storage { + QuicPathStorage() { + ngtcp2_path_storage_zero(this); + } +}; + +// Simple wrapper for ngtcp2_cid that handles hex encoding +class QuicCID : public MemoryRetainer { + public: + // Empty constructor + QuicCID() : ptr_(&cid_) {} + + // Copy constructor + QuicCID(const QuicCID& cid) : QuicCID(cid->data, cid->datalen) {} + + // Copy constructor + explicit QuicCID(const ngtcp2_cid& cid) : QuicCID(cid.data, cid.datalen) {} + + // Wrap constructor + explicit QuicCID(const ngtcp2_cid* cid) : ptr_(cid) {} + + QuicCID(const uint8_t* cid, size_t len) : QuicCID() { + ngtcp2_cid* ptr = this->cid(); + ngtcp2_cid_init(ptr, cid, len); + ptr_ = ptr; + } + + struct Hash { + inline size_t operator()(const QuicCID& cid) const; + }; + + inline bool operator==(const QuicCID& other) const; + inline bool operator!=(const QuicCID& other) const; + + inline std::string ToString() const; + + // Copy assignment + QuicCID& operator=(const QuicCID& cid) { + if (this == &cid) return *this; + this->~QuicCID(); + return *new(this) QuicCID(std::move(cid)); + } + + const ngtcp2_cid& operator*() const { return *ptr_; } + const ngtcp2_cid* operator->() const { return ptr_; } + const ngtcp2_cid* cid() const { return ptr_; } + const uint8_t* data() const { return ptr_->data; } + operator bool() const { return ptr_->datalen > 0; } + size_t length() const { return ptr_->datalen; } + + ngtcp2_cid* cid() { + CHECK_EQ(ptr_, &cid_); + return &cid_; + } + + unsigned char* data() { + return reinterpret_cast(cid()->data); + } + + void set_length(size_t length) { + cid()->datalen = length; + } + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QuicCID) + SET_SELF_SIZE(QuicCID) + + template + using Map = std::unordered_map; + + private: + ngtcp2_cid cid_{}; + const ngtcp2_cid* ptr_; +}; + +// Simple timer wrapper that is used to implement the internals +// for idle and retransmission timeouts. Call Update to start or +// reset the timer; Stop to halt the timer. +class Timer final : public MemoryRetainer { + public: + inline explicit Timer(Environment* env, std::function fn); + + // Stops the timer with the side effect of the timer no longer being usable. + // It will be cleaned up and the Timer object will be destroyed. + inline void Stop(); + + // If the timer is not currently active, interval must be either 0 or greater. + // If the timer is already active, interval is ignored. + inline void Update(uint64_t interval); + + static inline void Free(Timer* timer); + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(Timer) + SET_SELF_SIZE(Timer) + + private: + static inline void OnTimeout(uv_timer_t* timer); + + bool stopped_ = false; + Environment* env_; + std::function fn_; + uv_timer_t timer_; +}; + +using TimerPointer = DeleteFnPtr; + +class StatelessResetToken : public MemoryRetainer { + public: + inline StatelessResetToken( + uint8_t* token, + const uint8_t* secret, + const QuicCID& cid); + inline StatelessResetToken( + const uint8_t* secret, + const QuicCID& cid); + explicit StatelessResetToken( + const uint8_t* token) + : token_(token) {} + + inline std::string ToString() const; + const uint8_t* data() const { return token_; } + + struct Hash { + inline size_t operator()(const StatelessResetToken& token) const; + }; + + inline bool operator==(const StatelessResetToken& other) const; + inline bool operator!=(const StatelessResetToken& other) const; + + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(StatelessResetToken) + SET_SELF_SIZE(StatelessResetToken) + + template + using Map = + std::unordered_map< + StatelessResetToken, + BaseObjectPtr, + StatelessResetToken::Hash>; + + private: + uint8_t buf_[NGTCP2_STATELESS_RESET_TOKENLEN]{}; + const uint8_t* token_; +}; + +template +inline size_t get_length(const T*, size_t len); + +} // namespace quic +} // namespace node + +#endif // NOE_WANT_INTERNALS + +#endif // SRC_QUIC_NODE_QUIC_UTIL_H_ diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 27a9a01c7c2170..3cc44a31772642 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -39,7 +39,8 @@ inline StreamReq* StreamReq::FromObject(v8::Local req_wrap_obj) { inline void StreamReq::Dispose() { object()->SetAlignedPointerInInternalField( StreamReq::kStreamReqField, nullptr); - delete this; + BaseObjectPtr destroy_me{GetAsyncWrap()}; + destroy_me->Detach(); } inline v8::Local StreamReq::object() { diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 277eb6b81bae32..cb03686b4773a9 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -200,6 +200,7 @@ void UDPWrap::Initialize(Local target, Local constants = Object::New(env->isolate()); NODE_DEFINE_CONSTANT(constants, UV_UDP_IPV6ONLY); + NODE_DEFINE_CONSTANT(constants, UV_UDP_REUSEADDR); target->Set(context, env->constants_string(), constants).Check(); diff --git a/src/udp_wrap.h b/src/udp_wrap.h index 6fed1d2dfea810..75a123d8fa793e 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -215,6 +215,11 @@ class UDPWrap final : public HandleWrap, v8::Local current_send_req_wrap_; }; +int sockaddr_for_family(int address_family, + const char* address, + const unsigned short port, + sockaddr_storage* addr); + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/util.h b/src/util.h index 5eaa20b760168b..e33136658c63e0 100644 --- a/src/util.h +++ b/src/util.h @@ -560,6 +560,11 @@ struct MallocedBuffer { size = new_size; } + void Realloc(size_t new_size) { + Truncate(new_size); + data = UncheckedRealloc(data, new_size); + } + inline bool is_empty() const { return data == nullptr; } MallocedBuffer() : data(nullptr), size(0) {} @@ -714,6 +719,11 @@ constexpr size_t arraysize(const T (&)[N]) { return N; } +template +constexpr size_t strsize(const T (&)[N]) { + return N - 1; +} + // Round up a to the next highest multiple of b. template constexpr T RoundUp(T a, T b) { diff --git a/test/cctest/test_quic_buffer.cc b/test/cctest/test_quic_buffer.cc new file mode 100644 index 00000000000000..0e4f697ac3d4f9 --- /dev/null +++ b/test/cctest/test_quic_buffer.cc @@ -0,0 +1,199 @@ +#include "quic/node_quic_buffer-inl.h" +#include "node_bob-inl.h" +#include "util-inl.h" +#include "uv.h" + +#include "gtest/gtest.h" +#include +#include + +using node::quic::QuicBuffer; +using node::quic::QuicBufferChunk; +using node::bob::Status; +using node::bob::Options; +using node::bob::Done; + +TEST(QuicBuffer, Simple) { + char data[100]; + memset(&data, 0, node::arraysize(data)); + uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); + + bool done = false; + + QuicBuffer buffer; + buffer.Push(&buf, 1, [&](int status) { + EXPECT_EQ(0, status); + done = true; + }); + + buffer.Consume(100); + CHECK_EQ(0, buffer.length()); + + // We have to move the read head forward in order to consume + buffer.Seek(1); + buffer.Consume(100); + CHECK_EQ(true, done); + CHECK_EQ(0, buffer.length()); +} + +TEST(QuicBuffer, ConsumeMore) { + char data[100]; + memset(&data, 0, node::arraysize(data)); + uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); + + bool done = false; + + QuicBuffer buffer; + buffer.Push(&buf, 1, [&](int status) { + EXPECT_EQ(0, status); + done = true; + }); + + buffer.Seek(1); + buffer.Consume(150); // Consume more than what was buffered + CHECK_EQ(true, done); + CHECK_EQ(0, buffer.length()); +} + +TEST(QuicBuffer, Multiple) { + uv_buf_t bufs[] { + uv_buf_init(const_cast("abcdefghijklmnopqrstuvwxyz"), 26), + uv_buf_init(const_cast("zyxwvutsrqponmlkjihgfedcba"), 26) + }; + + QuicBuffer buf; + bool done = false; + buf.Push(bufs, 2, [&](int status) { done = true; }); + + buf.Seek(2); + CHECK_EQ(buf.remaining(), 50); + CHECK_EQ(buf.length(), 52); + + buf.Consume(25); + CHECK_EQ(buf.length(), 27); + + buf.Consume(25); + CHECK_EQ(buf.length(), 2); + + buf.Consume(2); + CHECK_EQ(0, buf.length()); +} + +TEST(QuicBuffer, Multiple2) { + char* ptr = new char[100]; + memset(ptr, 0, 50); + memset(ptr + 50, 1, 50); + + uv_buf_t bufs[] = { + uv_buf_init(ptr, 50), + uv_buf_init(ptr + 50, 50) + }; + + int count = 0; + + QuicBuffer buffer; + buffer.Push( + bufs, node::arraysize(bufs), + [&](int status) { + count++; + CHECK_EQ(0, status); + delete[] ptr; + }); + buffer.Seek(node::arraysize(bufs)); + + buffer.Consume(25); + CHECK_EQ(75, buffer.length()); + buffer.Consume(25); + CHECK_EQ(50, buffer.length()); + buffer.Consume(25); + CHECK_EQ(25, buffer.length()); + buffer.Consume(25); + CHECK_EQ(0, buffer.length()); + + // The callback was only called once tho + CHECK_EQ(1, count); +} + +TEST(QuicBuffer, Cancel) { + char* ptr = new char[100]; + memset(ptr, 0, 50); + memset(ptr + 50, 1, 50); + + uv_buf_t bufs[] = { + uv_buf_init(ptr, 50), + uv_buf_init(ptr + 50, 50) + }; + + int count = 0; + + QuicBuffer buffer; + buffer.Push( + bufs, node::arraysize(bufs), + [&](int status) { + count++; + CHECK_EQ(UV_ECANCELED, status); + delete[] ptr; + }); + + buffer.Seek(1); + buffer.Consume(25); + CHECK_EQ(75, buffer.length()); + buffer.Cancel(); + CHECK_EQ(0, buffer.length()); + + // The callback was only called once tho + CHECK_EQ(1, count); +} + +TEST(QuicBuffer, Move) { + QuicBuffer buffer1; + QuicBuffer buffer2; + + char data[100]; + memset(&data, 0, node::arraysize(data)); + uv_buf_t buf = uv_buf_init(data, node::arraysize(data)); + + buffer1.Push(&buf, 1); + + CHECK_EQ(100, buffer1.length()); + + buffer2 = std::move(buffer1); + CHECK_EQ(0, buffer1.length()); + CHECK_EQ(100, buffer2.length()); +} + +TEST(QuicBuffer, QuicBufferChunk) { + std::unique_ptr chunk = + std::make_unique(100); + memset(chunk->out(), 1, 100); + + QuicBuffer buffer; + buffer.Push(std::move(chunk)); + buffer.End(); + CHECK_EQ(100, buffer.length()); + + auto next = [&]( + int status, + const ngtcp2_vec* data, + size_t count, + Done done) { + CHECK_EQ(status, Status::STATUS_END); + CHECK_EQ(count, 1); + CHECK_NOT_NULL(data); + done(100); + }; + + CHECK_EQ(buffer.remaining(), 100); + + ngtcp2_vec data[2]; + size_t len = sizeof(data) / sizeof(ngtcp2_vec); + buffer.Pull(next, Options::OPTIONS_SYNC | Options::OPTIONS_END, data, len); + + CHECK_EQ(buffer.remaining(), 0); + + buffer.Consume(50); + CHECK_EQ(50, buffer.length()); + + buffer.Consume(50); + CHECK_EQ(0, buffer.length()); +} diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc new file mode 100644 index 00000000000000..eb1a9c53319580 --- /dev/null +++ b/test/cctest/test_quic_cid.cc @@ -0,0 +1,31 @@ +#include "quic/node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "util-inl.h" +#include "ngtcp2/ngtcp2.h" +#include "gtest/gtest.h" +#include +#include + +using node::quic::QuicCID; + +TEST(QuicCID, Simple) { + ngtcp2_cid cid1; + ngtcp2_cid cid2; + uint8_t data1[3] = { 'a', 'b', 'c' }; + uint8_t data2[4] = { 1, 2, 3, 4 }; + ngtcp2_cid_init(&cid1, data1, 3); + ngtcp2_cid_init(&cid2, data2, 4); + + QuicCID qcid1(cid1); + CHECK(qcid1); + CHECK_EQ(qcid1.length(), 3); + CHECK_EQ(qcid1.ToString(), "616263"); + + QuicCID qcid2(cid2); + qcid1 = qcid2; + CHECK_EQ(qcid1.ToString(), qcid2.ToString()); + + qcid1.set_length(5); + memset(qcid1.data(), 1, 5); + CHECK_EQ(qcid1.ToString(), "0101010101"); +} diff --git a/test/cctest/test_quic_verifyhostnameidentity.cc b/test/cctest/test_quic_verifyhostnameidentity.cc new file mode 100644 index 00000000000000..f611239ac72e4a --- /dev/null +++ b/test/cctest/test_quic_verifyhostnameidentity.cc @@ -0,0 +1,349 @@ + +#include "base_object-inl.h" +#include "quic/node_quic_crypto.h" +#include "quic/node_quic_util-inl.h" +#include "node_sockaddr-inl.h" +#include "util.h" +#include "gtest/gtest.h" + +#include + +#include +#include +#include + +using node::quic::VerifyHostnameIdentity; + +enum altname_type { + TYPE_DNS, + TYPE_IP, + TYPE_URI +}; + +struct altname { + altname_type type; + const char* name; +}; + +void ToAltNamesMap( + const altname* names, + size_t names_len, + std::unordered_multimap* map) { + for (size_t n = 0; n < names_len; n++) { + switch (names[n].type) { + case TYPE_DNS: + map->emplace("dns", names[n].name); + continue; + case TYPE_IP: + map->emplace("ip", names[n].name); + continue; + case TYPE_URI: + map->emplace("uri", names[n].name); + continue; + } + } +} + +TEST(QuicCrypto, BasicCN_1) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("a.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_2) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("A.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_3_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_4) { + std::unordered_multimap altnames; + CHECK_EQ(VerifyHostnameIdentity("a.com", std::string("a.com."), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_5_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string(".a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_6_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_7_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_8_Fail) { + std::unordered_multimap altnames; + altnames.emplace("uri", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_9) { + std::unordered_multimap altnames; + altnames.emplace("ip", "8.8.8.8"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_10_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "8.8.8.8/24"); + CHECK_EQ( + VerifyHostnameIdentity("8.8.8.8", std::string("8.8.8.8"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_11) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("*.a.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_12_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("ba.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_13_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("\n.a.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_14_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "omg.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("*.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_15_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string("b*b.a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_16_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_17) { + // TODO(@jasnell): This should test multiple CN's. The code is only + // implemented to support one. Need to fix + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity("foo.com", std::string("foo.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_18_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_19_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_20) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.co.uk"); + CHECK_EQ( + VerifyHostnameIdentity("a.co.uk", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_21_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("a.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_22_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_23) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_24) { + std::unordered_multimap altnames; + altnames.emplace("dns", "A.COM"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string("b.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_25_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_26) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_27_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("c.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_28) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_29) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a-cb.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_30_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + + +TEST(QuicCrypto, BasicCN_31) { + std::unordered_multimap altnames; + altnames.emplace("dns", "*b.a.com"); + altnames.emplace("dns", "a.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), 0); +} + + +TEST(QuicCrypto, BasicCN_32) { + std::unordered_multimap altnames; + altnames.emplace("uri", "a.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_33_Fail) { + std::unordered_multimap altnames; + altnames.emplace("uri", "*.b.a.com"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +// // Invalid URI +// { +// host: 'a.b.a.com', cert: { +// subjectaltname: 'URI:http://[a.b.a.com]/', +// subject: {} +// } +// }, + +TEST(QuicCrypto, BasicCN_35_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("a.b.a.com", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_36) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.1", std::string(), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_37_Fail) { + std::unordered_multimap altnames; + altnames.emplace("ip", "127.0.0.1"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.2", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_38_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("127.0.0.1", std::string(), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_39_Fail) { + std::unordered_multimap altnames; + altnames.emplace("dns", "a.com"); + CHECK_EQ( + VerifyHostnameIdentity("localhost", std::string("localhost"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} + +TEST(QuicCrypto, BasicCN_40) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity( + "xn--bcher-kva.example.com", + std::string("*.example.com"), altnames), 0); +} + +TEST(QuicCrypto, BasicCN_41_Fail) { + std::unordered_multimap altnames; + CHECK_EQ( + VerifyHostnameIdentity( + "xn--bcher-kva.example.com", + std::string("xn--*.example.com"), altnames), + X509_V_ERR_HOSTNAME_MISMATCH); +} diff --git a/test/common/README.md b/test/common/README.md index 5479a39d8c215c..ba35fa075be996 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -21,6 +21,7 @@ This directory contains modules used to test the Node.js implementation. * [Report module](#report-module) * [tick module](#tick-module) * [tmpdir module](#tmpdir-module) +* [UDP pair helper](#udp-pair-helper) * [WPT module](#wpt-module) ## Benchmark Module @@ -935,6 +936,19 @@ listener to process `'beforeExit'`. If a file needs to be left open until Node.js completes, use a child process and call `refresh()` only in the parent. +## UDP pair helper + +The `common/udppair` module exports a function `makeUDPPair` and a class +`FakeUDPWrap`. + +`FakeUDPWrap` emits `'send'` events when data is to be sent on it, and provides +an `emitReceived()` API for acting as if data has been received on it. + +`makeUDPPair` returns an object `{ clientSide, serverSide }` where each side +is an `FakeUDPWrap` connected to the other side. + +There is no difference between client or server side beyond their names. + ## WPT Module ### `harness` diff --git a/test/common/fixtures.js b/test/common/fixtures.js index 2390ee8284e421..e33ab1d3e1b3e6 100644 --- a/test/common/fixtures.js +++ b/test/common/fixtures.js @@ -20,9 +20,14 @@ function readFixtureKey(name, enc) { return fs.readFileSync(fixturesPath('keys', name), enc); } +function readFixtureKeys(enc, ...names) { + return names.map((name) => readFixtureKey(name, enc)); +} + module.exports = { fixturesDir, path: fixturesPath, readSync: readFixtureSync, - readKey: readFixtureKey + readKey: readFixtureKey, + readKeys: readFixtureKeys, }; diff --git a/test/common/index.js b/test/common/index.js index 28ce841c48cc3f..f888ad2f449d41 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -50,6 +50,7 @@ const noop = () => {}; const hasCrypto = Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO; +const hasQuic = hasCrypto && Boolean(process.versions.ngtcp2); // Check for flags. Skip this for workers (both, the `cluster` module and // `worker_threads`) and child processes. @@ -674,6 +675,7 @@ const common = { getTTYfd, hasIntl, hasCrypto, + hasQuic, hasMultiLocalhost, invalidArgTypeHelper, isAIX, diff --git a/test/common/quic.js b/test/common/quic.js new file mode 100644 index 00000000000000..6fe121886ad0a2 --- /dev/null +++ b/test/common/quic.js @@ -0,0 +1,38 @@ +/* eslint-disable node-core/require-common-first, node-core/required-modules */ +'use strict'; + +// Common bits for all QUIC-related tests +const { debuglog } = require('util'); +const { readKeys } = require('./fixtures'); +const { createWriteStream } = require('fs'); +const kHttp3Alpn = 'h3-27'; + +const [ key, cert, ca ] = + readKeys( + 'binary', + 'agent1-key.pem', + 'agent1-cert.pem', + 'ca1-cert.pem'); + +const debug = debuglog('test'); + +const kServerPort = process.env.NODE_DEBUG_KEYLOG ? 5678 : 0; +const kClientPort = process.env.NODE_DEBUG_KEYLOG ? 5679 : 0; + +function setupKeylog(session) { + if (process.env.NODE_DEBUG_KEYLOG) { + const kl = createWriteStream(process.env.NODE_DEBUG_KEYLOG); + session.on('keylog', kl.write.bind(kl)); + } +} + +module.exports = { + key, + cert, + ca, + debug, + kServerPort, + kClientPort, + setupKeylog, + kHttp3Alpn, +}; diff --git a/test/common/udppair.js b/test/common/udppair.js new file mode 100644 index 00000000000000..0178d722fde938 --- /dev/null +++ b/test/common/udppair.js @@ -0,0 +1,100 @@ +/* eslint-disable node-core/require-common-first, node-core/required-modules */ +'use strict'; +const { internalBinding } = require('internal/test/binding'); +const { JSUDPWrap } = internalBinding('js_udp_wrap'); +const EventEmitter = require('events'); + +class FakeUDPWrap extends EventEmitter { + constructor() { + super(); + + this._handle = new JSUDPWrap(); + + this._handle.onreadstart = () => this._startReading(); + this._handle.onreadstop = () => this._stopReading(); + this._handle.onwrite = + (wrap, buffers, addr) => this._write(wrap, buffers, addr); + this._handle.getsockname = (obj) => { + Object.assign(obj, { address: '127.0.0.1', family: 'IPv4', port: 1337 }); + return 0; + }; + + this.reading = false; + this.bufferedReceived = []; + this.emitBufferedImmediate = null; + } + + _emitBuffered = () => { + if (!this.reading) return; + if (this.bufferedReceived.length > 0) { + this.emitReceived(this.bufferedReceived.shift()); + this.emitBufferedImmediate = setImmediate(this._emitBuffered); + } else { + this.emit('wantRead'); + } + }; + + _startReading() { + this.reading = true; + this.emitBufferedImmediate = setImmediate(this._emitBuffered); + } + + _stopReading() { + this.reading = false; + clearImmediate(this.emitBufferedImmediate); + } + + _write(wrap, buffers, addr) { + this.emit('send', { buffers, addr }); + setImmediate(() => this._handle.onSendDone(wrap, 0)); + } + + afterBind() { + this._handle.onAfterBind(); + } + + emitReceived(info) { + if (!this.reading) { + this.bufferedReceived.push(info); + return; + } + + const { + buffers, + addr: { + family = 4, + address = '127.0.0.1', + port = 1337, + }, + flags = 0 + } = info; + + let familyInt; + switch (family) { + case 'IPv4': familyInt = 4; break; + case 'IPv6': familyInt = 6; break; + default: throw new Error('bad family'); + } + + for (const buffer of buffers) { + this._handle.emitReceived(buffer, familyInt, address, port, flags); + } + } +} + +function makeUDPPair() { + const serverSide = new FakeUDPWrap(); + const clientSide = new FakeUDPWrap(); + + serverSide.on('send', + (chk) => setImmediate(() => clientSide.emitReceived(chk))); + clientSide.on('send', + (chk) => setImmediate(() => serverSide.emitReceived(chk))); + + return { serverSide, clientSide }; +} + +module.exports = { + FakeUDPWrap, + makeUDPPair +}; diff --git a/test/parallel/test-module-cjs-helpers.js b/test/parallel/test-module-cjs-helpers.js index 12de65598e54e1..cadb5f99362cda 100644 --- a/test/parallel/test-module-cjs-helpers.js +++ b/test/parallel/test-module-cjs-helpers.js @@ -5,5 +5,5 @@ require('../common'); const assert = require('assert'); const { builtinLibs } = require('internal/modules/cjs/helpers'); -const expectedLibs = process.features.inspector ? 34 : 33; +const expectedLibs = process.features.inspector ? 35 : 34; assert.strictEqual(builtinLibs.length, expectedLibs); diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 14484293dc4621..f0aac8deda15c3 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -2,8 +2,18 @@ const common = require('../common'); const assert = require('assert'); -const expected_keys = ['ares', 'brotli', 'modules', 'node', - 'uv', 'v8', 'zlib', 'nghttp2', 'napi', 'llhttp']; +const expected_keys = [ + 'ares', + 'brotli', + 'modules', + 'node', + 'uv', + 'v8', + 'zlib', + 'nghttp2', + 'napi', + 'llhttp' +]; if (common.hasCrypto) { expected_keys.push('openssl'); @@ -16,6 +26,11 @@ if (common.hasIntl) { expected_keys.push('unicode'); } +if (common.hasQuic) { + expected_keys.push('ngtcp2'); + expected_keys.push('nghttp3'); +} + expected_keys.sort(); const actual_keys = Object.keys(process.versions).sort(); diff --git a/test/parallel/test-quic-binding.js b/test/parallel/test-quic-binding.js new file mode 100644 index 00000000000000..3d5a5b581fbc24 --- /dev/null +++ b/test/parallel/test-quic-binding.js @@ -0,0 +1,39 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests the availability and correctness of internalBinding(quic) + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { internalBinding } = require('internal/test/binding'); + +const quic = internalBinding('quic'); +assert(quic); + +assert(quic.constants); + +// Version numbers used to identify IETF drafts are created by +// adding the draft number to 0xff0000, in this case 19 (25). +assert.strictEqual(quic.constants.NGTCP2_PROTO_VER.toString(16), 'ff00001b'); +assert.strictEqual(quic.constants.NGTCP2_ALPN_H3, '\u0005h3-27'); + +// The following just tests for the presence of things we absolutely need. +// They don't test the functionality of those things. + +assert(quic.sessionConfig instanceof Float64Array); +assert(quic.http3Config instanceof Float64Array); + +assert(quic.QuicSocket); +assert(quic.QuicEndpoint); +assert(quic.QuicStream); + +assert.strictEqual(typeof quic.createClientSession, 'function'); +assert.strictEqual(typeof quic.openBidirectionalStream, 'function'); +assert.strictEqual(typeof quic.openUnidirectionalStream, 'function'); +assert.strictEqual(typeof quic.setCallbacks, 'function'); +assert.strictEqual(typeof quic.initSecureContext, 'function'); +assert.strictEqual(typeof quic.initSecureContextClient, 'function'); +assert.strictEqual(typeof quic.silentCloseSession, 'function'); diff --git a/test/parallel/test-quic-client-connect-multiple-parallel.js b/test/parallel/test-quic-client-connect-multiple-parallel.js new file mode 100644 index 00000000000000..43282a13f31bb0 --- /dev/null +++ b/test/parallel/test-quic-client-connect-multiple-parallel.js @@ -0,0 +1,57 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that .connect() can be called multiple times with different servers. + +const assert = require('assert'); +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +(async function() { + const servers = []; + for (let i = 0; i < 3; i++) { + const server = quic.createSocket(); + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); + })); + + server.on('close', common.mustCall()); + + servers.push(server); + } + + await Promise.all(servers.map((server) => once(server, 'ready'))); + + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + let done = 0; + for (const server of servers) { + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustCall((stream) => { + stream.on('data', common.mustCall( + (chk) => assert.strictEqual(chk.toString(), 'Hi!'))); + stream.on('end', common.mustCall(() => { + server.close(); + req.close(); + if (++done === servers.length) client.close(); + })); + })); + + req.on('close', common.mustCall()); + } +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-connect-multiple-sequential.js b/test/parallel/test-quic-client-connect-multiple-sequential.js new file mode 100644 index 00000000000000..51c8289f3fe363 --- /dev/null +++ b/test/parallel/test-quic-client-connect-multiple-sequential.js @@ -0,0 +1,56 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that .connect() can be called multiple times with different servers. + +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const { once } = require('events'); + +(async function() { + const servers = []; + for (let i = 0; i < 3; i++) { + const server = quic.createSocket(); + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); + })); + + server.on('close', common.mustCall()); + + servers.push(server); + } + + await Promise.all(servers.map((server) => once(server, 'ready'))); + + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + for (const server of servers) { + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + const [ stream ] = await once(req, 'stream'); + stream.resume(); + await once(stream, 'end'); + + server.close(); + req.close(); + await once(req, 'close'); + } + + client.close(); + + await once(client, 'close'); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-empty-preferred-address.js b/test/parallel/test-quic-client-empty-preferred-address.js new file mode 100644 index 00000000000000..6518c8a2795f9e --- /dev/null +++ b/test/parallel/test-quic-client-empty-preferred-address.js @@ -0,0 +1,57 @@ +// Flags: --no-warnings +'use strict'; + +// This test ensures that when we don't define `preferredAddress` +// on the server while the `preferredAddressPolicy` on the client +// is `accpet`, it works as expected. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { key, cert, ca } = require('../common/quic'); +const { createSocket } = require('quic'); +const { once } = require('events'); + +(async () => { + const server = createSocket(); + + let client; + const options = { key, cert, ca, alpn: 'zzz' }; + server.listen(options); + + server.on('session', common.mustCall((serverSession) => { + serverSession.on('stream', common.mustCall(async (stream) => { + stream.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString('utf8'), 'hello'); + })); + + await once(stream, 'end'); + + stream.close(); + client.close(); + server.close(); + })); + })); + + await once(server, 'ready'); + + client = createSocket({ client: options }); + + const clientSession = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + preferredAddressPolicy: 'accept', + }); + + await once(clientSession, 'secure'); + + const stream = clientSession.openStream(); + stream.end('hello'); + + await Promise.all([ + once(stream, 'close'), + once(client, 'close'), + once(server, 'close')]); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-client-server.js b/test/parallel/test-quic-client-server.js new file mode 100644 index 00000000000000..0615894ffeda0c --- /dev/null +++ b/test/parallel/test-quic-client-server.js @@ -0,0 +1,372 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests a simple QUIC client/server round-trip + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { internalBinding } = require('internal/test/binding'); +const { + constants: { + NGTCP2_NO_ERROR, + QUIC_ERROR_APPLICATION, + } +} = internalBinding('quic'); + +const { Buffer } = require('buffer'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const fs = require('fs'); +const { + key, + cert, + ca, + debug, +} = require('../common/quic'); + +const filedata = fs.readFileSync(__filename, { encoding: 'utf8' }); + +const { createSocket } = require('quic'); + +const kStatelessResetToken = + Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); + +let client; + +const server = createSocket({ + validateAddress: true, + statelessResetSecret: kStatelessResetToken +}); + +const unidata = ['I wonder if it worked.', 'test']; +const kServerName = 'agent2'; // Intentionally the wrong servername +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +const countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ + key, + cert, + ca, + requestCert: true, + rejectUnauthorized: false, + alpn: kALPN, +}); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + assert.strictEqual(session.maxStreams.bidi, 100); + assert.strictEqual(session.maxStreams.uni, 3); + + { + const { + address, + family, + port + } = session.remoteAddress; + const endpoint = client.endpoints[0].address; + assert.strictEqual(port, endpoint.port); + assert.strictEqual(family, endpoint.family); + debug(`QuicServerSession Client ${family} address ${address}:${port}`); + } + + session.on('usePreferredAddress', common.mustNotCall()); + + session.on('clientHello', common.mustCall( + (alpn, servername, ciphers, cb) => { + assert.strictEqual(alpn, kALPN); + assert.strictEqual(servername, kServerName); + assert.strictEqual(ciphers.length, 4); + cb(); + })); + + session.on('OCSPRequest', common.mustCall( + (servername, context, cb) => { + debug('QuicServerSession received a OCSP request'); + assert.strictEqual(servername, kServerName); + + // This will be a SecureContext. By default it will + // be the SecureContext used to create the QuicSession. + // If the user wishes to do something with it, it can, + // but if it wishes to pass in a new SecureContext, + // it can pass it in as the second argument to the + // callback below. + assert(context); + debug('QuicServerSession Certificate: ', context.getCertificate()); + debug('QuicServerSession Issuer: ', context.getIssuer()); + + // The callback can be invoked asynchronously + setImmediate(() => { + // The first argument is a potential error, + // in which case the session will be destroyed + // immediately. + // The second is an optional new SecureContext + // The third is the ocsp response. + // All arguments are optional + cb(null, null, Buffer.from('hello')); + }); + })); + + session.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicServerSession TLS Handshake Complete'); + debug(' Server name: %s', servername); + debug(' ALPN: %s', alpn); + debug(' Cipher: %s, %s', cipher.name, cipher.version); + assert.strictEqual(session.servername, servername); + assert.strictEqual(servername, kServerName); + assert.strictEqual(session.alpnProtocol, alpn); + + assert.strictEqual(session.getPeerCertificate().subject.CN, 'agent1'); + + assert(session.authenticated); + assert.strictEqual(session.authenticationError, undefined); + + const uni = session.openStream({ halfOpen: true }); + assert(uni.unidirectional); + assert(!uni.bidirectional); + assert(uni.serverInitiated); + assert(!uni.clientInitiated); + assert(!uni.pending); + uni.write(unidata[0], common.mustCall()); + uni.end(unidata[1], common.mustCall()); + uni.on('finish', common.mustCall()); + uni.on('end', common.mustCall()); + uni.on('data', common.mustNotCall()); + uni.on('close', common.mustCall(() => { + assert.strictEqual(uni.finalSize, 0n); + })); + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + })); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + assert.strictEqual(stream.id, 0); + assert.strictEqual(stream.session, session); + assert(stream.bidirectional); + assert(!stream.unidirectional); + assert(stream.clientInitiated); + assert(!stream.serverInitiated); + assert(!stream.pending); + + const file = fs.createReadStream(__filename); + let data = ''; + file.pipe(stream); + stream.setEncoding('utf8'); + stream.on('blocked', common.mustNotCall()); + stream.on('data', (chunk) => { + data += chunk; + + debug('Server: min data rate: %f', stream.dataRateHistogram.min); + debug('Server: max data rate: %f', stream.dataRateHistogram.max); + debug('Server: data rate 50%: %f', + stream.dataRateHistogram.percentile(50)); + debug('Server: data rate 99%: %f', + stream.dataRateHistogram.percentile(99)); + + debug('Server: min data size: %f', stream.dataSizeHistogram.min); + debug('Server: max data size: %f', stream.dataSizeHistogram.max); + debug('Server: data size 50%: %f', + stream.dataSizeHistogram.percentile(50)); + debug('Server: data size 99%: %f', + stream.dataSizeHistogram.percentile(99)); + }); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, filedata); + debug('Server received expected data for stream %d', stream.id); + })); + stream.on('finish', common.mustCall()); + stream.on('close', common.mustCall(() => { + assert.strictEqual(typeof stream.duration, 'bigint'); + assert.strictEqual(typeof stream.bytesReceived, 'bigint'); + assert.strictEqual(typeof stream.bytesSent, 'bigint'); + assert.strictEqual(typeof stream.maxExtendedOffset, 'bigint'); + assert.strictEqual(stream.finalSize, BigInt(filedata.length)); + })); + })); + + session.on('close', common.mustCall(() => { + const { + code, + family + } = session.closeCode; + debug(`Server session closed with code ${code} (family: ${family})`); + assert.strictEqual(code, NGTCP2_NO_ERROR); + + const err = { + code: 'ERR_QUICSESSION_DESTROYED', + name: 'Error' + }; + assert.throws(() => session.ping(), { ...err }); + assert.throws(() => session.openStream(), { + ...err, + message: 'Cannot call openStream after a QuicSession has been destroyed' + }); + assert.throws(() => session.updateKey(), { + ...err, + message: 'Cannot call updateKey after a QuicSession has been destroyed' + }); + })); +})); + +server.on('ready', common.mustCall(() => { + const endpoints = server.endpoints; + for (const endpoint of endpoints) { + const address = endpoint.address; + debug('Server is listening on address %s:%d', + address.address, + address.port); + } + const endpoint = endpoints[0]; + + client = createSocket({ client: { key, cert, ca, alpn: kALPN } + }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + debug(' Bound duration', + client.boundDuration); + debug(' Bytes Sent/Received: %d/%d', + client.bytesSent, + client.bytesReceived); + debug(' Packets Sent/Received: %d/%d', + client.packetsSent, + client.packetsReceived); + debug(' Sessions:', client.clientSessions); + })); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: kServerName, + requestOCSP: true, + }); + + assert.strictEqual(req.servername, kServerName); + + req.on('usePreferredAddress', common.mustNotCall()); + + req.on('OCSPResponse', common.mustCall((response) => { + debug(`QuicClientSession OCSP response: "${response.toString()}"`); + assert.strictEqual(response.toString(), 'hello'); + })); + + req.on('sessionTicket', common.mustCall((ticket, params) => { + debug('Session ticket received'); + assert(ticket instanceof Buffer); + assert(params instanceof Buffer); + debug(' Ticket: %s', ticket.toString('hex')); + debug(' Params: %s', params.toString('hex')); + }, 2)); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + debug(' Server name: %s', servername); + debug(' ALPN: %s', alpn); + debug(' Cipher: %s, %s', cipher.name, cipher.version); + assert.strictEqual(servername, kServerName); + assert.strictEqual(req.servername, kServerName); + assert.strictEqual(alpn, kALPN); + assert.strictEqual(req.alpnProtocol, kALPN); + assert(req.ephemeralKeyInfo); + assert.strictEqual(req.getPeerCertificate().subject.CN, 'agent1'); + + debug('Client, min handshake ack: %f', + req.handshakeAckHistogram.min); + debug('Client, max handshake ack: %f', + req.handshakeAckHistogram.max); + debug('Client, min handshake rate: %f', + req.handshakeContinuationHistogram.min); + debug('Client, max handshake rate: %f', + req.handshakeContinuationHistogram.max); + + // The server's identity won't be valid because the requested + // SNI hostname does not match the certificate used. + debug('QuicClientSession server is %sauthenticated', + req.authenticated ? '' : 'not '); + assert(!req.authenticated); + assert.throws(() => { throw req.authenticationError; }, { + code: 'ERR_QUIC_VERIFY_HOSTNAME_MISMATCH', + message: 'Hostname mismatch' + }); + + { + const { + address, + family, + port + } = req.remoteAddress; + const endpoint = server.endpoints[0].address; + assert.strictEqual(port, endpoint.port); + assert.strictEqual(family, endpoint.family); + debug(`QuicClientSession Server ${family} address ${address}:${port}`); + } + + const file = fs.createReadStream(__filename); + const stream = req.openStream(); + file.pipe(stream); + let data = ''; + stream.resume(); + stream.setEncoding('utf8'); + stream.on('blocked', common.mustNotCall()); + stream.on('data', (chunk) => data += chunk); + stream.on('finish', common.mustCall()); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, filedata); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + assert.strictEqual(stream.finalSize, BigInt(filedata.length)); + countdown.dec(); + })); + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, unidata.join('')); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed', stream.id); + assert.strictEqual(stream.finalSize, 26n); + countdown.dec(); + })); + })); + + req.on('close', common.mustCall(() => { + const { + code, + family + } = req.closeCode; + debug(`Client session closed with code ${code} (family: ${family})`); + assert.strictEqual(code, NGTCP2_NO_ERROR); + assert.strictEqual(family, QUIC_ERROR_APPLICATION); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', () => { + debug('Server closing. Duration', server.duration); + debug(' Bound duration:', + server.boundDuration); + debug(' Listen duration:', + server.listenDuration); + debug(' Bytes Sent/Received: %d/%d', + server.bytesSent, + server.bytesReceived); + debug(' Packets Sent/Received: %d/%d', + server.packetsSent, + server.packetsReceived); + debug(' Sessions:', server.serverSessions); +}); diff --git a/test/parallel/test-quic-errors-quicsession-openstream.js b/test/parallel/test-quic-errors-quicsession-openstream.js new file mode 100644 index 00000000000000..02325e6cca6151 --- /dev/null +++ b/test/parallel/test-quic-errors-quicsession-openstream.js @@ -0,0 +1,84 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test errors thrown when openStream is called incorrectly +// or is not permitted + +const { createHook } = require('async_hooks'); +const assert = require('assert'); +const quic = require('quic'); + +// Ensure that no QUICSTREAM instances are created during the test +createHook({ + init(id, type) { + assert.notStrictEqual(type, 'QUICSTREAM'); + } +}).enable(); + +const Countdown = require('../common/countdown'); +const { key, cert, ca } = require('../common/quic'); + +const options = { key, cert, ca, alpn: 'zzz', maxStreamsUni: 0 }; +const server = quic.createSocket({ server: options }); +const client = quic.createSocket({ client: options }); + +const countdown = new Countdown(1, () => { + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + session.on('stream', common.mustNotCall()); +})); + +server.on('ready', common.mustCall(() => { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + ['z', 1, {}, [], null, Infinity, 1n].forEach((i) => { + assert.throws( + () => req.openStream({ halfOpen: i }), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + ['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => req.openStream({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + [-1, Number.MAX_SAFE_INTEGER + 1].forEach((highWaterMark) => { + assert.throws(() => req.openStream({ highWaterMark }), { + code: 'ERR_OUT_OF_RANGE' + }); + }); + + ['a', 1n, [], {}, false].forEach((highWaterMark) => { + assert.throws(() => req.openStream({ highWaterMark }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + req.on('ready', common.mustCall()); + req.on('secure', common.mustCall()); + + // Unidirectional streams are not allowed. openStream will succeeed + // but the stream will be destroyed immediately. The underlying + // QuicStream C++ handle will not be created. + req.openStream({ + halfOpen: true, + highWaterMark: 10, + defaultEncoding: 'utf16le' + }).on('error', common.expectsError({ + code: 'ERR_QUICSTREAM_OPEN_FAILED' + })).on('error', common.mustCall(() => countdown.dec())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-errors-quicsocket-connect.js b/test/parallel/test-quic-errors-quicsocket-connect.js new file mode 100644 index 00000000000000..2ba330a60f819a --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-connect.js @@ -0,0 +1,243 @@ +// Flags: --no-warnings +'use strict'; + +// Tests error and input validation checks for QuicSocket.connect() + +const common = require('../common'); + +if (!common.hasQuic) + common.skip('missing quic'); + +const { createHook } = require('async_hooks'); +const assert = require('assert'); +const { createSocket } = require('quic'); + +// Ensure that a QuicClientSession handle is never created during the +// error condition tests (ensures that argument and error validation) +// is occurring before the underlying handle is created. +createHook({ + init(id, type) { + assert.notStrictEqual(type, 'QUICCLIENTSESSION'); + } +}).enable(); + +const client = createSocket(); + +// Test invalid minDHSize options argument +['test', 1n, {}, [], false].forEach((minDHSize) => { + assert.throws(() => client.connect({ minDHSize }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid port argument option +[-1, 'test', 1n, {}, [], NaN, false, 65536].forEach((port) => { + assert.throws(() => client.connect({ port }), { + code: 'ERR_SOCKET_BAD_PORT' + }); +}); + +// Test invalid address argument option +[-1, 10, 1n, {}, [], true].forEach((address) => { + assert.throws(() => client.connect({ address }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test servername can't be IP address argument option +[ + '0.0.0.0', + '8.8.8.8', + '127.0.0.1', + '192.168.0.1', + '::', + '1::', + '::1', + '1::8', + '1::7:8', + '1:2:3:4:5:6:7:8', + '1:2:3:4:5:6::8', + '2001:0000:1234:0000:0000:C1C0:ABCD:0876', + '3ffe:0b00:0000:0000:0001:0000:0000:000a', + 'a:0:0:0:0:0:0:0', + 'fe80::7:8%eth0', + 'fe80::7:8%1' +].forEach((servername) => { + assert.throws(() => client.connect({ servername }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + +[-1, 10, 1n, {}, [], true].forEach((servername) => { + assert.throws(() => client.connect({ servername }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid remoteTransportParams argument option +[-1, 'test', 1n, {}, []].forEach((remoteTransportParams) => { + assert.throws(() => client.connect({ remoteTransportParams }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid sessionTicket argument option +[-1, 'test', 1n, {}, []].forEach((sessionTicket) => { + assert.throws(() => client.connect({ sessionTicket }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid alpn argument option +[-1, 10, 1n, {}, [], true].forEach((alpn) => { + assert.throws(() => client.connect({ alpn }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[ + 'idleTimeout', + 'activeConnectionIdLimit', + 'maxAckDelay', + 'maxData', + 'maxPacketSize', + 'maxStreamDataBidiLocal', + 'maxStreamDataBidiRemote', + 'maxStreamDataUni', + 'maxStreamsBidi', + 'maxStreamsUni', + 'highWaterMark', +].forEach((prop) => { + assert.throws(() => client.connect({ [prop]: -1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => client.connect({ [prop]: Number.MAX_SAFE_INTEGER + 1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => client.connect({ [prop]: val }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); +}); + +// activeConnectionIdLimit must be between 2 and 8, inclusive +[1, 9].forEach((activeConnectionIdLimit) => { + assert.throws(() => client.connect({ activeConnectionIdLimit }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +['a', 1n, 1, [], {}].forEach((ipv6Only) => { + assert.throws(() => client.connect({ ipv6Only }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, [], {}].forEach((preferredAddressPolicy) => { + assert.throws(() => client.connect({ preferredAddressPolicy }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, 'test', [], {}].forEach((qlog) => { + assert.throws(() => client.connect({ qlog }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, 'test', [], {}].forEach((requestOCSP) => { + assert.throws(() => client.connect({ requestOCSP }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, [], {}, 'aaa'].forEach((type) => { + assert.throws(() => client.connect({ type }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + + +[ + 'qpackMaxTableCapacity', + 'qpackBlockedStreams', + 'maxHeaderListSize', + 'maxPushes', +].forEach((prop) => { + assert.throws(() => client.connect({ h3: { [prop]: -1 } }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => client.connect({ h3: { [prop]: Number.MAX_SAFE_INTEGER + 1 } }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => client.connect({ h3: { [prop]: val } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); +}); + +['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => client.connect({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); +}); + + +// Test that connect cannot be called after QuicSocket is closed. +client.close(); +assert.throws(() => client.connect(), { + code: 'ERR_QUICSOCKET_DESTROYED' +}); + +// TODO(@jasnell): Test additional options: +// +// Client QuicSession Related: +// +// [x] idleTimeout - must be a number greater than zero +// [x] ipv6Only - must be a boolean +// [x] activeConnectionIdLimit - must be a number between 2 and 8 +// [x] maxAckDelay - must be a number greater than zero +// [x] maxData - must be a number greater than zero +// [x] maxPacketSize - must be a number greater than zero +// [x] maxStreamDataBidiLocal - must be a number greater than zero +// [x] maxStreamDataBidiRemote - must be a number greater than zero +// [x] maxStreamDataUni - must be a number greater than zero +// [x] maxStreamsBidi - must be a number greater than zero +// [x] maxStreamsUni - must be a number greater than zero +// [x] preferredAddressPolicy - must be eiher 'accept' or 'reject' +// [x] qlog - must be a boolean +// [x] requestOCSP - must be a boolean +// [x] type - must be a string, either 'udp4' or 'udp6' +// +// HTTP/3 Related: +// +// [x] h3.qpackMaxTableCapacity - must be a number greater than zero +// [x] h3.qpackBlockedStreams - must be a number greater than zero +// [x] h3.maxHeaderListSize - must be a number greater than zero +// [x] h3.maxPushes - must be a number greater than zero +// +// Secure Context Related: +// +// [ ] ca (certificate authority) - must be a string, string array, +// Buffer, or Buffer array. +// [ ] cert (cert chain) - must be a string, string array, Buffer, or +// Buffer array. +// [ ] ciphers - must be a string +// [ ] clientCertEngine - must be a string +// [ ] crl - must be a string, string array, Buffer, or Buffer array +// [ ] dhparam - must be a string or Buffer +// [ ] ecdhCurve - must be a string +// [ ] honorCipherOrder - must be a boolean +// [ ] key - must be a string, string array, Buffer, or Buffer array +// [ ] passphrase - must be a string +// [ ] pfx - must be a string, string array, Buffer, or Buffer array +// [ ] secureOptions - must be a number +// [x] minDHSize - must be a number diff --git a/test/parallel/test-quic-errors-quicsocket-create.js b/test/parallel/test-quic-errors-quicsocket-create.js new file mode 100644 index 00000000000000..447f7c8ea692ef --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-create.js @@ -0,0 +1,187 @@ +// Flags: --no-warnings +'use strict'; + +// Test QuicSocket constructor option errors + +const common = require('../common'); +const async_hooks = require('async_hooks'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); + +// Hook verifies that no QuicSocket handles are actually created. +async_hooks.createHook({ + init: common.mustCallAtLeast((_, type) => { + assert.notStrictEqual(type, 'QUICSOCKET'); + }) +}).enable(); + +const { createSocket } = require('quic'); + +// Test invalid QuicSocket options argument +[1, 'test', false, 1n, null].forEach((i) => { + assert.throws(() => createSocket(i), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket port argument option +[-1, 'test', 1n, {}, [], NaN, false].forEach((port) => { + assert.throws(() => createSocket({ endpoint: { port } }), { + code: 'ERR_SOCKET_BAD_PORT' + }); +}); + +// Test invalid QuicSocket addressargument option +[-1, 10, 1n, {}, [], NaN, false].forEach((address) => { + assert.throws(() => createSocket({ endpoint: { address } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket type argument option +[1, false, 1n, {}, null, NaN].forEach((type) => { + assert.throws(() => createSocket({ endpoint: { type } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket ipv6Only argument option +[1, NaN, 1n, null, {}, []].forEach((ipv6Only) => { + assert.throws(() => createSocket({ endpoint: { ipv6Only } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket reuseAddr argument option +[1, NaN, 1n, null, {}, []].forEach((reuseAddr) => { + assert.throws(() => createSocket({ endpoint: { reuseAddr } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket lookup argument option +[1, 1n, {}, [], 'test', true].forEach((lookup) => { + assert.throws(() => createSocket({ lookup }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket validateAddress argument option +[1, NaN, 1n, null, {}, []].forEach((validateAddress) => { + assert.throws(() => createSocket({ validateAddress }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket validateAddressLRU argument option +[1, NaN, 1n, null, {}, []].forEach((validateAddressLRU) => { + assert.throws(() => createSocket({ validateAddressLRU }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket autoClose argument option +[1, NaN, 1n, null, {}, []].forEach((autoClose) => { + assert.throws(() => createSocket({ autoClose }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket qlog argument option +[1, NaN, 1n, null, {}, []].forEach((qlog) => { + assert.throws(() => createSocket({ qlog }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + + +// Test invalid QuicSocket retryTokenTimeout option +[0, 61, NaN].forEach((retryTokenTimeout) => { + assert.throws(() => createSocket({ retryTokenTimeout }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket retryTokenTimeout option +['test', null, 1n, {}, [], false].forEach((retryTokenTimeout) => { + assert.throws(() => createSocket({ retryTokenTimeout }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnectionsPerHost) => { + assert.throws(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxConnectionsPerHost) => { + assert.throws(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxConnections option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxConnections) => { + assert.throws(() => createSocket({ maxConnections }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxConnectionsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxConnections) => { + assert.throws(() => createSocket({ maxConnections }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +// Test invalid QuicSocket maxStatelessResetsPerHost option +[0, Number.MAX_SAFE_INTEGER + 1, NaN].forEach((maxStatelessResetsPerHost) => { + assert.throws(() => createSocket({ maxStatelessResetsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +// Test invalid QuicSocket maxStatelessResetsPerHost option +[ + 'test', + null, + 1n, + {}, + [], + false +].forEach((maxStatelessResetsPerHost) => { + assert.throws(() => createSocket({ maxStatelessResetsPerHost }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[1, 1n, false, 'test'].forEach((options) => { + assert.throws(() => createSocket({ endpoint: options }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + assert.throws(() => createSocket({ client: options }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + assert.throws(() => createSocket({ server: options }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); diff --git a/test/parallel/test-quic-errors-quicsocket-listen.js b/test/parallel/test-quic-errors-quicsocket-listen.js new file mode 100644 index 00000000000000..330d49e7b1341c --- /dev/null +++ b/test/parallel/test-quic-errors-quicsocket-listen.js @@ -0,0 +1,179 @@ +// Flags: --no-warnings +'use strict'; + +// Tests error and input validation checks for QuicSocket.connect() + +const common = require('../common'); + +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); + +// Test invalid callback function +{ + const server = createSocket(); + [1, 1n].forEach((cb) => { + assert.throws(() => server.listen({}, cb), { + code: 'ERR_INVALID_CALLBACK' + }); + }); +} + +// Test QuicSocket is already listening +{ + const server = createSocket(); + server.listen(); + assert.throws(() => server.listen(), { + code: 'ERR_QUICSOCKET_LISTENING' + }); + server.close(); +} + +// Test QuicSocket listen after destroy error +{ + const server = createSocket(); + server.close(); + assert.throws(() => server.listen(), { + code: 'ERR_QUICSOCKET_DESTROYED' + }); +} + +{ + // Test incorrect ALPN + const server = createSocket(); + [1, 1n, true, {}, [], null].forEach((alpn) => { + assert.throws(() => server.listen({ alpn }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + // Test invalid idle timeout + [ + 'idleTimeout', + 'activeConnectionIdLimit', + 'maxAckDelay', + 'maxData', + 'maxPacketSize', + 'maxStreamDataBidiLocal', + 'maxStreamDataBidiRemote', + 'maxStreamDataUni', + 'maxStreamsBidi', + 'maxStreamsUni', + 'highWaterMark', + ].forEach((prop) => { + assert.throws(() => server.listen({ [prop]: -1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + assert.throws( + () => server.listen({ [prop]: Number.MAX_SAFE_INTEGER + 1 }), { + code: 'ERR_OUT_OF_RANGE' + }); + + ['a', 1n, [], {}, false].forEach((val) => { + assert.throws(() => server.listen({ [prop]: val }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + }); + + [1, 1n, 'test', {}, []].forEach((rejectUnauthorized) => { + assert.throws(() => server.listen({ rejectUnauthorized }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', {}, []].forEach((requestCert) => { + assert.throws(() => server.listen({ requestCert }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', {}, []].forEach((requestCert) => { + assert.throws(() => server.listen({ requestCert }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, 'test', false].forEach((preferredAddress) => { + assert.throws(() => server.listen({ preferredAddress }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + + [1, 1n, null, false, {}, []].forEach((address) => { + assert.throws(() => server.listen({ preferredAddress: { address } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [-1].forEach((port) => { + assert.throws(() => server.listen({ preferredAddress: { port } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 'test', false, null, {}, []].forEach((type) => { + assert.throws(() => server.listen({ preferredAddress: { type } }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + }); + + [1, 1n, false, [], {}, null].forEach((ciphers) => { + assert.throws(() => server.listen({ ciphers }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + [1, 1n, false, [], {}, null].forEach((groups) => { + assert.throws(() => server.listen({ groups }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + + ['', 1n, {}, [], false, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => server.listen({ defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + // Make sure that after all of the validation checks, the socket + // is not actually marked as listening at all. + assert.strictEqual(typeof server.listening, 'boolean'); + assert(!server.listening); +} + + +// Options to check +// * [x] alpn +// * [x] idleTimeout +// * [x] activeConnectionIdLimit +// * [x] maxAckDelay +// * [x] maxData +// * [x] maxPacketSize +// * [x] maxStreamsBidi +// * [x] maxStreamsUni +// * [x] maxStreamDataBidiLocal +// * [x] maxStreamDataBidiRemote +// * [x] maxStreamDataUni +// * [x] preferredAddress +// * [x] requestCert +// * [x] rejectUnauthorized + +// SecureContext Options +// * [ ] ca +// * [ ] cert +// * [x] ciphers +// * [ ] clientCertEngine +// * [ ] crl +// * [ ] dhparam +// * [ ] groups +// * [ ] ecdhCurve +// * [ ] honorCipherOrder +// * [ ] key +// * [ ] passphrase +// * [ ] pfx +// * [ ] secureOptions +// * [ ] sessionIdContext diff --git a/test/parallel/test-quic-http3-client-server.js b/test/parallel/test-quic-http3-client-server.js new file mode 100644 index 00000000000000..8c7638b55747db --- /dev/null +++ b/test/parallel/test-quic-http3-client-server.js @@ -0,0 +1,157 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests a simple QUIC HTTP/3 client/server round-trip + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const fs = require('fs'); + +const { + key, + cert, + ca, + debug, + kHttp3Alpn, + kServerPort, + kClientPort, + setupKeylog, +} = require('../common/quic'); + +const filedata = fs.readFileSync(__filename, { encoding: 'utf8' }); + +const { createSocket } = require('quic'); + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +const kServerName = 'agent2'; // Intentionally the wrong servername + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ + key, + cert, + ca, + alpn: kHttp3Alpn, +}); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + assert.strictEqual(session.maxStreams.bidi, 100); + assert.strictEqual(session.maxStreams.uni, 3); + + setupKeylog(session); + + session.on('secure', common.mustCall((_, alpn) => { + debug('QuicServerSession handshake completed'); + assert.strictEqual(session.alpnProtocol, alpn); + })); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + const file = fs.createReadStream(__filename); + let data = ''; + + assert(stream.submitInitialHeaders({ ':status': '200' })); + + file.pipe(stream); + stream.setEncoding('utf8'); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':path', '/' ], + [ ':authority', 'localhost' ], + [ ':scheme', 'https' ], + [ ':method', 'POST' ] + ]; + assert.deepStrictEqual(expected, headers); + debug('Received expected request headers'); + })); + stream.on('informationalHeaders', common.mustNotCall()); + stream.on('trailingHeaders', common.mustNotCall()); + + stream.on('data', (chunk) => { + data += chunk; + }); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, filedata); + debug('Server received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kHttp3Alpn } + }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + h3: { maxPushes: 10 } + }); + debug('QuicClientSession Created'); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession handshake completed'); + + const file = fs.createReadStream(__filename); + const stream = req.openStream(); + + assert(stream.submitInitialHeaders({ + ':method': 'POST', + ':scheme': 'https', + ':authority': 'localhost', + ':path': '/', + })); + file.pipe(stream); + let data = ''; + stream.resume(); + stream.setEncoding('utf8'); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':status', '200' ] + ]; + assert.deepStrictEqual(expected, headers); + debug('Received expected response headers'); + })); + stream.on('informationalHeaders', common.mustNotCall()); + stream.on('trailingHeaders', common.mustNotCall()); + + stream.on('data', (chunk) => data += chunk); + stream.on('finish', common.mustCall()); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, filedata); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-http3-push.js b/test/parallel/test-quic-http3-push.js new file mode 100644 index 00000000000000..d91e98a7ee6ebd --- /dev/null +++ b/test/parallel/test-quic-http3-push.js @@ -0,0 +1,157 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests a simple QUIC HTTP/3 client/server round-trip + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { key, cert, ca, kHttp3Alpn } = require('../common/quic'); + +const { createSocket } = require('quic'); + +let client; +const server = createSocket(); + +const countdown = new Countdown(2, () => { + server.close(); + client.close(); +}); + +const options = { key, cert, ca, alpn: kHttp3Alpn }; + +server.listen(options); + +server.on('session', common.mustCall((session) => { + + session.on('stream', common.mustCall((stream) => { + assert(stream.submitInitialHeaders({ ':status': '200' })); + + [-1, Number.MAX_SAFE_INTEGER + 1].forEach((highWaterMark) => { + assert.throws(() => stream.pushStream({}, { highWaterMark }), { + code: 'ERR_OUT_OF_RANGE' + }); + }); + ['', 1n, {}, [], false].forEach((highWaterMark) => { + assert.throws(() => stream.pushStream({}, { highWaterMark }), { + code: 'ERR_INVALID_ARG_TYPE' + }); + }); + ['', 1, 1n, true, [], {}, 'zebra'].forEach((defaultEncoding) => { + assert.throws(() => stream.pushStream({}, { defaultEncoding }), { + code: 'ERR_INVALID_ARG_VALUE' + }); + }); + + const push = stream.pushStream({ + ':method': 'GET', + ':scheme': 'https', + ':authority': 'localhost', + ':path': '/foo' + }); + assert(push); + push.submitInitialHeaders({ ':status': '200' }); + push.end('testing'); + push.on('close', common.mustCall()); + push.on('finish', common.mustCall()); + + stream.end('hello world'); + stream.resume(); + stream.on('end', common.mustCall()); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':path', '/' ], + [ ':authority', 'localhost' ], + [ ':scheme', 'https' ], + [ ':method', 'POST' ] + ]; + assert.deepStrictEqual(expected, headers); + })); + stream.on('informationalHeaders', common.mustNotCall()); + stream.on('trailingHeaders', common.mustNotCall()); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + client = createSocket({ client: options }); + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + maxStreamsUni: 10, + h3: { maxPushes: 10 } + }); + + req.on('stream', common.mustCall((stream) => { + let data = ''; + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [':status', '200'] + ]; + assert.deepStrictEqual(expected, headers); + })); + + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'testing'); + })); + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + + stream.on('pushHeaders', common.mustCall((headers, push_id) => { + const expected = [ + [ ':path', '/foo' ], + [ ':authority', 'localhost' ], + [ ':scheme', 'https' ], + [ ':method', 'GET' ] + ]; + assert.deepStrictEqual(expected, headers); + assert.strictEqual(push_id, 0); + })); + + assert(stream.submitInitialHeaders({ + ':method': 'POST', + ':scheme': 'https', + ':authority': 'localhost', + ':path': '/', + })); + + stream.end('hello world'); + stream.resume(); + stream.on('finish', common.mustCall()); + stream.on('end', common.mustCall()); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':status', '200' ] + ]; + assert.deepStrictEqual(expected, headers); + })); + stream.on('informationalHeaders', common.mustNotCall()); + stream.on('trailingHeaders', common.mustNotCall()); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-http3-trailers.js b/test/parallel/test-quic-http3-trailers.js new file mode 100644 index 00000000000000..58d84098d9b175 --- /dev/null +++ b/test/parallel/test-quic-http3-trailers.js @@ -0,0 +1,109 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests a simple QUIC HTTP/3 client/server round-trip + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { key, cert, ca, kHttp3Alpn } = require('../common/quic'); + +const { createSocket } = require('quic'); + +let client; +const server = createSocket(); + +const countdown = new Countdown(1, () => { + server.close(); + client.close(); +}); + +const options = { key, cert, ca, alpn: kHttp3Alpn }; + +server.listen(options); + +server.on('session', common.mustCall((session) => { + + session.on('stream', common.mustCall((stream) => { + assert(stream.submitInitialHeaders({ ':status': '200' })); + + stream.submitTrailingHeaders({ 'a': 1 }); + stream.end('hello world'); + stream.resume(); + stream.on('end', common.mustCall()); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':path', '/' ], + [ ':authority', 'localhost' ], + [ ':scheme', 'https' ], + [ ':method', 'POST' ] + ]; + assert.deepStrictEqual(expected, headers); + })); + + stream.on('trailingHeaders', common.mustCall((headers) => { + const expected = [ [ 'b', '2' ] ]; + assert.deepStrictEqual(expected, headers); + })); + + stream.on('informationalHeaders', common.mustNotCall()); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + client = createSocket({ client: options }); + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + maxStreamsUni: 10, + h3: { maxPushes: 10 } + }); + + req.on('close', common.mustCall()); + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + + stream.on('trailingHeaders', common.mustCall((headers) => { + const expected = [ [ 'a', '1' ] ]; + assert.deepStrictEqual(expected, headers); + })); + + assert(stream.submitInitialHeaders({ + ':method': 'POST', + ':scheme': 'https', + ':authority': 'localhost', + ':path': '/', + })); + + stream.submitTrailingHeaders({ 'b': 2 }); + stream.end('hello world'); + stream.resume(); + stream.on('finish', common.mustCall()); + stream.on('end', common.mustCall()); + + stream.on('initialHeaders', common.mustCall((headers) => { + const expected = [ + [ ':status', '200' ] + ]; + assert.deepStrictEqual(expected, headers); + })); + stream.on('informationalHeaders', common.mustNotCall()); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-idle-timeout.js b/test/parallel/test-quic-idle-timeout.js new file mode 100644 index 00000000000000..af38c511f55531 --- /dev/null +++ b/test/parallel/test-quic-idle-timeout.js @@ -0,0 +1,73 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +const kALPN = 'zzz'; +const idleTimeout = common.platformTimeout(1); +const options = { key, cert, ca, alpn: kALPN }; + +// Test idleTimeout. The test will hang and fail with a timeout +// if the idleTimeout is not working correctly. + +(async () => { + const server = createSocket({ server: options }); + const client = createSocket({ client: options }); + + server.listen(); + server.on('session', common.mustCall()); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + idleTimeout, + }); + + await once(session, 'close'); + + assert(session.idleTimeout); + client.close(); + server.close(); + + await Promise.all([ + once(client, 'close'), + once(server, 'close') + ]); +})().then(common.mustCall()); + + +(async () => { + const server = createSocket({ server: options }); + const client = createSocket({ client: options }); + + server.listen({ idleTimeout }); + + server.on('session', common.mustCall(async (session) => { + await once(session, 'close'); + assert(session.idleTimeout); + client.close(); + server.close(); + await Promise.all([ + once(client, 'close'), + once(server, 'close') + ]); + })); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + await once(session, 'close'); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-ipv6only.js b/test/parallel/test-quic-ipv6only.js new file mode 100644 index 00000000000000..d54200d658c303 --- /dev/null +++ b/test/parallel/test-quic-ipv6only.js @@ -0,0 +1,128 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); + +if (!common.hasIPv6) + common.skip('missing ipv6'); + +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +const kALPN = 'zzz'; + +// Setting `type` to `udp4` while setting `ipv6Only` to `true` is possible +// and it will throw an error. +{ + const server = createSocket({ endpoint: { type: 'udp4', ipv6Only: true } }); + + server.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'EINVAL'); + assert.strictEqual(err.message, 'bind EINVAL 0.0.0.0'); + })); + + server.listen({ key, cert, ca, alpn: kALPN }); +} + +// Connecting ipv6 server by "127.0.0.1" should work when `ipv6Only` +// is set to `false`. +(async () => { + const server = createSocket({ endpoint: { type: 'udp6', ipv6Only: false } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + + server.on('session', common.mustCall((serverSession) => { + serverSession.on('stream', common.mustCall()); + })); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + await once(session, 'secure'); + + const stream = session.openStream({ halfOpen: true }); + stream.end('hello'); + + await once(stream, 'close'); + + client.close(); + server.close(); + + await Promise.allSettled([ + once(client, 'close'), + once(server, 'close') + ]); +})().then(common.mustCall()); + +// When the `ipv6Only` set to `true`, a client cann't connect to it +// through "127.0.0.1". +(async () => { + const server = createSocket({ endpoint: { type: 'udp6', ipv6Only: true } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + server.on('session', common.mustNotCall()); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + idleTimeout: common.platformTimeout(1), + }); + + session.on('secure', common.mustNotCall()); + + await once(session, 'close'); + + client.close(); + server.close(); + + await Promise.allSettled([ + once(client, 'close'), + once(server, 'close') + ]); +})(); + +// Creating the QuicSession fails when connect type does not match the +// the connect IP address... +(async () => { + const server = createSocket({ endpoint: { type: 'udp6' } }); + const client = createSocket({ client: { key, cert, ca, alpn: kALPN } }); + + server.listen({ key, cert, ca, alpn: kALPN }); + server.on('session', common.mustNotCall()); + + await once(server, 'ready'); + + const session = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + type: 'udp6', + idleTimeout: common.platformTimeout(1), + }); + + session.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ERR_QUICCLIENTSESSION_FAILED'); + client.close(); + server.close(); + })); + + session.on('secure', common.mustNotCall()); + session.on('close', common.mustCall()); + + await Promise.allSettled([ + once(client, 'close'), + once(server, 'close') + ]); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-keylog.js b/test/parallel/test-quic-keylog.js new file mode 100644 index 00000000000000..ef0702f5297897 --- /dev/null +++ b/test/parallel/test-quic-keylog.js @@ -0,0 +1,67 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests QUIC keylogging + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); + +const { createSocket } = require('quic'); + +const kKeylogs = [ + /^CLIENT_HANDSHAKE_TRAFFIC_SECRET .*/, + /^SERVER_HANDSHAKE_TRAFFIC_SECRET .*/, + /^QUIC_CLIENT_HANDSHAKE_TRAFFIC_SECRET .*/, + /^QUIC_SERVER_HANDSHAKE_TRAFFIC_SECRET .*/, + /^CLIENT_TRAFFIC_SECRET_0 .*/, + /^SERVER_TRAFFIC_SECRET_0 .*/, + /^QUIC_CLIENT_TRAFFIC_SECRET_0 .*/, + /^QUIC_SERVER_TRAFFIC_SECRET_0 .*/, +]; + +const options = { key, cert, ca, alpn: 'zzz' }; + +(async () => { + const server = createSocket({ server: options }); + const client = createSocket({ client: options }); + + const kServerKeylogs = Array.from(kKeylogs); + const kClientKeylogs = Array.from(kKeylogs); + + server.listen(); + + server.on('session', common.mustCall((session) => { + session.on('keylog', common.mustCall((line) => { + assert.match(line.toString(), kServerKeylogs.shift()); + }, kServerKeylogs.length)); + })); + + await once(server, 'ready'); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('keylog', common.mustCall((line) => { + assert.match(line.toString(), kClientKeylogs.shift()); + }, kClientKeylogs.length)); + + await once(req, 'secure'); + + server.close(); + client.close(); + + await Promise.allSettled([ + once(server, 'close'), + once(client, 'close') + ]); + + assert.strictEqual(kServerKeylogs.length, 0); + assert.strictEqual(kClientKeylogs.length, 0); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-maxconnectionsperhost.js b/test/parallel/test-quic-maxconnectionsperhost.js new file mode 100644 index 00000000000000..206442646710c9 --- /dev/null +++ b/test/parallel/test-quic-maxconnectionsperhost.js @@ -0,0 +1,86 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { createSocket } = require('quic'); +const assert = require('assert'); +const Countdown = require('../common/countdown'); +const { key, cert, ca } = require('../common/quic'); +const kServerName = 'agent2'; +const kALPN = 'zzz'; + +// QuicSockets must throw errors when maxConnectionsPerHost is not a +// safe integer or is out of range. +{ + [-1, 0, Number.MAX_SAFE_INTEGER + 1, 1.1].forEach((maxConnectionsPerHost) => { + assert.throws(() => createSocket({ maxConnectionsPerHost }), { + code: 'ERR_OUT_OF_RANGE' + }); + }); +} + +// Test that new client sessions will be closed when it exceeds +// maxConnectionsPerHost. +{ + const kMaxConnectionsPerHost = 5; + const kIdleTimeout = 0; + + let client; + let server; + + const countdown = new Countdown(kMaxConnectionsPerHost + 1, () => { + client.close(); + server.close(); + }); + + function connect() { + return client.connect({ + key, + cert, + ca, + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + servername: kServerName, + alpn: kALPN, + idleTimeout: kIdleTimeout, + }); + } + + server = createSocket({ maxConnectionsPerHost: kMaxConnectionsPerHost }); + + server.listen({ key, cert, ca, alpn: kALPN, idleTimeout: kIdleTimeout }); + + server.on('session', common.mustCall(() => {}, kMaxConnectionsPerHost)); + + server.on('close', common.mustCall(() => { + assert.strictEqual(server.serverBusyCount, 1n); + })); + + server.on('ready', common.mustCall(() => { + client = createSocket(); + + const sessions = []; + + for (let i = 0; i < kMaxConnectionsPerHost; i += 1) { + const req = connect(); + req.on('error', common.mustNotCall()); + req.on('close', common.mustCall(() => countdown.dec())); + sessions.push(req); + } + + const extra = connect(); + extra.on('error', console.log); + extra.on('close', common.mustCall(() => { + countdown.dec(); + // Shutdown the remaining open sessions. + setImmediate(common.mustCall(() => { + for (const req of sessions) + req.close(); + })); + })); + + })); +} diff --git a/test/parallel/test-quic-process-cleanup.js b/test/parallel/test-quic-process-cleanup.js new file mode 100644 index 00000000000000..85e9320a77f5ba --- /dev/null +++ b/test/parallel/test-quic-process-cleanup.js @@ -0,0 +1,57 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that shutting down a process containing an active QUIC server behaves +// well. We use Workers because they have a more clearly defined shutdown +// sequence and we can stop execution at any point. + +const quic = require('quic'); +const { Worker, workerData } = require('worker_threads'); + +if (workerData == null) { + new Worker(__filename, { workerData: { removeFromSocket: true } }); + new Worker(__filename, { workerData: { removeFromSocket: false } }); + return; +} + +const { key, cert, ca } = require('../common/quic'); +const options = { key, cert, ca, alpn: 'meow' }; + +const server = quic.createSocket({ server: options }); + +server.listen(); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: false }); + stream.write('Hi!'); + stream.on('data', common.mustNotCall()); + stream.on('finish', common.mustNotCall()); + stream.on('close', common.mustNotCall()); + stream.on('end', common.mustNotCall()); + })); + + session.on('close', common.mustNotCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: options }); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustCall(() => { + if (workerData.removeFromSocket) + req.removeFromSocket(); + process.exit(); // Exits the worker thread + })); + + req.on('close', common.mustNotCall()); +})); + +server.on('close', common.mustNotCall()); diff --git a/test/parallel/test-quic-qlog.js b/test/parallel/test-quic-qlog.js new file mode 100644 index 00000000000000..916416c33fab73 --- /dev/null +++ b/test/parallel/test-quic-qlog.js @@ -0,0 +1,66 @@ +// Flags: --expose-internals --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { makeUDPPair } = require('../common/udppair'); +const assert = require('assert'); +const quic = require('quic'); +const { kUDPHandleForTesting } = require('internal/quic/core'); + +const { key, cert, ca } = require('../common/quic'); + +const { serverSide, clientSide } = makeUDPPair(); + +const server = quic.createSocket({ + validateAddress: true, + endpoint: { [kUDPHandleForTesting]: serverSide._handle }, + qlog: true +}); + +serverSide.afterBind(); +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + gatherQlog(session, 'server'); + + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: true }); + stream.end('Hi!'); + })); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: clientSide._handle }, + client: { key, cert, ca, alpn: 'meow' }, + qlog: true + }); + clientSide.afterBind(); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + qlog: true + }); + + gatherQlog(req, 'client'); + + req.on('stream', common.mustCall((stream) => { + stream.resume(); + stream.on('end', common.mustCall(() => { + req.close(); + })); + })); +})); + +function gatherQlog(session, id) { + let log = ''; + session.on('qlog', (chunk) => log += chunk); + session.on('close', common.mustCall(() => { + const { qlog_version, traces } = JSON.parse(log); + assert.strictEqual(typeof qlog_version, 'string'); + assert.strictEqual(typeof traces[0].events, 'object'); + })); +} diff --git a/test/parallel/test-quic-quicendpoint-address.js b/test/parallel/test-quic-quicendpoint-address.js new file mode 100644 index 00000000000000..17c8f6991c5e12 --- /dev/null +++ b/test/parallel/test-quic-quicendpoint-address.js @@ -0,0 +1,87 @@ +// Flags: --no-warnings +'use strict'; + +// Tests multiple aspects of QuicSocket multiple endpoint support + +const common = require('../common'); +const { once } = require('events'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); + +const { key, cert, ca } = require('../common/quic'); + +const { createSocket } = require('quic'); + +async function Test1(options, address) { + const server = createSocket(options); + assert.strictEqual(server.endpoints.length, 1); + assert.strictEqual(server.endpoints[0].bound, false); + assert.deepStrictEqual({}, server.endpoints[0].address); + + server.listen({ key, cert, ca, alpn: 'zzz' }); + + await once(server, 'ready'); + assert.strictEqual(server.endpoints.length, 1); + const endpoint = server.endpoints[0]; + assert.strictEqual(endpoint.bound, true); + assert.strictEqual(endpoint.destroyed, false); + assert.strictEqual(typeof endpoint.address.port, 'number'); + assert.strictEqual(endpoint.address.address, address); + server.close(); + assert.strictEqual(endpoint.destroyed, true); +} + +async function Test2() { + // Creates a server with multiple endpoints (one on udp4 and udp6) + const server = createSocket({ endpoint: { type: 'udp6' } }); + server.addEndpoint(); + assert.strictEqual(server.endpoints.length, 2); + assert.strictEqual(server.endpoints[0].bound, false); + assert.deepStrictEqual({}, server.endpoints[0].address); + + server.listen({ key, cert, ca, alpn: 'zzz' }); + + await once(server, 'ready'); + + assert.strictEqual(server.endpoints.length, 2); + + { + const endpoint = server.endpoints[0]; + assert.strictEqual(endpoint.bound, true); + assert.strictEqual(endpoint.destroyed, false); + assert.strictEqual(endpoint.address.family, 'IPv6'); + assert.strictEqual(typeof endpoint.address.port, 'number'); + assert.strictEqual(endpoint.address.address, '::'); + } + + { + const endpoint = server.endpoints[1]; + assert.strictEqual(endpoint.bound, true); + assert.strictEqual(endpoint.destroyed, false); + assert.strictEqual(endpoint.address.family, 'IPv4'); + assert.strictEqual(typeof endpoint.address.port, 'number'); + assert.strictEqual(endpoint.address.address, '0.0.0.0'); + } + + server.close(); + for (const endpoint of server.endpoints) + assert.strictEqual(endpoint.destroyed, true); +} + +const tests = [ + Test1({}, '0.0.0.0'), + Test1({ endpoint: { port: 0 } }, '0.0.0.0'), + Test1({ endpoint: { address: '127.0.0.1', port: 0 } }, '127.0.0.1'), + Test1({ endpoint: { address: 'localhost', port: 0 } }, '127.0.0.1') +]; + +if (common.hasIPv6) { + tests.push( + Test1({ endpoint: { type: 'udp6' } }, '::'), + Test1({ endpoint: { type: 'udp6', address: 'localhost' } }, '::1'), + Test2()); +} + +Promise.all(tests); diff --git a/test/parallel/test-quic-quicession-server-openstream-pending.js b/test/parallel/test-quic-quicession-server-openstream-pending.js new file mode 100644 index 00000000000000..6207834cab0d8a --- /dev/null +++ b/test/parallel/test-quic-quicession-server-openstream-pending.js @@ -0,0 +1,59 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that opening a stream works even if the session isn’t ready yet. + +const assert = require('assert'); +const quic = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); +const options = { key, cert, ca, alpn: 'meow' }; + +(async () => { + const server = quic.createSocket({ server: options }); + const client = quic.createSocket({ client: options }); + + server.listen(); + + server.on('session', common.mustCall((session) => { + // The server can create a stream immediately without waiting + // for the secure event... however, the data will not actually + // be transmitted until the handshake is completed. + const stream = session.openStream({ halfOpen: true }); + stream.on('close', common.mustCall()); + stream.on('error', console.log); + stream.end('hello'); + + session.on('stream', common.mustNotCall()); + })); + + await once(server, 'ready'); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + const [ stream ] = await once(req, 'stream'); + + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall()); + + await once(stream, 'close'); + + assert.strictEqual(data, 'hello'); + + server.close(); + client.close(); + + await Promise.all([ + once(server, 'close'), + once(client, 'close') + ]); + +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-openstream-pending.js b/test/parallel/test-quic-quicsession-openstream-pending.js new file mode 100644 index 00000000000000..6b406a396e1bba --- /dev/null +++ b/test/parallel/test-quic-quicsession-openstream-pending.js @@ -0,0 +1,64 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Test that opening a stream works even if the session isn’t ready yet. + +const assert = require('assert'); +const quic = require('quic'); +const { key, cert, ca } = require('../common/quic'); +const { once } = require('events'); +const options = { key, cert, ca, alpn: 'meow' }; + +(async () => { + const server = quic.createSocket({ server: options }); + const client = quic.createSocket({ client: options }); + + server.listen(); + + server.on('session', common.mustCall((session) => { + session.on('stream', common.mustCall(async (stream) => { + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + await once(stream, 'end'); + assert.strictEqual(data, 'Hello!'); + })); + })); + + await once(server, 'ready'); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + // In this case, the QuicStream is usable but corked + // until the underlying internal QuicStream handle + // has been created, which will not happen until + // after the TLS handshake has been completed. + const stream = req.openStream({ halfOpen: true }); + stream.end('Hello!'); + stream.on('error', common.mustNotCall()); + stream.resume(); + assert(!req.allowEarlyData); + assert(!req.handshakeComplete); + assert(stream.pending); + + await once(stream, 'ready'); + + assert(req.handshakeComplete); + assert(!stream.pending); + + await once(stream, 'close'); + + server.close(); + client.close(); + + await Promise.all([ + once(server, 'close'), + once(client, 'close') + ]); +})().then(common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-resume.js b/test/parallel/test-quic-quicsession-resume.js new file mode 100644 index 00000000000000..b1af4663b0ab54 --- /dev/null +++ b/test/parallel/test-quic-quicsession-resume.js @@ -0,0 +1,100 @@ +'use strict'; + +// Tests a simple QUIC client/server round-trip + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { Buffer } = require('buffer'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug, +} = require('../common/quic'); + +const { createSocket } = require('quic'); + +const options = { key, cert, ca, alpn: 'zzz' }; + +const server = createSocket({ server: options }); +const client = createSocket({ client: options }); + +const countdown = new Countdown(2, () => { + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + assert(session.usingEarlyData); + })); + + session.on('stream', common.mustCall((stream) => { + stream.resume(); + })); +}, 2)); + +server.on('ready', common.mustCall(() => { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + const stream = req.openStream({ halfOpen: true }); + stream.end('hello'); + stream.resume(); + stream.on('close', () => countdown.dec()); + + req.on('sessionTicket', common.mustCall((ticket, params) => { + assert(ticket instanceof Buffer); + assert(params instanceof Buffer); + debug(' Ticket: %s', ticket.toString('hex')); + debug(' Params: %s', params.toString('hex')); + + // Destroy this initial client session... + req.destroy(); + + // Wait a tick then start a new one. + setImmediate(newSession, ticket, params); + }, 1)); + + function newSession(sessionTicket, remoteTransportParams) { + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + sessionTicket, + remoteTransportParams, + autoStart: false, + }); + + assert(req.allowEarlyData); + + const stream = req.openStream({ halfOpen: true }); + stream.end('hello'); + stream.on('error', common.mustNotCall()); + stream.on('close', common.mustCall(() => countdown.dec())); + + req.startHandshake(); + + // TODO(@jasnell): There's a slight bug in here in that + // calling end() will uncork the stream, causing data to + // be flushed to the C++ layer, which will trigger a + // SendPendingData that will start the handshake. That + // has the effect of short circuiting the intent of + // manual startHandshake(), which makes it not use 0RTT + // for the stream data. + + req.on('secure', common.mustCall(() => { + // TODO(@jasnell): This will be false for now because no + // early data was sent. Once we actually start making + // use of early data on the client side, this should be + // true when the early data was accepted. + assert(!req.usingEarlyData); + })); + } +})); diff --git a/test/parallel/test-quic-quicsession-send-fd.js b/test/parallel/test-quic-quicsession-send-fd.js new file mode 100644 index 00000000000000..2d208dea850729 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-fd.js @@ -0,0 +1,86 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const quic = require('quic'); +const fs = require('fs'); + +const { key, cert, ca } = require('../common/quic'); + +const variants = []; +for (const variant of ['sendFD', 'sendFile', 'sendFD+fileHandle']) { + for (const offset of [-1, 0, 100]) { + for (const length of [-1, 100]) { + variants.push({ variant, offset, length }); + } + } +} + +for (const { variant, offset, length } of variants) { + const server = quic.createSocket(); + let fd; + + server.listen({ key, cert, ca, alpn: 'meow' }); + + server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: false }); + + stream.on('data', common.mustNotCall()); + stream.on('finish', common.mustCall()); + stream.on('close', common.mustCall()); + stream.on('end', common.mustCall()); + + if (variant === 'sendFD') { + fd = fs.openSync(__filename, 'r'); + stream.sendFD(fd, { offset, length }); + } else if (variant === 'sendFD+fileHandle') { + fs.promises.open(__filename, 'r').then(common.mustCall((handle) => { + fd = handle; + stream.sendFD(handle, { offset, length }); + })); + } else { + assert.strictEqual(variant, 'sendFile'); + stream.sendFile(__filename, { offset, length }); + } + })); + + session.on('close', common.mustCall()); + })); + + server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustCall((stream) => { + const data = []; + stream.on('data', (chunk) => data.push(chunk)); + stream.on('end', common.mustCall(() => { + let expectedContent = fs.readFileSync(__filename); + if (offset !== -1) expectedContent = expectedContent.slice(offset); + if (length !== -1) expectedContent = expectedContent.slice(0, length); + assert.deepStrictEqual(Buffer.concat(data), expectedContent); + + stream.end(); + client.close(); + server.close(); + if (fd !== undefined) { + if (fd.close) fd.close().then(common.mustCall()); + else fs.closeSync(fd); + } + })); + })); + + req.on('close', common.mustCall()); + })); + + server.on('close', common.mustCall()); +} diff --git a/test/parallel/test-quic-quicsession-send-file-close-before-open.js b/test/parallel/test-quic-quicsession-send-file-close-before-open.js new file mode 100644 index 00000000000000..5e923204fe93d3 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-close-before-open.js @@ -0,0 +1,46 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const quic = require('quic'); +const fs = require('fs'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: false }); + + fs.open = common.mustCall(fs.open); + fs.close = common.mustCall(fs.close); + + stream.sendFile(__filename); + stream.destroy(); // Destroy the stream before opening the fd finishes. + + session.close(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-send-file-open-error-handled.js b/test/parallel/test-quic-quicsession-send-file-open-error-handled.js new file mode 100644 index 00000000000000..142f76538a6ded --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-open-error-handled.js @@ -0,0 +1,49 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const path = require('path'); +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: true }); + const nonexistentPath = path.resolve(__dirname, 'nonexistent.file'); + + stream.sendFile(nonexistentPath, { + onError: common.expectsError({ + code: 'ENOENT', + syscall: 'open', + path: nonexistentPath + }) + }); + + session.close(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-send-file-open-error.js b/test/parallel/test-quic-quicsession-send-file-open-error.js new file mode 100644 index 00000000000000..ac0720ad155ca0 --- /dev/null +++ b/test/parallel/test-quic-quicsession-send-file-open-error.js @@ -0,0 +1,49 @@ +// Flags: --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const path = require('path'); +const quic = require('quic'); + +const { key, cert, ca } = require('../common/quic'); + +const server = quic.createSocket(); + +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = session.openStream({ halfOpen: false }); + const nonexistentPath = path.resolve(__dirname, 'nonexistent.file'); + + stream.on('error', common.expectsError({ + code: 'ENOENT', + syscall: 'open', + path: nonexistentPath + })); + + stream.sendFile(nonexistentPath); + + session.close(); + server.close(); + })); + + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ client: { key, cert, ca, alpn: 'meow' } }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustNotCall()); + + req.on('close', common.mustCall(() => client.close())); +})); + +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicsession-server-destroy-early.js b/test/parallel/test-quic-quicsession-server-destroy-early.js new file mode 100644 index 00000000000000..95ac20b000b96f --- /dev/null +++ b/test/parallel/test-quic-quicsession-server-destroy-early.js @@ -0,0 +1,81 @@ +// Flags: --no-warnings +'use strict'; + +// Test that destroying a QuicStream immediately and synchronously +// after creation does not crash the process and closes the streams +// abruptly on both ends of the connection. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const fs = require('fs'); +const fixtures = require('../common/fixtures'); +const key = fixtures.readKey('agent1-key.pem', 'binary'); +const cert = fixtures.readKey('agent1-cert.pem', 'binary'); +const ca = fixtures.readKey('ca1-cert.pem', 'binary'); +const { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +const kServerPort = process.env.NODE_DEBUG_KEYLOG ? 5678 : 0; +const kClientPort = process.env.NODE_DEBUG_KEYLOG ? 5679 : 0; + +const kServerName = 'agent2'; // Intentionally the wrong servername +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +server.listen({ key, cert, ca, alpn: kALPN }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + if (process.env.NODE_DEBUG_KEYLOG) { + const kl = fs.createWriteStream(process.env.NODE_DEBUG_KEYLOG); + session.on('keylog', kl.write.bind(kl)); + } + + session.on('close', common.mustCall(() => { + client.close(); + server.close(); + + assert.throws(() => server.close(), { + code: 'ERR_QUICSOCKET_DESTROYED', + name: 'Error', + message: 'Cannot call close after a QuicSocket has been destroyed' + }); + })); + session.on('stream', common.mustNotCall()); + + // Prematurely destroy the session without waiting for the + // handshake to complete. + session.destroy(); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kALPN } + }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + })); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + }); + + req.on('secure', common.mustNotCall()); + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-quicsocket-close.js b/test/parallel/test-quic-quicsocket-close.js new file mode 100644 index 00000000000000..c287e958a62067 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-close.js @@ -0,0 +1,19 @@ +// Flags: --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { createSocket } = require('quic'); + +{ + const socket = createSocket(); + socket.close(common.mustCall()); + socket.on('close', common.mustCall()); + assert.throws(() => socket.close(), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: 'Cannot call close after a QuicSocket has been destroyed' + }); +} diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js new file mode 100644 index 00000000000000..9ec9312ce431c5 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-packetloss-stream-rx.js @@ -0,0 +1,109 @@ +// Flags: --no-warnings +'use strict'; + +// Tests that stream data is successfully transmitted under +// packet loss conditions on the receiving end. + +// TODO(@jasnell): We need an equivalent test that checks +// transmission end random packet loss. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug +} = require('../common/quic'); +// TODO(@jasnell): There's currently a bug in pipeline when piping +// a duplex back into to itself. +// const { pipeline } = require('stream'); + +const { createSocket } = require('quic'); + +const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const options = { key, cert, ca, alpn: 'echo' }; + +const client = createSocket({ client: options }); +const server = createSocket({ server: options }); + +// Both client and server will drop received packets about 20% of the time +// It is important to keep in mind that this will make the runtime of the +// test non-deterministic. If we encounter flaky timeouts with this test, +// the randomized packet loss will be the reason, but random packet loss +// is exactly what is being tested. So if flaky timeouts do occur, it will +// be best to extend the failure timeout for this test. +server.setDiagnosticPacketLoss({ rx: 0.2 }); +client.setDiagnosticPacketLoss({ rx: 0.2 }); + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + stream.on('data', (chunk) => stream.write(chunk)); + stream.on('end', () => stream.end()); + // TODO(@jasnell): There's currently a bug in pipeline when piping + // a duplex back into to itself. + // pipeline(stream, stream, common.mustCall((err) => { + // assert(!err); + // })); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + + let n = 0; + // This forces multiple stream packets to be sent out + // rather than all the data being written in a single + // packet. + function sendChunk() { + if (n < kData.length) { + stream.write(kData[n++], common.mustCall()); + setImmediate(sendChunk); + } else { + stream.end(); + } + } + sendChunk(); + + let data = ''; + stream.resume(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + debug('Received data: %s', kData); + assert.strictEqual(data, kData); + })); + + stream.on('close', common.mustCall(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); +})); diff --git a/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js b/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js new file mode 100644 index 00000000000000..c5453ac6b83966 --- /dev/null +++ b/test/parallel/test-quic-quicsocket-packetloss-stream-tx.js @@ -0,0 +1,109 @@ +// Flags: --no-warnings +'use strict'; + +// Tests that stream data is successfully transmitted under +// packet loss conditions on the receiving end. + +// TODO(@jasnell): We need an equivalent test that checks +// transmission end random packet loss. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug +} = require('../common/quic'); +// TODO(@jasnell): There's currently a bug in pipeline when piping +// a duplex back into to itself. +// const { pipeline } = require('stream'); + +const { createSocket } = require('quic'); + +const kData = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const options = { key, cert, ca, alpn: 'echo' }; + +const client = createSocket({ client: options }); +const server = createSocket({ server: options }); + +// Both client and server will drop transmitted packets about 20% of the time +// It is important to keep in mind that this will make the runtime of the +// test non-deterministic. If we encounter flaky timeouts with this test, +// the randomized packet loss will be the reason, but random packet loss +// is exactly what is being tested. So if flaky timeouts do occur, it will +// be best to extend the failure timeout for this test. +server.setDiagnosticPacketLoss({ tx: 0.2 }); +client.setDiagnosticPacketLoss({ tx: 0.2 }); + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + stream.on('data', (chunk) => stream.write(chunk)); + stream.on('end', () => stream.end()); + // TODO(@jasnell): There's currently a bug in pipeline when piping + // a duplex back into to itself. + // pipeline(stream, stream, common.mustCall((err) => { + // assert(!err); + // })); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + + let n = 0; + // This forces multiple stream packets to be sent out + // rather than all the data being written in a single + // packet. + function sendChunk() { + if (n < kData.length) { + stream.write(kData[n++], common.mustCall()); + setImmediate(sendChunk); + } else { + stream.end(); + } + } + sendChunk(); + + let data = ''; + stream.resume(); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + debug('Received data: %s', kData); + assert.strictEqual(data, kData); + })); + + stream.on('close', common.mustCall(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); +})); diff --git a/test/parallel/test-quic-quicsocket-serverbusy.js b/test/parallel/test-quic-quicsocket-serverbusy.js new file mode 100644 index 00000000000000..d53b32190642fb --- /dev/null +++ b/test/parallel/test-quic-quicsocket-serverbusy.js @@ -0,0 +1,62 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Tests QUIC server busy support + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { + key, + cert, + ca, + debug, + kServerPort, + kClientPort +} = require('../common/quic'); + +const { createSocket } = require('quic'); +const options = { key, cert, ca, alpn: 'zzz' }; + +let client; +const server = createSocket({ + endpoint: { port: kServerPort }, + server: options +}); + +server.on('busy', common.mustCall((busy) => { + assert.strictEqual(busy, true); +})); + +// When the server is set as busy, all connections +// will be rejected with a SERVER_BUSY response. +server.setServerBusy(); +server.listen(); + +server.on('close', common.mustCall()); +server.on('listening', common.mustCall()); +server.on('session', common.mustNotCall()); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: options + }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustNotCall()); + + req.on('close', common.mustCall(() => { + server.close(); + client.close(); + })); +})); diff --git a/test/parallel/test-quic-quicsocket.js b/test/parallel/test-quic-quicsocket.js new file mode 100644 index 00000000000000..5a5cf57505e72e --- /dev/null +++ b/test/parallel/test-quic-quicsocket.js @@ -0,0 +1,154 @@ +// Flags: --no-warnings +'use strict'; + +// Test QuicSocket constructor option errors. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); + +const { createSocket } = require('quic'); + +const socket = createSocket(); +assert(socket); + +// Before listen is called, serverSecureContext is always undefined. +assert.strictEqual(socket.serverSecureContext, undefined); + +assert.deepStrictEqual(socket.endpoints.length, 1); + +// Socket is not bound, so address should be empty +assert.deepStrictEqual(socket.endpoints[0].address, {}); + +// Socket is not bound +assert(!socket.bound); + +// Socket is not pending +assert(!socket.pending); + +// Socket is not destroyed +assert(!socket.destroyed); + +assert.strictEqual(typeof socket.duration, 'bigint'); +assert.strictEqual(typeof socket.boundDuration, 'bigint'); +assert.strictEqual(typeof socket.listenDuration, 'bigint'); +assert.strictEqual(typeof socket.bytesReceived, 'bigint'); +assert.strictEqual(socket.bytesReceived, 0n); +assert.strictEqual(socket.bytesSent, 0n); +assert.strictEqual(socket.packetsReceived, 0n); +assert.strictEqual(socket.packetsSent, 0n); +assert.strictEqual(socket.serverSessions, 0n); +assert.strictEqual(socket.clientSessions, 0n); + +const endpoint = socket.endpoints[0]; +assert(endpoint); + +// Will throw because the QuicSocket is not bound +{ + const err = { code: 'EBADF' }; + assert.throws(() => endpoint.setTTL(1), err); + assert.throws(() => endpoint.setMulticastTTL(1), err); + assert.throws(() => endpoint.setBroadcast(), err); + assert.throws(() => endpoint.setMulticastLoopback(), err); + assert.throws(() => endpoint.setMulticastInterface('0.0.0.0'), err); + // TODO(@jasnell): Verify behavior of add/drop membership then test + // assert.throws(() => endpoint.addMembership( + // '127.0.0.1', '127.0.0.1'), err); + // assert.throws(() => endpoint.dropMembership( + // '127.0.0.1', '127.0.0.1'), err); +} + +['test', null, {}, [], 1n, false].forEach((rx) => { + assert.throws(() => socket.setDiagnosticPacketLoss({ rx }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +['test', null, {}, [], 1n, false].forEach((tx) => { + assert.throws(() => socket.setDiagnosticPacketLoss({ tx }), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +[ + { rx: -1 }, + { rx: 1.1 }, + { tx: -1 }, + { tx: 1.1 } +].forEach((options) => { + assert.throws(() => socket.setDiagnosticPacketLoss(options), { + code: 'ERR_OUT_OF_RANGE' + }); +}); + +[1, 1n, [], {}, null].forEach((args) => { + assert.throws(() => socket.setServerBusy(args), { + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +socket.listen({ alpn: 'zzz' }); +assert(socket.pending); + +socket.on('ready', common.mustCall(() => { + assert(endpoint.bound); + + // QuicSocket is already listening. + assert.throws(() => socket.listen(), { + code: 'ERR_QUICSOCKET_LISTENING' + }); + + assert.strictEqual(typeof endpoint.address.address, 'string'); + assert.strictEqual(typeof endpoint.address.port, 'number'); + assert.strictEqual(typeof endpoint.address.family, 'string'); + + if (!common.isWindows) + assert.strictEqual(typeof endpoint.fd, 'number'); + + endpoint.setTTL(1); + endpoint.setMulticastTTL(1); + endpoint.setBroadcast(); + endpoint.setBroadcast(true); + endpoint.setBroadcast(false); + + endpoint.setMulticastLoopback(); + endpoint.setMulticastLoopback(true); + endpoint.setMulticastLoopback(false); + + endpoint.setMulticastInterface('0.0.0.0'); + + socket.setDiagnosticPacketLoss({ rx: 0.5, tx: 0.5 }); + + socket.destroy(); + assert(socket.destroyed); +})); + +socket.on('close', common.mustCall(() => { + [ + 'ref', + 'unref', + 'setTTL', + 'setMulticastTTL', + 'setBroadcast', + 'setMulticastLoopback', + 'setMulticastInterface', + 'addMembership', + 'dropMembership' + ].forEach((op) => { + assert.throws(() => endpoint[op](), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: `Cannot call ${op} after a QuicSocket has been destroyed` + }); + }); + + [ + 'setServerBusy', + ].forEach((op) => { + assert.throws(() => socket[op](), { + code: 'ERR_QUICSOCKET_DESTROYED', + message: `Cannot call ${op} after a QuicSocket has been destroyed` + }); + }); +})); diff --git a/test/parallel/test-quic-quicstream-close-early.js b/test/parallel/test-quic-quicstream-close-early.js new file mode 100644 index 00000000000000..358e8705e4c733 --- /dev/null +++ b/test/parallel/test-quic-quicstream-close-early.js @@ -0,0 +1,122 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug, + kServerPort, + kClientPort, + setupKeylog +} = require('../common/quic'); + +const { createSocket } = require('quic'); + +let client; +const server = createSocket({ endpoint: { port: kServerPort } }); + +const kServerName = 'agent1'; +const kALPN = 'zzz'; + +const countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + setupKeylog(session); + + session.on('secure', common.mustCall((servername, alpn, cipher) => { + const uni = session.openStream({ halfOpen: true }); + + uni.write('hi', common.expectsError()); + + uni.close(3); + + uni.on('error', common.mustCall(() => { + assert.strictEqual(uni.aborted, true); + })); + + uni.on('data', common.mustNotCall()); + uni.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed on server', + uni.id); + })); + + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + })); + + session.on('stream', common.mustNotCall()); + session.on('close', common.mustCall()); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + client = createSocket({ + endpoint: { port: kClientPort }, + client: { key, cert, ca, alpn: kALPN } + }); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port, + servername: kServerName, + }); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + + stream.write('hello', common.expectsError()); + stream.write('there', common.expectsError()); + + stream.close(1); + + stream.on('error', common.mustCall(() => { + assert.strictEqual(stream.aborted, true); + })); + + stream.on('end', common.mustNotCall()); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + stream.on('abort', common.mustNotCall()); + stream.on('data', common.mustCall((chunk) => { + assert.strictEqual(chunk.toString(), 'hi'); + })); + stream.on('end', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d ended on client', + stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed on client', + stream.id); + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-destroy.js b/test/parallel/test-quic-quicstream-destroy.js new file mode 100644 index 00000000000000..ca5c508320454a --- /dev/null +++ b/test/parallel/test-quic-quicstream-destroy.js @@ -0,0 +1,75 @@ +// Flags: --no-warnings +'use strict'; + +// Test that destroying a QuicStream immediately and synchronously +// after creation does not crash the process and closes the streams +// abruptly on both ends of the connection. + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const assert = require('assert'); +const { + debug, + key, + cert, + ca +} = require('../common/quic'); + +const { createSocket } = require('quic'); + +const options = { key, cert, ca, alpn: 'zzz' }; + +const server = createSocket({ server: options }); + +server.listen(); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + stream.destroy(); + stream.on('close', common.mustCall()); + stream.on('error', common.mustNotCall()); + assert(stream.destroyed); + })); +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + const client = createSocket({ client: options }); + + client.on('close', common.mustCall(() => { + debug('Client closing. Duration', client.duration); + })); + + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port + }); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Complete'); + + const stream = req.openStream(); + stream.write('foo'); + // Do not explicitly end the stream here. + + stream.on('finish', common.mustNotCall()); + stream.on('data', common.mustNotCall()); + stream.on('end', common.mustCall()); + + stream.on('close', common.mustCall(() => { + debug('Stream closed on client side'); + assert(stream.destroyed); + client.close(); + server.close(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-quicstream-identifiers.js b/test/parallel/test-quic-quicstream-identifiers.js new file mode 100644 index 00000000000000..dd3443c0b4211a --- /dev/null +++ b/test/parallel/test-quic-quicstream-identifiers.js @@ -0,0 +1,156 @@ +// Flags: --no-warnings +'use strict'; + +// Tests that both client and server can open +// bidirectional and unidirectional streams, +// and that the properties for each are set +// accordingly. +// +// +------+----------------------------------+ +// | ID | Stream Type | +// +------+----------------------------------+ +// | 0 | Client-Initiated, Bidirectional | +// | | | +// | 1 | Server-Initiated, Bidirectional | +// | | | +// | 2 | Client-Initiated, Unidirectional | +// | | | +// | 3 | Server-Initiated, Unidirectional | +// +------+----------------------------------+ + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { debug, key, cert } = require('../common/quic'); + +const { createSocket } = require('quic'); +const options = { key, cert, alpn: 'zzz' }; + +let client; +const server = createSocket({ server: options }); + +const countdown = new Countdown(4, () => { + debug('Countdown expired. Closing sockets'); + server.close(); + client.close(); +}); + +const closeHandler = common.mustCall(() => countdown.dec(), 4); + +server.listen(); +server.on('session', common.mustCall((session) => { + debug('QuicServerSession created'); + session.on('secure', common.mustCall(() => { + debug('QuicServerSession TLS Handshake Completed.'); + + ([3, 1n, [], {}, null, 'meow']).forEach((halfOpen) => { + assert.throws(() => session.openStream({ halfOpen }), { + code: 'ERR_INVALID_ARG_TYPE', + }); + }); + + const uni = session.openStream({ halfOpen: true }); + uni.end('test'); + debug('Unidirectional, Server-initiated stream %d opened', uni.id); + + const bidi = session.openStream(); + bidi.end('test'); + bidi.resume(); + bidi.on('end', common.mustCall()); + debug('Bidirectional, Server-initiated stream %d opened', bidi.id); + + assert.strictEqual(uni.id, 3); + assert(uni.unidirectional); + assert(uni.serverInitiated); + assert(!uni.bidirectional); + assert(!uni.clientInitiated); + + assert.strictEqual(bidi.id, 1); + assert(bidi.bidirectional); + assert(bidi.serverInitiated); + assert(!bidi.unidirectional); + assert(!bidi.clientInitiated); + })); + + session.on('stream', common.mustCall((stream) => { + assert(stream.clientInitiated); + assert(!stream.serverInitiated); + switch (stream.id) { + case 0: + debug('Bidirectional, Client-initiated stream %d received', stream.id); + assert(stream.bidirectional); + assert(!stream.unidirectional); + stream.end('test'); + break; + case 2: + debug('Unidirectional, Client-initiated stream %d receieved', + stream.id); + assert(stream.unidirectional); + assert(!stream.bidirectional); + break; + } + stream.resume(); + stream.on('end', common.mustCall()); + }, 2)); +})); + +server.on('ready', common.mustCall(() => { + debug('Server listening on port %d', server.endpoints[0].address.port); + client = createSocket({ client: options }); + const req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Completed'); + const bidi = req.openStream(); + bidi.end('test'); + bidi.resume(); + bidi.on('close', closeHandler); + assert.strictEqual(bidi.id, 0); + debug('Bidirectional, Client-initiated stream %d opened', bidi.id); + + assert(bidi.clientInitiated); + assert(bidi.bidirectional); + assert(!bidi.serverInitiated); + assert(!bidi.unidirectional); + + const uni = req.openStream({ halfOpen: true }); + uni.end('test'); + uni.on('close', closeHandler); + assert.strictEqual(uni.id, 2); + debug('Unidirectional, Client-initiated stream %d opened', uni.id); + + assert(uni.clientInitiated); + assert(!uni.bidirectional); + assert(!uni.serverInitiated); + assert(uni.unidirectional); + })); + + req.on('stream', common.mustCall((stream) => { + assert(stream.serverInitiated); + assert(!stream.clientInitiated); + switch (stream.id) { + case 1: + debug('Bidirectional, Server-initiated stream %d received', stream.id); + assert(!stream.unidirectional); + assert(stream.bidirectional); + stream.end(); + break; + case 3: + debug('Unidirectional, Server-initiated stream %d received', stream.id); + assert(stream.unidirectional); + assert(!stream.bidirectional); + } + stream.resume(); + stream.on('end', common.mustCall()); + stream.on('close', closeHandler); + }, 2)); + +})); + +server.on('listening', common.mustCall()); diff --git a/test/parallel/test-quic-simple-client-migrate.js b/test/parallel/test-quic-simple-client-migrate.js new file mode 100644 index 00000000000000..b25fd43716f8e6 --- /dev/null +++ b/test/parallel/test-quic-simple-client-migrate.js @@ -0,0 +1,108 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const { + key, + cert, + ca, + debug, +} = require('../common/quic'); + +const { createSocket } = require('quic'); +const { pipeline } = require('stream'); + +let req; +let client; +let client2; +const server = createSocket(); + +const options = { key, cert, ca, alpn: 'zzz' }; +const countdown = new Countdown(2, () => { + debug('Countdown expired. Destroying sockets'); + req.close(); + server.close(); + client2.close(); +}); + +server.listen(options); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + + session.on('stream', common.mustCall((stream) => { + debug('Bidirectional, Client-initiated stream %d received', stream.id); + pipeline(stream, stream, common.mustCall()); + + session.openStream({ halfOpen: true }).end('Hello from the server'); + })); + +})); + +server.on('ready', common.mustCall(() => { + debug('Server is listening on port %d', server.endpoints[0].address.port); + + client = createSocket({ client: options }); + client2 = createSocket({ client: options }); + + req = client.connect({ + address: common.localhostIPv4, + port: server.endpoints[0].address.port, + }); + + client.on('close', common.mustCall()); + + req.on('secure', common.mustCall(() => { + debug('QuicClientSession TLS Handshake Complete'); + + let data = ''; + + const stream = req.openStream(); + debug('Bidirectional, Client-initiated stream %d opened', stream.id); + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'Hello from the client'); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Bidirectional, Client-initiated stream %d closed', stream.id); + countdown.dec(); + })); + // Send some data on one connection... + stream.write('Hello '); + + // Wait just a bit, then migrate to a different + // QuicSocket and continue sending. + setTimeout(common.mustCall(() => { + req.setSocket(client2, (err) => { + assert(!err); + client.close(); + stream.end('from the client'); + }); + }), common.platformTimeout(100)); + })); + + req.on('stream', common.mustCall((stream) => { + debug('Unidirectional, Server-initiated stream %d received', stream.id); + let data = ''; + stream.setEncoding('utf8'); + stream.on('data', (chunk) => data += chunk); + stream.on('end', common.mustCall(() => { + assert.strictEqual(data, 'Hello from the server'); + debug('Client received expected data for stream %d', stream.id); + })); + stream.on('close', common.mustCall(() => { + debug('Unidirectional, Server-initiated stream %d closed', stream.id); + countdown.dec(); + })); + })); +})); + +server.on('listening', common.mustCall()); +server.on('close', common.mustCall()); diff --git a/test/parallel/test-quic-statelessreset.js b/test/parallel/test-quic-statelessreset.js new file mode 100644 index 00000000000000..c1f4a28f108337 --- /dev/null +++ b/test/parallel/test-quic-statelessreset.js @@ -0,0 +1,76 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +// Testing stateless reset + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const { internalBinding } = require('internal/test/binding'); +const assert = require('assert'); + +const { key, cert, ca } = require('../common/quic'); + +const { + kHandle, +} = require('internal/stream_base_commons'); +const { silentCloseSession } = internalBinding('quic'); + +const { createSocket } = require('quic'); + +const kStatelessResetToken = + Buffer.from('000102030405060708090A0B0C0D0E0F', 'hex'); + +let client; + +const server = createSocket({ statelessResetSecret: kStatelessResetToken }); + +server.listen({ key, cert, ca, alpn: 'zzz' }); + +server.on('session', common.mustCall((session) => { + session.on('stream', common.mustCall((stream) => { + // silentCloseSession is an internal-only testing tool + // that allows us to prematurely destroy a QuicSession + // without the proper communication flow with the connected + // peer. We call this to simulate a local crash that loses + // state, which should trigger the server to send a + // stateless reset token to the client. + silentCloseSession(session[kHandle]); + })); + + session.on('close', common.mustCall()); +})); + +server.on('close', common.mustCall(() => { + // Verify stats recording + assert.strictEqual(server.statelessResetCount, 1n); +})); + +server.on('ready', common.mustCall(() => { + const endpoint = server.endpoints[0]; + + client = createSocket({ client: { key, cert, ca, alpn: 'zzz' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + }); + + req.on('secure', common.mustCall(() => { + const stream = req.openStream(); + stream.end('hello'); + stream.resume(); + stream.on('close', common.mustCall()); + })); + + req.on('close', common.mustCall(() => { + assert.strictEqual(req.statelessReset, true); + server.close(); + client.close(); + })); + +})); diff --git a/test/parallel/test-quic-with-fake-udp.js b/test/parallel/test-quic-with-fake-udp.js new file mode 100644 index 00000000000000..b96fe681d6a006 --- /dev/null +++ b/test/parallel/test-quic-with-fake-udp.js @@ -0,0 +1,61 @@ +// Flags: --expose-internals --no-warnings +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +// Tests that QUIC works properly when using a pair of mocked UDP ports. + +const { makeUDPPair } = require('../common/udppair'); +const assert = require('assert'); +const quic = require('quic'); +const { kUDPHandleForTesting } = require('internal/quic/core'); + +const { key, cert, ca } = require('../common/quic'); + +const { serverSide, clientSide } = makeUDPPair(); + +const server = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: serverSide._handle } +}); + +serverSide.afterBind(); +server.listen({ key, cert, ca, alpn: 'meow' }); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall(() => { + const stream = session.openStream({ halfOpen: false }); + stream.end('Hi!'); + stream.on('data', common.mustNotCall()); + stream.on('finish', common.mustCall()); + stream.on('close', common.mustNotCall()); + stream.on('end', common.mustNotCall()); + })); + + session.on('close', common.mustNotCall()); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + endpoint: { [kUDPHandleForTesting]: clientSide._handle }, + client: { key, cert, ca, alpn: 'meow' } + }); + clientSide.afterBind(); + + const req = client.connect({ + address: 'localhost', + port: server.endpoints[0].address.port + }); + + req.on('stream', common.mustCall((stream) => { + stream.on('data', common.mustCall((data) => { + assert.strictEqual(data.toString(), 'Hi!'); + })); + + stream.on('end', common.mustCall()); + })); + + req.on('close', common.mustNotCall()); +})); + +server.on('close', common.mustNotCall()); diff --git a/test/parallel/test-validators.js b/test/parallel/test-validators.js index 1c36cc17ec046b..814c201a2aafa0 100644 --- a/test/parallel/test-validators.js +++ b/test/parallel/test-validators.js @@ -3,11 +3,14 @@ require('../common'); const assert = require('assert'); +const { Buffer } = require('buffer'); const { validateArray, validateBoolean, + validateBuffer, validateInteger, validateObject, + validateString, } = require('internal/validators'); const { MAX_SAFE_INTEGER, MIN_SAFE_INTEGER } = Number; const outOfRangeError = { @@ -23,6 +26,31 @@ const invalidArgValueError = { name: 'TypeError', }; +{ + // validateString tests + validateString('hi', 'foo'); + + [1, false, [], {}, NaN, 1n, null, undefined].forEach((i) => { + assert.throws(() => validateString(i, 'foo'), invalidArgTypeError); + }); + + validateString(undefined, 'foo', { allowUndefined: true }); +} + +{ + // validateBuffer tests + validateBuffer(Buffer.from('hi'), 'foo'); + validateBuffer(Buffer.alloc(10), 'foo'); + validateBuffer(new Uint8Array(10), 'foo'); + validateBuffer(new DataView((new Uint8Array(10)).buffer)); + + [1, false, '', {}, [], 1n, null, undefined].forEach((i) => { + assert.throws(() => validateBuffer(i, 'foo'), invalidArgTypeError); + }); + + validateBuffer(undefined, 'foo', { allowUndefined: true }); +} + { // validateInteger tests. @@ -37,8 +65,15 @@ const invalidArgValueError = { }, outOfRangeError); // validateInteger() works with unsafe integers. - validateInteger(MAX_SAFE_INTEGER + 1, 'foo', 0, MAX_SAFE_INTEGER + 1); - validateInteger(MIN_SAFE_INTEGER - 1, 'foo', MIN_SAFE_INTEGER - 1); + validateInteger( + MAX_SAFE_INTEGER + 1, + 'foo', + { min: 0, max: MAX_SAFE_INTEGER + 1 }); + validateInteger( + MIN_SAFE_INTEGER - 1, + 'foo', + { min: MIN_SAFE_INTEGER - 1 }); + validateInteger(undefined, 'foo', { allowUndefined: true }); } { @@ -69,6 +104,8 @@ const invalidArgValueError = { validateBoolean(val, 'foo'); }, invalidArgTypeError); }); + + validateBoolean(undefined, 'foo', { allowUndefined: true }); } { diff --git a/test/pummel/test-heapdump-quic.js b/test/pummel/test-heapdump-quic.js new file mode 100644 index 00000000000000..68526751915988 --- /dev/null +++ b/test/pummel/test-heapdump-quic.js @@ -0,0 +1,152 @@ +// Flags: --expose-internals +'use strict'; +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const quic = require('quic'); + +const { recordState } = require('../common/heap'); +const fixtures = require('../common/fixtures'); +const key = fixtures.readKey('agent1-key.pem', 'binary'); +const cert = fixtures.readKey('agent1-cert.pem', 'binary'); +const ca = fixtures.readKey('ca1-cert.pem', 'binary'); + +{ + const state = recordState(); + state.validateSnapshotNodes('Node / QuicStream', []); + state.validateSnapshotNodes('Node / QuicSession', []); + state.validateSnapshotNodes('Node / QuicSocket', []); +} + +const server = quic.createSocket({ port: 0, validateAddress: true }); + +server.listen({ + key, + cert, + ca, + rejectUnauthorized: false, + maxCryptoBuffer: 4096, + alpn: 'meow' +}); + +server.on('session', common.mustCall((session) => { + session.on('secure', common.mustCall((servername, alpn, cipher) => { + // eslint-disable-next-line no-unused-vars + const stream = session.openStream({ halfOpen: false }); + + const state = recordState(); + + state.validateSnapshotNodes('Node / QuicSocket', [ + { + children: [ + { node_name: 'QuicSocket', edge_name: 'wrapped' }, + { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, + { node_name: 'Node / sessions', edge_name: 'sessions' }, + { node_name: 'Node / dcid_to_scid', edge_name: 'dcid_to_scid' }, + ] + } + ], { loose: true }); + + state.validateSnapshotNodes('Node / QuicStream', [ + { + children: [ + { node_name: 'QuicStream', edge_name: 'wrapped' }, + { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, + { node_name: 'Node / QuicBuffer', edge_name: 'buffer' }, + { node_name: 'Node / HistogramBase', edge_name: 'data_rx_rate' }, + { node_name: 'Node / HistogramBase', edge_name: 'data_rx_size' }, + { node_name: 'Node / HistogramBase', edge_name: 'data_rx_ack' } + ] + } + ], { loose: true }); + + state.validateSnapshotNodes('Node / QuicBuffer', [ + { + children: [ + { node_name: 'Node / length', edge_name: 'length' } + ] + } + ], { loose: true }); + + state.validateSnapshotNodes('Node / QuicSession', [ + { + children: [ + { node_name: 'QuicServerSession', edge_name: 'wrapped' }, + { node_name: 'Node / QuicCryptoContext', + edge_name: 'crypto_context' }, + { node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' }, + { node_name: 'Node / HistogramBase', + edge_name: 'crypto_handshake_rate' }, + { node_name: 'Node / Timer', edge_name: 'retransmit' }, + { node_name: 'Node / Timer', edge_name: 'idle' }, + { node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' }, + { node_name: 'Node / QuicBuffer', edge_name: 'txbuf' }, + { node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' }, + { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, + { node_name: 'Node / current_ngtcp2_memory', + edge_name: 'current_ngtcp2_memory' }, + { node_name: 'Node / streams', edge_name: 'streams' }, + { node_name: 'Node / std::basic_string', edge_name: 'alpn' }, + { node_name: 'Node / std::basic_string', edge_name: 'hostname' }, + { node_name: 'Float64Array', edge_name: 'state' }, + ] + }, + { + children: [ + { node_name: 'QuicClientSession', edge_name: 'wrapped' }, + { node_name: 'Node / QuicCryptoContext', + edge_name: 'crypto_context' }, + { node_name: 'Node / HistogramBase', edge_name: 'crypto_rx_ack' }, + { node_name: 'Node / HistogramBase', + edge_name: 'crypto_handshake_rate' }, + { node_name: 'Node / Timer', edge_name: 'retransmit' }, + { node_name: 'Node / Timer', edge_name: 'idle' }, + { node_name: 'Node / QuicBuffer', edge_name: 'sendbuf' }, + { node_name: 'Node / QuicBuffer', edge_name: 'txbuf' }, + { node_name: 'Float64Array', edge_name: 'recovery_stats_buffer' }, + { node_name: 'BigUint64Array', edge_name: 'stats_buffer' }, + { node_name: 'Node / current_ngtcp2_memory', + edge_name: 'current_ngtcp2_memory' }, + { node_name: 'Node / streams', edge_name: 'streams' }, + { node_name: 'Node / std::basic_string', edge_name: 'alpn' }, + { node_name: 'Node / std::basic_string', edge_name: 'hostname' }, + { node_name: 'Float64Array', edge_name: 'state' }, + ] + } + ], { loose: true }); + + state.validateSnapshotNodes('Node / QuicCryptoContext', [ + { + children: [ + { node_name: 'Node / rx_secret', edge_name: 'rx_secret' }, + { node_name: 'Node / tx_secret', edge_name: 'tx_secret' }, + { node_name: 'Node / QuicBuffer', edge_name: 'initial_crypto' }, + { node_name: 'Node / QuicBuffer', + edge_name: 'handshake_crypto' }, + { node_name: 'Node / QuicBuffer', edge_name: 'app_crypto' }, + ] + } + ], { loose: true }); + + session.destroy(); + server.close(); + })); +})); + +server.on('ready', common.mustCall(() => { + const client = quic.createSocket({ + port: 0, + client: { + key, + cert, + ca, + alpn: 'meow' + } + }); + + client.connect({ + address: 'localhost', + port: server.address.port + }).on('close', common.mustCall(() => client.close())); +})); diff --git a/test/sequential/test-async-wrap-getasyncid.js b/test/sequential/test-async-wrap-getasyncid.js index ab1f2110a3debc..17eb781e5d4b97 100644 --- a/test/sequential/test-async-wrap-getasyncid.js +++ b/test/sequential/test-async-wrap-getasyncid.js @@ -45,6 +45,13 @@ const { getSystemErrorName } = require('util'); delete providers.STREAMPIPE; delete providers.MESSAGEPORT; delete providers.WORKER; + // TODO(danbev): Test for these + delete providers.QUICCLIENTSESSION; + delete providers.QUICSERVERSESSION; + delete providers.QUICSENDWRAP; + delete providers.QUICSOCKET; + delete providers.QUICSTREAM; + delete providers.JSUDPWRAP; if (!common.isMainThread) delete providers.INSPECTORJSBINDING; delete providers.KEYPAIRGENREQUEST; diff --git a/test/sequential/test-quic-preferred-address-ipv6.js b/test/sequential/test-quic-preferred-address-ipv6.js new file mode 100644 index 00000000000000..bfad714c6e9684 --- /dev/null +++ b/test/sequential/test-quic-preferred-address-ipv6.js @@ -0,0 +1,94 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const key = fixtures.readKey('agent1-key.pem', 'binary'); +const cert = fixtures.readKey('agent1-cert.pem', 'binary'); +const ca = fixtures.readKey('ca1-cert.pem', 'binary'); +const { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +let client; + +const server = createSocket({ endpoint: { type: 'udp6' } }); + +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN, preferredAddress: { + port: common.PORT, + address: '::', + type: 'udp6', +} }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + session.on('stream', common.mustCall((stream) => { + stream.end('hello world'); + stream.resume(); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + })); +})); + +server.on('ready', common.mustCall(() => { + const endpoints = server.endpoints; + for (const endpoint of endpoints) { + const address = endpoint.address; + debug('Server is listening on address %s:%d', + address.address, + address.port); + } + const endpoint = endpoints[0]; + + client = createSocket({ endpoint: { type: 'udp6' }, client: { + key, + cert, + ca, + alpn: kALPN, + preferredAddressPolicy: 'accept' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + type: 'udp6', + }); + + req.on('ready', common.mustCall(() => { + req.on('usePreferredAddress', common.mustCall(({ address, port, type }) => { + assert.strictEqual(address, '::'); + assert.strictEqual(port, common.PORT); + assert.strictEqual(type, 'udp6'); + })); + })); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + stream.end('hello world'); + stream.resume(); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/test/sequential/test-quic-preferred-address.js b/test/sequential/test-quic-preferred-address.js new file mode 100644 index 00000000000000..b030494a34e534 --- /dev/null +++ b/test/sequential/test-quic-preferred-address.js @@ -0,0 +1,93 @@ +// Flags: --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +if (!common.hasQuic) + common.skip('missing quic'); + +const Countdown = require('../common/countdown'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const key = fixtures.readKey('agent1-key.pem', 'binary'); +const cert = fixtures.readKey('agent1-cert.pem', 'binary'); +const ca = fixtures.readKey('ca1-cert.pem', 'binary'); +const { debuglog } = require('util'); +const debug = debuglog('test'); + +const { createSocket } = require('quic'); + +let client; + +const server = createSocket(); + +const kALPN = 'zzz'; // ALPN can be overriden to whatever we want + +const countdown = new Countdown(1, () => { + debug('Countdown expired. Destroying sockets'); + server.close(); + client.close(); +}); + +server.listen({ key, cert, ca, alpn: kALPN, preferredAddress: { + port: common.PORT, + address: '0.0.0.0', + type: 'udp4', +} }); + +server.on('session', common.mustCall((session) => { + debug('QuicServerSession Created'); + session.on('stream', common.mustCall((stream) => { + stream.end('hello world'); + stream.resume(); + stream.on('close', common.mustCall()); + stream.on('finish', common.mustCall()); + })); +})); + +server.on('ready', common.mustCall(() => { + const endpoints = server.endpoints; + for (const endpoint of endpoints) { + const address = endpoint.address; + debug('Server is listening on address %s:%d', + address.address, + address.port); + } + const endpoint = endpoints[0]; + + client = createSocket({ client: { + key, + cert, + ca, + alpn: kALPN, + preferredAddressPolicy: 'accept' } }); + + client.on('close', common.mustCall()); + + const req = client.connect({ + address: 'localhost', + port: endpoint.address.port, + servername: 'localhost', + }); + + req.on('ready', common.mustCall(() => { + req.on('usePreferredAddress', common.mustCall(({ address, port, type }) => { + assert.strictEqual(address, '0.0.0.0'); + assert.strictEqual(port, common.PORT); + assert.strictEqual(type, 'udp4'); + })); + })); + + req.on('secure', common.mustCall((servername, alpn, cipher) => { + const stream = req.openStream(); + stream.end('hello world'); + stream.resume(); + + stream.on('close', common.mustCall(() => { + countdown.dec(); + })); + })); + + req.on('close', common.mustCall()); +})); + +server.on('listening', common.mustCall()); diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 02b59d37ffd278..cb117e42c7b2a8 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -15,10 +15,29 @@ const jsPrimitives = { const jsGlobalObjectsUrl = `${jsDocPrefix}Reference/Global_Objects/`; const jsGlobalTypes = [ - 'Array', 'ArrayBuffer', 'ArrayBufferView', 'DataView', 'Date', 'Error', - 'EvalError', 'Function', 'Map', 'Object', 'Promise', 'RangeError', - 'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', - 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', 'WebAssembly.Instance', + 'Array', + 'ArrayBuffer', + 'ArrayBufferView', + 'BigInt', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Function', + 'Map', + 'Object', + 'Promise', + 'RangeError', + 'ReferenceError', + 'RegExp', + 'Set', + 'SharedArrayBuffer', + 'SyntaxError', + 'TypeError', + 'TypedArray', + 'URIError', + 'Uint8Array', + 'WebAssembly.Instance', ]; const customTypesMap = { @@ -123,6 +142,10 @@ const customTypesMap = { 'perf_hooks.html#perf_hooks_class_performanceobserver', 'PerformanceObserverEntryList': 'perf_hooks.html#perf_hooks_class_performanceobserverentrylist', + 'QuicEndpoint': 'quic.html#quic_class_quicendpoint', + 'QuicSession': 'quic.html#quic_class_quicserversession_extends_quicsession', + 'QuicSocket': 'quic.html#quic_quic_createsocket_options', + 'QuicStream': 'quic.html#quic_class_quicstream_extends_stream_duplex', 'readline.Interface': 'readline.html#readline_class_interface', diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 51c5b4d0c6a66d..d27f018e91c328 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -88,6 +88,12 @@ addlicense "gtest" "test/cctest/gtest" "$(cat ${rootdir}/test/cctest/gtest/LICEN # nghttp2 addlicense "nghttp2" "deps/nghttp2" "$(cat ${rootdir}/deps/nghttp2/COPYING)" +# ngtcp2 +addlicense "ngtcp2" "deps/ngtcp2" "$(cat ${rootdir}/deps/ngtcp2/COPYING)" + +# nghttp3 +addlicense "nghttp3" "deps/nghttp3" "$(cat ${rootdir}/deps/nghttp3/COPYING)" + # node-inspect addlicense "node-inspect" "deps/node-inspect" "$(cat ${rootdir}/deps/node-inspect/LICENSE)" diff --git a/vcbuild.bat b/vcbuild.bat index 561eb535f2965e..dec19a86cc07a1 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -68,6 +68,7 @@ set openssl_no_asm= set doc= set extra_msbuild_args= set exit_code=0 +set experimental_quic= :next-arg if "%1"=="" goto args-done @@ -144,6 +145,7 @@ if /i "%1"=="cctest" set cctest=1&goto arg-ok if /i "%1"=="openssl-no-asm" set openssl_no_asm=1&goto arg-ok if /i "%1"=="doc" set doc=1&goto arg-ok if /i "%1"=="binlog" set extra_msbuild_args=/binaryLogger:%config%\node.binlog&goto arg-ok +if /i "%1"=="experimental-quic" set experimental_quic=1&goto arg-ok echo Error: invalid command line option `%1`. exit /b 1 @@ -196,6 +198,7 @@ if defined config_flags set configure_flags=%configure_flags% %config_flags% if defined target_arch set configure_flags=%configure_flags% --dest-cpu=%target_arch% if defined openssl_no_asm set configure_flags=%configure_flags% --openssl-no-asm if defined DEBUG_HELPER set configure_flags=%configure_flags% --verbose +if defined experimental_quic set configure_flags=%configure_flags% --experimental-quic if "%target_arch%"=="x86" if "%PROCESSOR_ARCHITECTURE%"=="AMD64" set configure_flags=%configure_flags% --no-cross-compiling if not exist "%~dp0deps\icu" goto no-depsicu @@ -703,7 +706,7 @@ del .used_configure_flags goto exit :help -echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-js-native-api/test-node-api/test-benchmark/test-internet/test-pummel/test-simple/test-message/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-async-hooks/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [noetw] [ltcg] [licensetf] [sign] [ia32/x86/x64/arm64] [vs2017/vs2019] [download-all] [lint/lint-ci/lint-js/lint-js-ci/lint-md] [lint-md-build] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] +echo vcbuild.bat [debug/release] [msi] [doc] [test/test-all/test-addons/test-js-native-api/test-node-api/test-benchmark/test-internet/test-pummel/test-simple/test-message/test-tick-processor/test-known-issues/test-node-inspect/test-check-deopts/test-npm/test-async-hooks/test-v8/test-v8-intl/test-v8-benchmarks/test-v8-all] [ignore-flaky] [static/dll] [noprojgen] [projgen] [small-icu/full-icu/without-intl] [nobuild] [nosnapshot] [noetw] [ltcg] [licensetf] [sign] [ia32/x86/x64/arm64] [vs2017/vs2019] [download-all] [lint/lint-ci/lint-js/lint-js-ci/lint-md] [lint-md-build] [package] [build-release] [upload] [no-NODE-OPTIONS] [link-module path-to-module] [debug-http2] [debug-nghttp2] [clean] [cctest] [no-cctest] [openssl-no-asm] [experimental-quic] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build From e7b25da6a8ce654699896c14026e53b8717b6470 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 12 Mar 2020 15:29:02 -0700 Subject: [PATCH 5/6] quic: fixup test-fs-chown PR-URL: https://github.com/nodejs/quic/pull/360 Reviewed-By: Anna Henningsen --- lib/fs.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/fs.js b/lib/fs.js index 52b7bdd810e635..3b1e19963e7943 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1154,8 +1154,8 @@ function chmodSync(path, mode) { function lchown(path, uid, gid, callback) { callback = makeCallback(callback); path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = callback; binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req); @@ -1163,8 +1163,8 @@ function lchown(path, uid, gid, callback) { function lchownSync(path, uid, gid) { path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = { path }; binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); @@ -1172,8 +1172,8 @@ function lchownSync(path, uid, gid) { function fchown(fd, uid, gid, callback) { validateInt32(fd, 'fd', 0); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = makeCallback(callback); @@ -1182,8 +1182,8 @@ function fchown(fd, uid, gid, callback) { function fchownSync(fd, uid, gid) { validateInt32(fd, 'fd', 0); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = {}; binding.fchown(fd, uid, gid, undefined, ctx); @@ -1193,8 +1193,8 @@ function fchownSync(fd, uid, gid) { function chown(path, uid, gid, callback) { callback = makeCallback(callback); path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const req = new FSReqCallback(); req.oncomplete = callback; @@ -1203,8 +1203,8 @@ function chown(path, uid, gid, callback) { function chownSync(path, uid, gid) { path = getValidatedPath(path); - validateInteger(uid, 'uid', -1, kMaxUserId); - validateInteger(gid, 'gid', -1, kMaxUserId); + validateInteger(uid, 'uid', { min: -1, max: kMaxUserId }); + validateInteger(gid, 'gid', { min: -1, max: kMaxUserId }); const ctx = { path }; binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); handleErrorFromBinding(ctx); From 928c50e4ea9641d95b51a37a977486ecf9cc3a1d Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 12 Mar 2020 15:08:08 -0700 Subject: [PATCH 6/6] quic: fixup broken test when quic unavailable PR-URL: https://github.com/nodejs/quic/pull/359 Reviewed-By: Anna Henningsen --- doc/api/errors.md | 7 +++++++ lib/internal/errors.js | 1 + lib/internal/quic/core.js | 2 ++ lib/internal/quic/util.js | 3 +++ lib/quic.js | 11 +++++++++++ src/node_native_module.cc | 13 +++++-------- 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index fe04156e57f6b1..d3a4046772f912 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1695,6 +1695,13 @@ TBD TBD + +### `ERR_QUIC_UNAVAILABLE` + +> Stabililty: 1 - Experimental + +TBD + ### `ERR_QUICCLIENTSESSION_FAILED` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 4475a26bafabfb..bd7804203898b6 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1308,6 +1308,7 @@ E('ERR_QUIC_ERROR', function(code, family) { return `QUIC session closed with ${familyType} error code ${code}`; }, Error); E('ERR_QUIC_TLS13_REQUIRED', 'QUIC requires TLS version 1.3', Error); +E('ERR_QUIC_UNAVAILABLE', 'QUIC is unavailable', Error); E('ERR_REQUIRE_ESM', (filename, parentPath = null, packageJsonPath = null) => { let msg = `Must use import to load ES Module: ${filename}`; diff --git a/lib/internal/quic/core.js b/lib/internal/quic/core.js index 18f60820c596fb..e906fb6b23323c 100644 --- a/lib/internal/quic/core.js +++ b/lib/internal/quic/core.js @@ -245,6 +245,8 @@ const kSocketDestroyed = 4; let diagnosticPacketLossWarned = false; let warnedVerifyHostnameIdentity = false; +assert(process.versions.ngtcp2 !== undefined); + // Called by the C++ internals when the socket is closed. // When this is called, the only thing left to do is destroy // the QuicSocket instance. diff --git a/lib/internal/quic/util.js b/lib/internal/quic/util.js index 28119b8e2628ff..d6e5dc10697622 100644 --- a/lib/internal/quic/util.js +++ b/lib/internal/quic/util.js @@ -9,6 +9,9 @@ const { }, } = require('internal/errors'); +const assert = require('internal/assert'); +assert(process.versions.ngtcp2 !== undefined); + const { isIP, } = require('internal/net'); diff --git a/lib/quic.js b/lib/quic.js index ca96385abf026c..4a9f05665da9a4 100644 --- a/lib/quic.js +++ b/lib/quic.js @@ -1,5 +1,16 @@ 'use strict'; +const { + codes: { + ERR_QUIC_UNAVAILABLE, + }, +} = require('internal/errors'); +const { assertCrypto } = require('internal/util'); + +assertCrypto(); +if (process.versions.ngtcp2 === undefined) + throw new ERR_QUIC_UNAVAILABLE(); + const { createSocket } = require('internal/quic/core'); diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 4ffc666600738d..786b9cc1546d62 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -87,23 +87,20 @@ void NativeModuleLoader::InitializeModuleCategories() { "crypto", "https", "http2", -#if !defined(NODE_EXPERIMENTAL_QUIC) - "quic", -#endif "tls", "_tls_common", "_tls_wrap", "internal/http2/core", "internal/http2/compat", -#if !defined(NODE_EXPERIMENTAL_QUIC) - "internal/quic/core", - "internal/quic/util", -#endif "internal/policy/manifest", "internal/process/policy", "internal/streams/lazy_transform", #endif // !HAVE_OPENSSL - +#if !defined(NODE_EXPERIMENTAL_QUIC) + "quic", + "internal/quic/core", + "internal/quic/util", +#endif "sys", // Deprecated. "wasi", // Experimental. "internal/test/binding",