From 3f1b2ffe0e01cbdf680355e008940b7a650d6a46 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 27 Jul 2025 09:02:52 -0700 Subject: [PATCH 1/4] quic: start re-enabling quic with openssl 3.5 Start working on re-enabling QUIC support with the availability of OpenSSL 3.5. This will be a multi-step process. Signed-off-by: James M Snell --- deps/ngtcp2/ngtcp2.gyp | 2 +- node.gyp | 10 +- node.gypi | 2 + src/quic/cid.cc | 23 ++-- src/quic/cid.h | 19 +-- src/quic/data.cc | 189 +++++++++++++++++--------- src/quic/data.h | 233 +++++++++++++++++++++++++++------ src/quic/defs.h | 3 +- src/quic/guard.h | 13 ++ src/quic/quic.cc | 2 - test/cctest/test_quic_cid.cc | 68 ++++++---- test/cctest/test_quic_error.cc | 47 ++++--- 12 files changed, 435 insertions(+), 176 deletions(-) create mode 100644 src/quic/guard.h diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index ca64d16778315d..5000f1f9fa676c 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -48,7 +48,7 @@ 'ngtcp2/crypto/shared.c' ], 'ngtcp2_sources_quictls': [ - 'ngtcp2/crypto/quictls/quictls.c' + #'ngtcp2/crypto/quictls/quictls.c' ], 'ngtcp2_sources_boringssl': [ 'ngtcp2/crypto/boringssl/boringssl.c' diff --git a/node.gyp b/node.gyp index 4437a34f9cf67b..26150fa5c5ec03 100644 --- a/node.gyp +++ b/node.gyp @@ -187,6 +187,8 @@ 'src/udp_wrap.cc', 'src/util.cc', 'src/uv.cc', + 'src/quic/cid.cc', + 'src/quic/data.cc', # headers to make for a more pleasant IDE experience 'src/aliased_buffer.h', 'src/aliased_buffer-inl.h', @@ -323,6 +325,10 @@ 'src/udp_wrap.h', 'src/util.h', 'src/util-inl.h', + 'src/quic/cid.h', + 'src/quic/data.h', + 'src/quic/defs.h', + 'src/quic/guard.h', ], 'node_crypto_sources': [ 'src/crypto/crypto_aes.cc', @@ -379,8 +385,6 @@ 'node_quic_sources': [ 'src/quic/application.cc', 'src/quic/bindingdata.cc', - 'src/quic/cid.cc', - 'src/quic/data.cc', 'src/quic/endpoint.cc', 'src/quic/http3.cc', 'src/quic/logstream.cc', @@ -394,8 +398,6 @@ 'src/quic/transportparams.cc', 'src/quic/application.h', 'src/quic/bindingdata.h', - 'src/quic/cid.h', - 'src/quic/data.h', 'src/quic/endpoint.h', 'src/quic/http3.h', 'src/quic/logstream.h', diff --git a/node.gypi b/node.gypi index a851fbbcb810bb..5ce22a3102c82c 100644 --- a/node.gypi +++ b/node.gypi @@ -380,6 +380,8 @@ 'defines': [ 'OPENSSL_API_COMPAT=0x10100000L', ], 'dependencies': [ './deps/openssl/openssl.gyp:openssl', + './deps/ngtcp2/ngtcp2.gyp:ngtcp2', + './deps/ngtcp2/ngtcp2.gyp:nghttp3', # For tests './deps/openssl/openssl.gyp:openssl-cli', diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 1b5fdd861b7a9a..2f72d24c892f43 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -1,12 +1,14 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC -#include "cid.h" +#if HAVE_OPENSSL +#include "guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include #include +#include "cid.h" +#include "defs.h" #include "nbytes.h" #include "ncrypto.h" -#include "quic/defs.h" namespace node::quic { @@ -77,7 +79,7 @@ std::string CID::ToString() const { return std::string(dest, written); } -CID CID::kInvalid{}; +const CID CID::kInvalid{}; // ============================================================================ // CID::Hash @@ -95,12 +97,12 @@ size_t CID::Hash::operator()(const CID& cid) const { // CID::Factory namespace { -class RandomCIDFactory : public CID::Factory { +class RandomCIDFactory final : public CID::Factory { public: RandomCIDFactory() = default; DISALLOW_COPY_AND_MOVE(RandomCIDFactory) - CID Generate(size_t length_hint) const override { + const CID Generate(size_t length_hint) const override { DCHECK_GE(length_hint, CID::kMinLength); DCHECK_LE(length_hint, CID::kMaxLength); Mutex::ScopedLock lock(mutex_); @@ -110,8 +112,8 @@ class RandomCIDFactory : public CID::Factory { return CID(start, length_hint); } - CID GenerateInto(ngtcp2_cid* cid, - size_t length_hint = CID::kMaxLength) const override { + const CID GenerateInto(ngtcp2_cid* cid, + size_t length_hint = CID::kMaxLength) const override { DCHECK_GE(length_hint, CID::kMinLength); DCHECK_LE(length_hint, CID::kMaxLength); Mutex::ScopedLock lock(mutex_); @@ -135,7 +137,7 @@ class RandomCIDFactory : public CID::Factory { } } - static constexpr int kPoolSize = 4096; + static constexpr int kPoolSize = 1024 * 16; mutable int pos_ = kPoolSize; mutable uint8_t pool_[kPoolSize]; mutable Mutex mutex_; @@ -148,4 +150,5 @@ const CID::Factory& CID::Factory::random() { } } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSS diff --git a/src/quic/cid.h b/src/quic/cid.h index 15aa6d90748007..16883e50f1637a 100644 --- a/src/quic/cid.h +++ b/src/quic/cid.h @@ -1,7 +1,7 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC + #include #include #include @@ -51,7 +51,7 @@ class CID final : public MemoryRetainer { CID(const CID& other); CID& operator=(const CID& other); - CID(CID&&) = delete; + DISALLOW_MOVE(CID) struct Hash final { size_t operator()(const CID& cid) const; @@ -68,6 +68,8 @@ class CID final : public MemoryRetainer { operator bool() const; size_t length() const; + // Returns a hex-encoded string representation of the CID useful + // for debugging. std::string ToString() const; SET_NO_MEMORY_INFO() @@ -75,7 +77,7 @@ class CID final : public MemoryRetainer { SET_SELF_SIZE(CID) template - using Map = std::unordered_map; + using Map = std::unordered_map; // A CID::Factory, as the name suggests, is used to create new CIDs. // Per https://datatracker.ietf.org/doc/draft-ietf-quic-load-balancers/, QUIC @@ -85,13 +87,13 @@ class CID final : public MemoryRetainer { // but will allow user code to provide their own CID::Factory implementation. class Factory; - static CID kInvalid; + static const CID kInvalid; // The default constructor creates an empty, zero-length CID. // Zero-length CIDs are not usable. We use them as a placeholder // for a missing or empty CID value. This is public only because // it is required for the CID::Map implementation. It should not - // be used. Use kInvalid instead. + // be used directly. Use kInvalid instead. CID(); private: @@ -107,12 +109,12 @@ class CID::Factory { // Generate a new CID. The length_hint must be between CID::kMinLength // and CID::kMaxLength. The implementation can choose to ignore the length. - virtual CID Generate(size_t length_hint = CID::kMaxLength) const = 0; + virtual const CID Generate(size_t length_hint = CID::kMaxLength) const = 0; // Generate a new CID into the given ngtcp2_cid. This variation of // Generate should be used far less commonly. - virtual CID GenerateInto(ngtcp2_cid* cid, - size_t length_hint = CID::kMaxLength) const = 0; + virtual const CID GenerateInto( + ngtcp2_cid* cid, size_t length_hint = CID::kMaxLength) const = 0; // The default random CID generator instance. static const Factory& random(); @@ -123,5 +125,4 @@ class CID::Factory { } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/quic/data.cc b/src/quic/data.cc index 699ddf4b210e8b..d54e4c6af2236c 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -1,5 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC - +#if HAVE_OPENSSL +#include "guard.h" +#ifndef OPENSSL_NO_QUIC #include "data.h" #include #include @@ -13,15 +14,21 @@ namespace node { using v8::Array; +using v8::ArrayBuffer; +using v8::ArrayBufferView; +using v8::BackingStore; using v8::BigInt; -using v8::Integer; +using v8::Just; using v8::Local; +using v8::Maybe; using v8::MaybeLocal; +using v8::Nothing; using v8::Uint8Array; using v8::Undefined; using v8::Value; namespace quic { +int DebugIndentScope::indent_ = 0; Path::Path(const SocketAddress& local, const SocketAddress& remote) { ngtcp2_addr_init(&this->local, local.data(), local.length()); @@ -50,9 +57,6 @@ std::string Path::ToString() const { PathStorage::PathStorage() { Reset(); } -PathStorage::operator ngtcp2_path() { - return path; -} void PathStorage::Reset() { ngtcp2_path_storage_zero(this); @@ -72,44 +76,54 @@ bool PathStorage::operator!=(const PathStorage& other) const { // ============================================================================ -Store::Store(std::shared_ptr store, - size_t length, - size_t offset) +Store::Store(std::shared_ptr store, size_t length, size_t offset) : store_(std::move(store)), length_(length), offset_(offset) { CHECK_LE(offset_, store_->ByteLength()); CHECK_LE(length_, store_->ByteLength() - offset_); } -Store::Store(std::unique_ptr store, - size_t length, - size_t offset) +Store::Store(std::unique_ptr store, size_t length, size_t offset) : store_(std::move(store)), length_(length), offset_(offset) { CHECK_LE(offset_, store_->ByteLength()); CHECK_LE(length_, store_->ByteLength() - offset_); } -Store::Store(Local buffer, Option option) - : Store(buffer->GetBackingStore(), buffer->ByteLength()) { - if (option == Option::DETACH) { - USE(buffer->Detach(Local())); +Maybe Store::From( + Local buffer, + Local detach_key) { + if (!buffer->IsDetachable()) { + return Nothing(); + } + bool res; + auto backing = buffer->GetBackingStore(); + auto length = buffer->ByteLength(); + if (!buffer->Detach(detach_key).To(&res) || !res) { + return Nothing(); } + return Just(Store(std::move(backing), length, 0)); } -Store::Store(Local view, Option option) - : Store(view->Buffer()->GetBackingStore(), - view->ByteLength(), - view->ByteOffset()) { - if (option == Option::DETACH) { - USE(view->Buffer()->Detach(Local())); +Maybe Store::From( + Local view, + Local detach_key) { + if (!view->Buffer()->IsDetachable()) { + return Nothing(); } + bool res; + auto backing = view->Buffer()->GetBackingStore(); + auto length = view->ByteLength(); + auto offset = view->ByteOffset(); + if (!view->Buffer()->Detach(detach_key).To(&res) || !res) { + return Nothing(); + } + return Just(Store(std::move(backing), length, offset)); } Local Store::ToUint8Array(Environment* env) const { return !store_ - ? Uint8Array::New(v8::ArrayBuffer::New(env->isolate(), 0), 0, 0) - : Uint8Array::New(v8::ArrayBuffer::New(env->isolate(), store_), - offset_, - length_); + ? Uint8Array::New(ArrayBuffer::New(env->isolate(), 0), 0, 0) + : Uint8Array::New( + ArrayBuffer::New(env->isolate(), store_), offset_, length_); } Store::operator bool() const { @@ -119,25 +133,31 @@ size_t Store::length() const { return length_; } -template +size_t Store::total_length() const { + return store_ ? store_->ByteLength() : 0; +} + +template T Store::convert() const { + // We can only safely convert to T if we have a valid store. + CHECK(store_); T buf; buf.base = - store_ != nullptr ? static_cast(store_->Data()) + offset_ : nullptr; + store_ != nullptr ? static_cast(store_->Data()) + offset_ : nullptr; buf.len = length_; return buf; } Store::operator uv_buf_t() const { - return convert(); + return convert(); } Store::operator ngtcp2_vec() const { - return convert(); + return convert(); } Store::operator nghttp3_vec() const { - return convert(); + return convert(); } void Store::MemoryInfo(MemoryTracker* tracker) const { @@ -147,18 +167,23 @@ void Store::MemoryInfo(MemoryTracker* tracker) const { // ============================================================================ namespace { -std::string TypeName(QuicError::Type type) { +constexpr std::string_view TypeName(QuicError::Type type) { switch (type) { case QuicError::Type::APPLICATION: - return "APPLICATION"; + return "application"; case QuicError::Type::TRANSPORT: - return "TRANSPORT"; + return "transport"; case QuicError::Type::VERSION_NEGOTIATION: - return "VERSION_NEGOTIATION"; + return "version_negotiation"; case QuicError::Type::IDLE_CLOSE: - return "IDLE_CLOSE"; + return "idle_close"; + case QuicError::Type::DROP_CONNECTION: + return "drop_connection"; + case QuicError::Type::RETRY: + return "retry"; + default: + return ""; } - UNREACHABLE(); } } // namespace @@ -167,6 +192,8 @@ QuicError::QuicError(const std::string& reason) ngtcp2_ccerr_default(&error_); } +// Keep in mind that reason_ in each of the constructors here will copy +// the string from the ngtcp2_ccerr input. QuicError::QuicError(const ngtcp2_ccerr* ptr) : reason_(reinterpret_cast(ptr->reason), ptr->reasonlen), error_(), @@ -177,14 +204,6 @@ QuicError::QuicError(const ngtcp2_ccerr& error) error_(error), ptr_(&error_) {} -QuicError::operator bool() const { - if ((code() == QUIC_NO_ERROR && type() == Type::TRANSPORT) || - ((code() == QUIC_APP_NO_ERROR && type() == Type::APPLICATION))) { - return false; - } - return true; -} - const uint8_t* QuicError::reason_c_str() const { return reinterpret_cast(reason_.c_str()); } @@ -247,31 +266,45 @@ error_code QuicError::h3_liberr_to_code(int liberr) { return nghttp3_err_infer_quic_app_error_code(liberr); } -bool QuicError::is_crypto() const { +bool QuicError::is_crypto_error() const { return code() & NGTCP2_CRYPTO_ERROR; } -std::optional QuicError::crypto_error() const { - if (!is_crypto()) return std::nullopt; +std::optional QuicError::get_crypto_error() const { + if (!is_crypto_error()) return std::nullopt; return code() & ~NGTCP2_CRYPTO_ERROR; } MaybeLocal QuicError::ToV8Value(Environment* env) const { if ((type() == Type::TRANSPORT && code() == NGTCP2_NO_ERROR) || - (type() == Type::APPLICATION && code() == NGTCP2_APP_NOERROR) || (type() == Type::APPLICATION && code() == NGHTTP3_H3_NO_ERROR)) { + // Note that we only return undefined for *known* no-error application + // codes. It is possible that other application types use other specific + // no-error codes, but since we don't know which application is being used, + // we'll just return the error code value for those below. return Undefined(env->isolate()); } + Local type_str; + if (!node::ToV8Value(env->context(), TypeName(type())).ToLocal(&type_str)) { + return {}; + } + Local argv[] = { - Integer::New(env->isolate(), static_cast(type())), + type_str, BigInt::NewFromUnsigned(env->isolate(), code()), Undefined(env->isolate()), }; + // Note that per the QUIC specification, the reason, if present, is + // expected to be UTF-8 encoded. The spec uses the term "SHOULD" here, + // which means that is is entirely possible that some QUIC implementation + // could choose a different encoding, in which case the conversion here + // will produce garbage. That's ok though, we're going to use the default + // assumption that the impl is following the guidelines. if (reason_.length() > 0 && !node::ToV8Value(env->context(), reason()).ToLocal(&argv[2])) { - return MaybeLocal(); + return {}; } return Array::New(env->isolate(), argv, arraysize(argv)).As(); @@ -279,7 +312,8 @@ MaybeLocal QuicError::ToV8Value(Environment* env) const { std::string QuicError::ToString() const { std::string str = "QuicError("; - str += TypeName(type()) + ") "; + str += TypeName(type()); + str += ") "; str += std::to_string(code()); if (!reason_.empty()) str += ": " + reason_; return str; @@ -289,53 +323,78 @@ void QuicError::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("reason", reason_.length()); } -QuicError QuicError::ForTransport(error_code code, std::string reason) { +const QuicError QuicError::ForTransport(TransportError code, + std::string reason) { + return ForTransport(static_cast(code), std::move(reason)); +} + +const QuicError QuicError::ForTransport(error_code code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_transport_error( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForApplication(error_code code, std::string reason) { +const QuicError QuicError::ForApplication(Http3Error code, std::string reason) { + return ForApplication(static_cast(code), std::move(reason)); +} + +const QuicError QuicError::ForApplication(error_code code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_application_error( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForVersionNegotiation(std::string reason) { +const QuicError QuicError::ForVersionNegotiation(std::string reason) { return ForNgtcp2Error(NGTCP2_ERR_RECV_VERSION_NEGOTIATION, std::move(reason)); } -QuicError QuicError::ForIdleClose(std::string reason) { +const QuicError QuicError::ForIdleClose(std::string reason) { return ForNgtcp2Error(NGTCP2_ERR_IDLE_CLOSE, std::move(reason)); } -QuicError QuicError::ForNgtcp2Error(int code, std::string reason) { +const QuicError QuicError::ForDropConnection(std::string reason) { + return ForNgtcp2Error(NGTCP2_ERR_DROP_CONN, std::move(reason)); +} + +const QuicError QuicError::ForRetry(std::string reason) { + return ForNgtcp2Error(NGTCP2_ERR_RETRY, std::move(reason)); +} + +const QuicError QuicError::ForNgtcp2Error(int code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_liberr( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::ForTlsAlert(int code, std::string reason) { +const QuicError QuicError::ForTlsAlert(int code, std::string reason) { QuicError error(std::move(reason)); ngtcp2_ccerr_set_tls_alert( &error.error_, code, error.reason_c_str(), error.reason().length()); return error; } -QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { +const QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { return QuicError(ngtcp2_conn_get_ccerr(session)); } -QuicError QuicError::TRANSPORT_NO_ERROR = ForTransport(QUIC_NO_ERROR); -QuicError QuicError::APPLICATION_NO_ERROR = ForApplication(QUIC_APP_NO_ERROR); -QuicError QuicError::VERSION_NEGOTIATION = ForVersionNegotiation(); -QuicError QuicError::IDLE_CLOSE = ForIdleClose(); -QuicError QuicError::INTERNAL_ERROR = ForNgtcp2Error(NGTCP2_ERR_INTERNAL); +#define V(name) \ + const QuicError QuicError::TRANSPORT_##name = \ + ForTransport(TransportError::name); +QUIC_TRANSPORT_ERRORS(V) +#undef V + +const QuicError QuicError::HTTP3_NO_ERROR = ForApplication(NGHTTP3_H3_NO_ERROR); +const QuicError QuicError::VERSION_NEGOTIATION = ForVersionNegotiation(); +const QuicError QuicError::IDLE_CLOSE = ForIdleClose(); +const QuicError QuicError::DROP_CONNECTION = ForDropConnection(); +const QuicError QuicError::RETRY = ForRetry(); +const QuicError QuicError::INTERNAL_ERROR = ForNgtcp2Error(NGTCP2_ERR_INTERNAL); } // namespace quic } // namespace node -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL diff --git a/src/quic/data.h b/src/quic/data.h index 7f97403d61f414..a63bc24b63eced 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -1,29 +1,37 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include #include #include #include #include +#include #include #include +#include #include #include "defs.h" namespace node::quic { +template +concept OneByteType = sizeof(T) == 1; + struct Path final : public ngtcp2_path { - Path(const SocketAddress& local, const SocketAddress& remote); - inline operator ngtcp2_path*() { return this; } + explicit Path(const SocketAddress& local, const SocketAddress& remote); + Path(Path&& other) noexcept = default; + Path& operator=(Path&& other) noexcept = default; + DISALLOW_COPY(Path) std::string ToString() const; }; struct PathStorage final : public ngtcp2_path_storage { - PathStorage(); - operator ngtcp2_path(); + explicit PathStorage(); + PathStorage(PathStorage&& other) noexcept = default; + PathStorage& operator=(PathStorage&& other) noexcept = default; + DISALLOW_COPY(PathStorage) void Reset(); void CopyTo(PathStorage* path) const; @@ -32,6 +40,9 @@ struct PathStorage final : public ngtcp2_path_storage { bool operator!=(const PathStorage& other) const; }; +// Store acts as a wrapper around a v8::BackingStore, providing a convenient +// abstraction to map it to various buffer types used in QUIC, HTTP/3, and +// libuv, taking care of the necessary adjustments for length and offset. class Store final : public MemoryRetainer { public: Store() = default; @@ -42,16 +53,30 @@ class Store final : public MemoryRetainer { Store(std::unique_ptr store, size_t length, size_t offset = 0); - - enum class Option { - NONE, - DETACH, - }; - - Store(v8::Local buffer, Option option = Option::NONE); - Store(v8::Local view, Option option = Option::NONE); + Store(Store&& other) noexcept = default; + Store& operator=(Store&& other) noexcept = default; + DISALLOW_COPY(Store) + + // Creates a Store from the contents of an ArrayBuffer, always detaching + // it in the process. An empty Maybe will be returned if the ArrayBuffer + // is not detachable or detaching failed (likely due to a detach key + // mismatch). + static v8::Maybe From( + v8::Local buffer, + v8::Local detach_key = v8::Local()); + + // Creates a Store from the contents of an ArrayBufferView, always detaching + // it in the process. An empty Maybe will be returned if the ArrayBuffer + // is not detachable or detaching failed (likely due to a detach key + // mismatch). + static v8::Maybe From( + v8::Local view, + v8::Local detach_key = v8::Local()); v8::Local ToUint8Array(Environment* env) const; + inline v8::Local ToUint8Array(Realm* realm) const { + return ToUint8Array(realm->env()); + } operator uv_buf_t() const; operator ngtcp2_vec() const; @@ -59,33 +84,124 @@ class Store final : public MemoryRetainer { operator bool() const; size_t length() const; + // Returns the total length of the underlying store, not just the + // length of the view. This is useful for memory accounting. + size_t total_length() const; + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(Store) SET_SELF_SIZE(Store) private: - template + template T convert() const; std::shared_ptr store_; size_t length_ = 0; size_t offset_ = 0; + + // Because Store holds the v8::BackingStore and not the v8::ArrayBuffer, + // etc, the memory held by the Store is not directly, automatically + // associated with a v8::Isolate, and is therefore not accounted for + // as external memory. It is the responsibility of the owner of the + // Store instance to ensure the memory remains accounted for. }; +// Periodically, these need to be updated to match the latest ngtcp2 defs. +#define QUIC_TRANSPORT_ERRORS(V) \ + V(NO_ERROR) \ + V(INTERNAL_ERROR) \ + V(CONNECTION_REFUSED) \ + V(FLOW_CONTROL_ERROR) \ + V(STREAM_LIMIT_ERROR) \ + V(STREAM_STATE_ERROR) \ + V(FINAL_SIZE_ERROR) \ + V(FRAME_ENCODING_ERROR) \ + V(TRANSPORT_PARAMETER_ERROR) \ + V(CONNECTION_ID_LIMIT_ERROR) \ + V(PROTOCOL_VIOLATION) \ + V(INVALID_TOKEN) \ + V(APPLICATION_ERROR) \ + V(CRYPTO_BUFFER_EXCEEDED) \ + V(KEY_UPDATE_ERROR) \ + V(AEAD_LIMIT_REACHED) \ + V(NO_VIABLE_PATH) \ + V(CRYPTO_ERROR) \ + V(VERSION_NEGOTIATION_ERROR) + +// Periodically, these need to be updated to match the latest nghttp3 defs. +#define HTTP3_APPLICATION_ERRORS(V) \ + V(H3_NO_ERROR) \ + V(H3_GENERAL_PROTOCOL_ERROR) \ + V(H3_INTERNAL_ERROR) \ + V(H3_STREAM_CREATION_ERROR) \ + V(H3_CLOSED_CRITICAL_STREAM) \ + V(H3_FRAME_UNEXPECTED) \ + V(H3_FRAME_ERROR) \ + V(H3_EXCESSIVE_LOAD) \ + V(H3_ID_ERROR) \ + V(H3_SETTINGS_ERROR) \ + V(H3_MISSING_SETTINGS) \ + V(H3_REQUEST_REJECTED) \ + V(H3_REQUEST_CANCELLED) \ + V(H3_REQUEST_INCOMPLETE) \ + V(H3_MESSAGE_ERROR) \ + V(H3_CONNECT_ERROR) \ + V(H3_VERSION_FALLBACK) \ + V(QPACK_DECOMPRESSION_FAILED) \ + V(QPACK_ENCODER_STREAM_ERROR) \ + V(QPACK_DECODER_STREAM_ERROR) + class QuicError final : public MemoryRetainer { public: - static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; - static constexpr error_code QUIC_APP_NO_ERROR = 65280; + // The known error codes for the transport namespace. + enum class TransportError : error_code { +#define V(name) name = NGTCP2_##name, + QUIC_TRANSPORT_ERRORS(V) +#undef V + }; + + // Every QUIC application defines its own error codes in the application + // namespace. These are managed independently of each other and may overlap + // with other applications and even the transport namespace. The only way + // to correctly interpret an application error code is to know which + // application is being used. For convenience, we define constants for the + // known HTTP/3 application error codes here. + enum class Http3Error : error_code { +#define V(name) name = NGHTTP3_##name, + HTTP3_APPLICATION_ERRORS(V) +#undef V + }; + static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; + static constexpr error_code HTTP3_NO_ERROR_CODE = NGHTTP3_H3_NO_ERROR; + + // In QUIC, Errors are represented as namespaced 64-bit error codes. + // The error code only has meaning within the context of a specific + // namespace. The QuicError::Type enum defines the available namespaces. + // There are essentially two namespaces: transport and application, with + // a few additional special-cases that are variants of the transport + // namespace. enum class Type { TRANSPORT = NGTCP2_CCERR_TYPE_TRANSPORT, APPLICATION = NGTCP2_CCERR_TYPE_APPLICATION, + + // These are special cases of transport errors. VERSION_NEGOTIATION = NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION, IDLE_CLOSE = NGTCP2_CCERR_TYPE_IDLE_CLOSE, + DROP_CONNECTION = NGTCP2_CCERR_TYPE_DROP_CONN, + RETRY = NGTCP2_CCERR_TYPE_RETRY, }; + // Do not use the constructors directly in regular use. Use the static + // factory methods instead. Those will ensure that the underlying + // ngtcp2_ccerr is initialized correctly based on the type of error + // being created. explicit QuicError(const std::string& reason = ""); explicit QuicError(const ngtcp2_ccerr* ptr); explicit QuicError(const ngtcp2_ccerr& error); + QuicError(QuicError&& other) noexcept = default; + QuicError& operator=(QuicError&& other) noexcept = default; + DISALLOW_COPY(QuicError) Type type() const; error_code code() const; @@ -95,13 +211,16 @@ class QuicError final : public MemoryRetainer { operator const ngtcp2_ccerr&() const; operator const ngtcp2_ccerr*() const; - // Returns false if the QuicError uses a no_error code with type - // transport or application. - operator bool() const; - - bool is_crypto() const; - std::optional crypto_error() const; + // Crypto errors are a subset of transport errors. The error code includes + // the TLS alert code embedded within it. + bool is_crypto_error() const; + std::optional get_crypto_error() const; + // Note that since application errors are application-specific and we + // don't know which application is being used here, it is possible that + // the comparing two different QuicError instances from different applications + // will return true even if they are not semantically equivalent. This should + // not be a problem in practice. bool operator==(const QuicError& other) const; bool operator!=(const QuicError& other) const; @@ -110,29 +229,59 @@ class QuicError final : public MemoryRetainer { SET_SELF_SIZE(QuicError) std::string ToString() const; + + // Returns an array containing [type, code, reason], where type is a string + // representation of the error type, code is a bigint representation of the + // error code, and reason is a string representation of the error reason, or + // undefined if the reason is the empty string. This is expected to be used + // to construct a JS error object from the information in JS. v8::MaybeLocal ToV8Value(Environment* env) const; + inline v8::MaybeLocal ToV8Value(Realm* realm) const { + return ToV8Value(realm->env()); + } + // Utility functions for getting the default error reason strings for + // internal error codes returned by the underlying ngtcp2/nghttp3 libraries. static std::string reason_for_liberr(int liberr); static std::string reason_for_h3_liberr(int liberr); + + // Utility functions for checking if the given internal error codes are + // considered to be fatal by the underlying ngtcp2/nghttp3 libraries. static bool is_fatal_liberror(int liberr); static bool is_fatal_h3_liberror(int liberr); + + // Utility functions for converting between ngtcp2/nghttp3 internal error + // codes to the corresponding QUIC error codes. static error_code liberr_to_code(int liberr); static error_code h3_liberr_to_code(int liberr); - static QuicError ForTransport(error_code code, std::string reason = ""); - static QuicError ForApplication(error_code code, std::string reason = ""); - static QuicError ForVersionNegotiation(std::string reason = ""); - static QuicError ForIdleClose(std::string reason = ""); - static QuicError ForNgtcp2Error(int code, std::string reason = ""); - static QuicError ForTlsAlert(int code, std::string reason = ""); - - static QuicError FromConnectionClose(ngtcp2_conn* session); - - static QuicError TRANSPORT_NO_ERROR; - static QuicError APPLICATION_NO_ERROR; - static QuicError VERSION_NEGOTIATION; - static QuicError IDLE_CLOSE; - static QuicError INTERNAL_ERROR; + // Utility functions for creating QuicError instances. + // The reason is expected to always be UTF-8 encoded. + static const QuicError ForTransport(TransportError code, + std::string reason = ""); + static const QuicError ForTransport(error_code code, std::string reason = ""); + static const QuicError ForApplication(Http3Error code, + std::string reason = ""); + static const QuicError ForApplication(error_code code, + std::string reason = ""); + static const QuicError ForVersionNegotiation(std::string reason = ""); + static const QuicError ForIdleClose(std::string reason = ""); + static const QuicError ForDropConnection(std::string reason = ""); + static const QuicError ForRetry(std::string reason = ""); + static const QuicError ForNgtcp2Error(int code, std::string reason = ""); + static const QuicError ForTlsAlert(int code, std::string reason = ""); + + static const QuicError FromConnectionClose(ngtcp2_conn* session); + +#define V(name) static const QuicError TRANSPORT_##name; + QUIC_TRANSPORT_ERRORS(V) +#undef V + static const QuicError HTTP3_NO_ERROR; + static const QuicError VERSION_NEGOTIATION; + static const QuicError IDLE_CLOSE; + static const QuicError DROP_CONNECTION; + static const QuicError RETRY; + static const QuicError INTERNAL_ERROR; private: const uint8_t* reason_c_str() const; @@ -142,7 +291,17 @@ class QuicError final : public MemoryRetainer { const ngtcp2_ccerr* ptr_ = nullptr; }; +// Marked maybe_unused because these are used in the tests but not in the +// production code. +[[maybe_unused]] static bool operator==(const QuicError::TransportError& lhs, + error_code rhs) { + return static_cast(lhs) == rhs; +} +[[maybe_unused]] static bool operator==(const QuicError::Http3Error& lhs, + error_code rhs) { + return static_cast(lhs) == rhs; +} + } // namespace node::quic -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/quic/defs.h b/src/quic/defs.h index 8c97d30d26f77f..71b9059698a6e8 100644 --- a/src/quic/defs.h +++ b/src/quic/defs.h @@ -221,7 +221,6 @@ enum class DatagramStatus : uint8_t { CC_ALGOS(V) #undef V -constexpr uint64_t NGTCP2_APP_NOERROR = 65280; constexpr size_t kDefaultMaxPacketLength = NGTCP2_MAX_UDP_PAYLOAD_SIZE; constexpr size_t kMaxSizeT = std::numeric_limits::max(); constexpr uint64_t kMaxSafeJsInteger = 9007199254740991; @@ -230,7 +229,7 @@ constexpr size_t kMaxVectorCount = 16; using error_code = uint64_t; -class DebugIndentScope { +class DebugIndentScope final { public: inline DebugIndentScope() { ++indent_; } DISALLOW_COPY_AND_MOVE(DebugIndentScope) diff --git a/src/quic/guard.h b/src/quic/guard.h new file mode 100644 index 00000000000000..d84694b2887fa7 --- /dev/null +++ b/src/quic/guard.h @@ -0,0 +1,13 @@ +#pragma once + +#if HAVE_OPENSSL +#include +// QUIC is only available in Openssl 3.5.x and later. It was not introduced in +// Node.js until 3.5.1... prior to that we will not compile any of the QUIC +// related code. +#if OPENSSL_VERSION_NUMBER < 0x30500010 +#define OPENSSL_NO_QUIC = 1 +#endif +#else +#define OPENSSL_NO_QUIC = 1 +#endif diff --git a/src/quic/quic.cc b/src/quic/quic.cc index f642a725263cef..cb972df2b6c0f8 100644 --- a/src/quic/quic.cc +++ b/src/quic/quic.cc @@ -20,8 +20,6 @@ using v8::Value; namespace quic { -int DebugIndentScope::indent_ = 0; - void CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Endpoint::InitPerIsolate(isolate_data, target); diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc index 6d8a19431f714f..50be7d0fb5348c 100644 --- a/test/cctest/test_quic_cid.cc +++ b/test/cctest/test_quic_cid.cc @@ -1,4 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#if HAVE_OPENSSL +#include "quic/guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include @@ -12,42 +14,51 @@ using node::quic::CID; TEST(CID, Basic) { auto& random = CID::Factory::random(); { - auto cid = random.Generate(); + // Check that the kInvalid CID is indeed invalid. + CHECK(!CID::kInvalid); + } + { + const auto cid = random.Generate(); CHECK_EQ(cid.length(), CID::kMaxLength); CHECK(cid); CHECK_EQ(cid, cid); } { - auto cid = random.Generate(5); + const auto cid = random.Generate(5); CHECK_EQ(cid.length(), 5); CHECK(cid); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(); CHECK_NE(cid1, cid2); } { - auto cid1 = random.Generate(5); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(5); + const auto cid2 = random.Generate(); CHECK_NE(cid1, cid2); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(5); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(5); CHECK_NE(cid1, cid2); } { - auto cid = CID::kInvalid; // They are copy constructible... - auto cid2 = cid; + CID cid = CID::kInvalid; CHECK(!cid); CHECK_EQ(cid.length(), 0); - CHECK_EQ(cid, cid2); + CHECK_EQ(CID::kInvalid, cid); + + const auto cid2 = random.Generate(); + const auto cid3 = cid2; + CHECK_EQ(cid2, cid3); } { - auto cid1 = random.Generate(); - auto cid2 = random.Generate(); + const auto cid1 = random.Generate(); + const auto cid2 = random.Generate(); + const auto cid3 = random.Generate(); + CID::Map map; map[cid1] = "hello"; map[cid2] = "there"; @@ -55,12 +66,23 @@ TEST(CID, Basic) { CHECK_EQ(map[cid2], "there"); CHECK_NE(map[cid2], "hello"); CHECK_NE(map[cid1], "there"); + + // Remove the two CIDs. + CHECK_EQ(map.erase(cid1), 1); + CHECK_EQ(map.erase(cid2), 1); + + // Make sure they were removed. + CHECK_EQ(map.find(cid1), map.end()); + CHECK_EQ(map.find(cid2), map.end()); + + // The cid3 is not in the map, so it should not be found. + CHECK_EQ(map.find(cid3), map.end()); } { ngtcp2_cid cid_; uint8_t data[] = {1, 2, 3, 4, 5}; ngtcp2_cid_init(&cid_, data, 5); - auto cid = CID(cid_); + const CID cid(cid_); // This variation of the constructor copies the cid_, so if we // modify the original data it doesn't change in the CID. cid_.data[0] = 9; @@ -71,26 +93,28 @@ TEST(CID, Basic) { ngtcp2_cid cid_; uint8_t data[] = {1, 2, 3, 4, 5}; ngtcp2_cid_init(&cid_, data, 5); - auto cid = CID(&cid_); + const CID cid(&cid_); // This variation of the constructor wraps the cid_, so if we - // modify the original data it does change in the CID. + // modify the original data it does change in the CID... not + // that this is something we would normally do. cid_.data[0] = 9; CHECK_EQ(cid.length(), 5); CHECK_EQ(cid.ToString(), "0902030405"); } { // Generate a bunch to ensure that the pool is regenerated. - for (int n = 0; n < 1000; n++) { - random.Generate(); + for (int n = 0; n < 5000; n++) { + CHECK(random.Generate()); } } { ngtcp2_cid cid_; // Generate a bunch to ensure that the pool is regenerated. - for (int n = 0; n < 1000; n++) { - random.GenerateInto(&cid_, 10); + for (int n = 0; n < 5000; n++) { + CHECK(random.GenerateInto(&cid_, 10)); CHECK_EQ(cid_.datalen, 10); } } } -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL diff --git a/test/cctest/test_quic_error.cc b/test/cctest/test_quic_error.cc index f342f8b9ebf060..04e3524ff611ac 100644 --- a/test/cctest/test_quic_error.cc +++ b/test/cctest/test_quic_error.cc @@ -1,4 +1,6 @@ -#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#if HAVE_OPENSSL +#include "quic/guard.h" +#ifndef OPENSSL_NO_QUIC #include #include #include @@ -13,7 +15,9 @@ TEST(QuicError, NoError) { CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), ""); CHECK_EQ(err, QuicError::TRANSPORT_NO_ERROR); - CHECK(!err); + + CHECK_EQ(QuicError::TransportError::NO_ERROR, QuicError::QUIC_NO_ERROR); + CHECK_EQ(QuicError::Http3Error::H3_NO_ERROR, QuicError::HTTP3_NO_ERROR_CODE); QuicError err2("a reason"); CHECK_EQ(err2.code(), QuicError::QUIC_NO_ERROR); @@ -29,17 +33,13 @@ TEST(QuicError, NoError) { CHECK_EQ(err3.reason(), ""); CHECK_EQ(err3, QuicError::TRANSPORT_NO_ERROR); - // QuicError's are copy assignable - auto err4 = err3; - CHECK_EQ(err4, err3); - // QuicError's are movable - auto err5 = std::move(err4); + auto err5 = std::move(err3); CHECK_EQ(err5, err3); // Equality check ignores the reason CHECK(err5 == err2); - CHECK(err5 != QuicError::APPLICATION_NO_ERROR); + CHECK(err5 != QuicError::HTTP3_NO_ERROR); const ngtcp2_ccerr& ccerr = err5; CHECK_EQ(ccerr.error_code, NGTCP2_NO_ERROR); @@ -57,8 +57,8 @@ TEST(QuicError, NoError) { QuicError err7(ccerr2); CHECK_EQ(err6, err7); - CHECK_EQ(err.ToString(), "QuicError(TRANSPORT) 0"); - CHECK_EQ(err2.ToString(), "QuicError(TRANSPORT) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(transport) 0"); + CHECK_EQ(err2.ToString(), "QuicError(transport) 0: a reason"); ngtcp2_ccerr ccerr3; ngtcp2_ccerr_default(&ccerr3); @@ -68,18 +68,16 @@ TEST(QuicError, NoError) { } TEST(QuicError, ApplicationNoError) { - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.code(), - QuicError::QUIC_APP_NO_ERROR); - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.type(), - QuicError::Type::APPLICATION); - CHECK_EQ(QuicError::APPLICATION_NO_ERROR.reason(), ""); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.code(), QuicError::HTTP3_NO_ERROR_CODE); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.type(), QuicError::Type::APPLICATION); + CHECK_EQ(QuicError::HTTP3_NO_ERROR.reason(), ""); auto err = - QuicError::ForApplication(QuicError::QUIC_APP_NO_ERROR, "a reason"); - CHECK_EQ(err.code(), QuicError::QUIC_APP_NO_ERROR); + QuicError::ForApplication(QuicError::HTTP3_NO_ERROR_CODE, "a reason"); + CHECK_EQ(err.code(), QuicError::HTTP3_NO_ERROR_CODE); CHECK_EQ(err.type(), QuicError::Type::APPLICATION); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(APPLICATION) 65280: a reason"); + CHECK_EQ(err.ToString(), "QuicError(application) 256: a reason"); } TEST(QuicError, VersionNegotiation) { @@ -92,7 +90,7 @@ TEST(QuicError, VersionNegotiation) { CHECK_EQ(err.code(), 0); CHECK_EQ(err.type(), QuicError::Type::VERSION_NEGOTIATION); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(VERSION_NEGOTIATION) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(version_negotiation) 0: a reason"); } TEST(QuicError, IdleClose) { @@ -104,7 +102,7 @@ TEST(QuicError, IdleClose) { CHECK_EQ(err.code(), 0); CHECK_EQ(err.type(), QuicError::Type::IDLE_CLOSE); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(IDLE_CLOSE) 0: a reason"); + CHECK_EQ(err.ToString(), "QuicError(idle_close) 0: a reason"); CHECK_EQ(QuicError::IDLE_CLOSE, err); } @@ -114,7 +112,7 @@ TEST(QuicError, InternalError) { CHECK_EQ(err.code(), NGTCP2_INTERNAL_ERROR); CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), "a reason"); - CHECK_EQ(err.ToString(), "QuicError(TRANSPORT) 1: a reason"); + CHECK_EQ(err.ToString(), "QuicError(transport) 1: a reason"); printf("%s\n", QuicError::INTERNAL_ERROR.ToString().c_str()); CHECK_EQ(err, QuicError::INTERNAL_ERROR); @@ -125,8 +123,9 @@ TEST(QuicError, TlsAlert) { CHECK_EQ(err.code(), 257); CHECK_EQ(err.type(), QuicError::Type::TRANSPORT); CHECK_EQ(err.reason(), "a reason"); - CHECK(err.is_crypto()); - CHECK_EQ(err.crypto_error(), 1); + CHECK(err.is_crypto_error()); + CHECK_EQ(err.get_crypto_error(), 1); } -#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC +#endif // OPENSSL_NO_QUIC +#endif // HAVE_OPENSSL From 256838e3afae2504e825afb2c8abfff2cbc64ba0 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 27 Jul 2025 13:32:27 -0700 Subject: [PATCH 2/4] deps: update ngtcp2 to 1.14.0 Signed-off-by: James M Snell --- deps/ngtcp2/README.md | 6 + deps/ngtcp2/ngtcp2.gyp | 9 +- .../crypto/includes/ngtcp2/ngtcp2_crypto.h | 7 + .../includes/ngtcp2/ngtcp2_crypto_ossl.h | 198 +++ .../includes/ngtcp2/ngtcp2_crypto_picotls.h | 2 +- deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c | 1191 +++++++++++++++++ .../ngtcp2/lib/includes/ngtcp2/ngtcp2.h | 95 +- .../ngtcp2/lib/includes/ngtcp2/version.h | 4 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c | 272 ++-- deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h | 6 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h | 8 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c | 72 + deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h | 73 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c | 184 +-- deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h | 17 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c | 6 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h | 20 + deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c | 583 +++++--- deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h | 26 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h | 8 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c | 20 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h | 2 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h | 32 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c | 6 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c | 57 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h | 3 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c | 16 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h | 7 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c | 32 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h | 6 - deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h | 4 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c | 17 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h | 5 - deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c | 233 ++-- deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h | 25 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c | 28 +- .../ngtcp2/lib/ngtcp2_transport_params.c | 12 +- deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h | 7 - src/quic/guard.h | 2 +- 39 files changed, 2576 insertions(+), 725 deletions(-) create mode 100644 deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h create mode 100644 deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h diff --git a/deps/ngtcp2/README.md b/deps/ngtcp2/README.md index 3585fa1f3490c7..f3bf7e03957c92 100644 --- a/deps/ngtcp2/README.md +++ b/deps/ngtcp2/README.md @@ -36,6 +36,9 @@ $ cp -R lib/* ../node/deps/ngtcp2/ngtcp2/lib/ $ cp -R crypto/* ../node/deps/ngtcp2/ngtcp2/crypto/ ``` +Be sure to also update the `ngtcp2.gyp` file to reflect any changes in +the source files or include directories. + ### Updating nghttp3 To update nghttp3, replace `v0.7.0` with the desired git tag: @@ -47,3 +50,6 @@ $ autoreconf -i $ ./configure --prefix=$PWD/build --enable-lib-only $ cp -R lib/* ../node/deps/ngtcp2/nghttp3/lib/ ``` + +Be sure to also update the `ngtcp2.gyp` file to reflect any changes in +the source files or include directories. diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index 5000f1f9fa676c..0593c6c32a7805 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -9,12 +9,13 @@ 'ngtcp2/lib/ngtcp2_balloc.c', 'ngtcp2/lib/ngtcp2_bbr.c', 'ngtcp2/lib/ngtcp2_buf.c', + 'ngtcp2/lib/ngtcp2_callbacks.c', 'ngtcp2/lib/ngtcp2_cc.c', 'ngtcp2/lib/ngtcp2_cid.c', 'ngtcp2/lib/ngtcp2_conn.c', 'ngtcp2/lib/ngtcp2_conv.c', - 'ngtcp2/lib/ngtcp2_dcidtr.c', 'ngtcp2/lib/ngtcp2_crypto.c', + 'ngtcp2/lib/ngtcp2_dcidtr.c', 'ngtcp2/lib/ngtcp2_err.c', 'ngtcp2/lib/ngtcp2_frame_chain.c', 'ngtcp2/lib/ngtcp2_gaptr.c', @@ -47,8 +48,8 @@ 'ngtcp2/lib/ngtcp2_window_filter.c', 'ngtcp2/crypto/shared.c' ], - 'ngtcp2_sources_quictls': [ - #'ngtcp2/crypto/quictls/quictls.c' + 'ngtcp2_sources_ossl': [ + 'ngtcp2/crypto/ossl/ossl.c' ], 'ngtcp2_sources_boringssl': [ 'ngtcp2/crypto/boringssl/boringssl.c' @@ -142,7 +143,7 @@ }, 'sources': [ '<@(ngtcp2_sources)', - '<@(ngtcp2_sources_quictls)', + '<@(ngtcp2_sources_ossl)', ] }, { diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 4eaf615bd309f3..003ec6b4c3f23c 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -69,6 +69,13 @@ extern "C" { */ #define NGTCP2_CRYPTO_ERR_VERIFY_TOKEN -203 +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_ERR_NOMEM` indicates out of memory. + */ +#define NGTCP2_CRYPTO_ERR_NOMEM -501 + /** * @function * diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h new file mode 100644 index 00000000000000..417ec017c60c13 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_ossl.h @@ -0,0 +1,198 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF 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_OSSL_H +#define NGTCP2_CRYPTO_OSSL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +/** + * @macrosection + * + * ossl specific error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP` is the error + * code which indicates that TLS handshake routine is interrupted by + * X509 certificate lookup. See :macro:`SSL_ERROR_WANT_X509_LOOKUP` + * error description from `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP -10001 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB` is the + * error code which indicates that TLS handshake routine is + * interrupted by client hello callback. See + * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 + +/** + * @function + * + * `ngtcp2_crypto_ossl_from_ossl_encryption_level` translates + * |ossl_level| to :type:`ngtcp2_encryption_level`. This function is + * only available for ossl backend. + */ +NGTCP2_EXTERN ngtcp2_encryption_level +ngtcp2_crypto_ossl_from_ossl_encryption_level(uint32_t ossl_level); + +/** + * @function + * + * `ngtcp2_crypto_ossl_from_ngtcp2_encryption_level` translates + * |encryption_level| to OpenSSL encryption level. This function is + * only available for ossl backend. + */ +NGTCP2_EXTERN uint32_t ngtcp2_crypto_ossl_from_ngtcp2_encryption_level( + ngtcp2_encryption_level encryption_level); + +/** + * @struct + * + * :type:`ngtcp2_crypto_ossl_ctx` contains per-connection state, and + * must be set to `ngtcp2_conn_set_tls_native_handle`. + */ +typedef struct ngtcp2_crypto_ossl_ctx ngtcp2_crypto_ossl_ctx; + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_new` creates new + * :type:`ngtcp2_crypto_ossl_ctx` object, and sets it to |*pctx| if it + * succeeds. + * + * |ssl| is set to |*pctx|. It may be NULL, and in that case, call + * `ngtcp2_crypto_ossl_ctx_set_ssl` later to set ``SSL`` object. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGTCP2_CRYPTO_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_ctx_new(ngtcp2_crypto_ossl_ctx **pctx, + SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_del` frees resources allocated for |ctx|. + * It also frees memory pointed by |ctx|. + */ +NGTCP2_EXTERN void ngtcp2_crypto_ossl_ctx_del(ngtcp2_crypto_ossl_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_set_ssl` sets |ssl| to |ctx|. This + * function must be called after ``SSL`` object is created. + */ +NGTCP2_EXTERN void ngtcp2_crypto_ossl_ctx_set_ssl(ngtcp2_crypto_ossl_ctx *ctx, + SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_ctx_get_ssl` returns ``SSL`` object set to + * |ctx|. If the object has not been set, this function returns NULL. + */ +NGTCP2_EXTERN SSL *ngtcp2_crypto_ossl_ctx_get_ssl(ngtcp2_crypto_ossl_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ossl_init` initializes libngtcp2_crypto_ossl + * library. This initialization is optional. It is highly + * recommended to call this function before any use of + * libngtcp2_crypto library API to workaround the performance + * regression. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_init(void); + +/** + * @function + * + * `ngtcp2_crypto_ossl_configure_server_session` configures |ssl| for + * server side QUIC connection. It performs the following + * modifications: + * + * - Register callbacks via ``SSL_set_quic_tls_cbs`` + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * Application must call ``SSL_set_app_data(ssl, NULL)`` before + * calling ``SSL_free(ssl)`` if you cannot make `ngtcp2_conn` object + * alive until ``SSL_free`` is called. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_configure_server_session(SSL *ssl); + +/** + * @function + * + * `ngtcp2_crypto_ossl_configure_client_session` configures |ssl| for + * client side QUIC connection. It performs the following + * modifications: + * + * - Register callbacks via ``SSL_set_quic_tls_cbs`` + * + * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to + * SSL object by calling SSL_set_app_data, and + * :type:`ngtcp2_crypto_conn_ref` object must have + * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get + * :type:`ngtcp2_conn`. + * + * Application must call ``SSL_set_app_data(ssl, NULL)`` before + * calling ``SSL_free(ssl)`` if you cannot make `ngtcp2_conn` object + * alive until ``SSL_free`` is called. + * + * It returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_ossl_configure_client_session(SSL *ssl); + +#ifdef __cplusplus +} +#endif /* defined(__cplusplus) */ + +#endif /* !defined(NGTCP2_CRYPTO_OSSL_H) */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h index d3f2f978e79923..87ea4ba264dd55 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h @@ -37,7 +37,7 @@ extern "C" { * @struct * * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state of - * Picotls objects and must be an object to bet set to + * Picotls object, and must be set to * `ngtcp2_conn_set_tls_native_handle`. */ typedef struct ngtcp2_crypto_picotls_ctx { diff --git a/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c new file mode 100644 index 00000000000000..061d6acd21d976 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/ossl/ossl.c @@ -0,0 +1,1191 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * 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 /* defined(HAVE_CONFIG_H) */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ngtcp2_macro.h" +#include "shared.h" + +static int crypto_initialized; +static EVP_CIPHER *crypto_aes_128_gcm; +static EVP_CIPHER *crypto_aes_256_gcm; +static EVP_CIPHER *crypto_chacha20_poly1305; +static EVP_CIPHER *crypto_aes_128_ccm; +static EVP_CIPHER *crypto_aes_128_ctr; +static EVP_CIPHER *crypto_aes_256_ctr; +static EVP_CIPHER *crypto_chacha20; +static EVP_MD *crypto_sha256; +static EVP_MD *crypto_sha384; +static EVP_KDF *crypto_hkdf; + +int ngtcp2_crypto_ossl_init(void) { + crypto_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL); + if (crypto_aes_128_gcm == NULL) { + return -1; + } + + crypto_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL); + if (crypto_aes_256_gcm == NULL) { + return -1; + } + + crypto_chacha20_poly1305 = EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL); + if (crypto_chacha20_poly1305 == NULL) { + return -1; + } + + crypto_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL); + if (crypto_aes_128_ccm == NULL) { + return -1; + } + + crypto_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL); + if (crypto_aes_128_ctr == NULL) { + return -1; + } + + crypto_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL); + if (crypto_aes_256_ctr == NULL) { + return -1; + } + + crypto_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20", NULL); + if (crypto_chacha20 == NULL) { + return -1; + } + + crypto_sha256 = EVP_MD_fetch(NULL, "sha256", NULL); + if (crypto_sha256 == NULL) { + return -1; + } + + crypto_sha384 = EVP_MD_fetch(NULL, "sha384", NULL); + if (crypto_sha384 == NULL) { + return -1; + } + + crypto_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL); + if (crypto_hkdf == NULL) { + return -1; + } + + crypto_initialized = 1; + + return 0; +} + +static const EVP_CIPHER *crypto_aead_aes_128_gcm(void) { + if (crypto_aes_128_gcm) { + return crypto_aes_128_gcm; + } + + return EVP_aes_128_gcm(); +} + +static const EVP_CIPHER *crypto_aead_aes_256_gcm(void) { + if (crypto_aes_256_gcm) { + return crypto_aes_256_gcm; + } + + return EVP_aes_256_gcm(); +} + +static const EVP_CIPHER *crypto_aead_chacha20_poly1305(void) { + if (crypto_chacha20_poly1305) { + return crypto_chacha20_poly1305; + } + + return EVP_chacha20_poly1305(); +} + +static const EVP_CIPHER *crypto_aead_aes_128_ccm(void) { + if (crypto_aes_128_ccm) { + return crypto_aes_128_ccm; + } + + return EVP_aes_128_ccm(); +} + +static const EVP_CIPHER *crypto_cipher_aes_128_ctr(void) { + if (crypto_aes_128_ctr) { + return crypto_aes_128_ctr; + } + + return EVP_aes_128_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_aes_256_ctr(void) { + if (crypto_aes_256_ctr) { + return crypto_aes_256_ctr; + } + + return EVP_aes_256_ctr(); +} + +static const EVP_CIPHER *crypto_cipher_chacha20(void) { + if (crypto_chacha20) { + return crypto_chacha20; + } + + return EVP_chacha20(); +} + +static const EVP_MD *crypto_md_sha256(void) { + if (crypto_sha256) { + return crypto_sha256; + } + + return EVP_sha256(); +} + +static const EVP_MD *crypto_md_sha384(void) { + if (crypto_sha384) { + return crypto_sha384; + } + + return EVP_sha384(); +} + +static EVP_KDF *crypto_kdf_hkdf(void) { + if (crypto_hkdf) { + return crypto_hkdf; + } + + return EVP_KDF_fetch(NULL, "hkdf", NULL); +} + +static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { + switch (EVP_CIPHER_nid(aead)) { + case NID_aes_128_gcm: + case NID_aes_256_gcm: + return EVP_GCM_TLS_TAG_LEN; + case NID_chacha20_poly1305: + return EVP_CHACHAPOLY_TLS_TAG_LEN; + case NID_aes_128_ccm: + return EVP_CCM_TLS_TAG_LEN; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) { + md->native_handle = (void *)crypto_md_sha256(); + return md; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_aead_aes_128_gcm()); + ctx->md.native_handle = (void *)crypto_md_sha256(); + ctx->hp.native_handle = (void *)crypto_cipher_aes_128_ctr(); + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = crypto_aead_max_overhead(aead_native_handle); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)crypto_aead_aes_128_gcm()); +} + +static const EVP_CIPHER *crypto_cipher_id_get_aead(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return crypto_aead_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_aead_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return crypto_aead_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_aead_aes_128_ccm(); + default: + return NULL; + } +} + +static uint64_t crypto_cipher_id_get_aead_max_encryption(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; + default: + return 0; + } +} + +static uint64_t +crypto_cipher_id_get_aead_max_decryption_failure(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; + default: + return 0; + } +} + +static const EVP_CIPHER *crypto_cipher_id_get_hp(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_cipher_aes_128_ctr(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_cipher_aes_256_ctr(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return crypto_cipher_chacha20(); + default: + return NULL; + } +} + +static const EVP_MD *crypto_cipher_id_get_md(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return crypto_md_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return crypto_md_sha384(); + default: + return NULL; + } +} + +static int supported_cipher_id(uint32_t cipher_id) { + switch (cipher_id) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return 1; + default: + return 0; + } +} + +typedef struct crypto_buf crypto_buf; + +typedef struct crypto_buf { + uint8_t data[4096]; + uint8_t *pos; + uint8_t *last; + + crypto_buf *next; +} crypto_buf; + +static crypto_buf *crypto_buf_new(void) { + crypto_buf *cbuf = malloc(sizeof(crypto_buf)); + + if (cbuf == NULL) { + return NULL; + } + + cbuf->pos = cbuf->last = cbuf->data; + cbuf->next = NULL; + + return cbuf; +} + +static void crypto_buf_del(crypto_buf *cbuf) { free(cbuf); } + +static size_t crypto_buf_left(const crypto_buf *cbuf) { + return (size_t)(cbuf->data + sizeof(cbuf->data) - cbuf->last); +} + +static size_t crypto_buf_len(const crypto_buf *cbuf) { + return (size_t)(cbuf->last - cbuf->pos); +} + +static size_t crypto_buf_eof(const crypto_buf *cbuf) { + return cbuf->pos == cbuf->data + sizeof(cbuf->data); +} + +static void crypto_buf_write(crypto_buf *cbuf, const uint8_t *data, + size_t datalen) { + assert(crypto_buf_left(cbuf) >= datalen); + + memcpy(cbuf->last, data, datalen); + cbuf->last += datalen; +} + +struct ngtcp2_crypto_ossl_ctx { + SSL *ssl; + ngtcp2_encryption_level tx_level; + crypto_buf *crypto_head, *crypto_read, *crypto_write; + size_t crypto_head_released; + uint8_t *remote_params; +}; + +int ngtcp2_crypto_ossl_ctx_new(ngtcp2_crypto_ossl_ctx **possl_ctx, SSL *ssl) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = malloc(sizeof(**possl_ctx)); + + if (ossl_ctx == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + + ossl_ctx->ssl = ssl; + ossl_ctx->tx_level = NGTCP2_ENCRYPTION_LEVEL_INITIAL; + ossl_ctx->crypto_head = ossl_ctx->crypto_read = ossl_ctx->crypto_write = NULL; + ossl_ctx->crypto_head_released = 0; + ossl_ctx->remote_params = NULL; + + *possl_ctx = ossl_ctx; + + return 0; +} + +void ngtcp2_crypto_ossl_ctx_del(ngtcp2_crypto_ossl_ctx *ossl_ctx) { + crypto_buf *cbuf, *next; + + if (!ossl_ctx) { + return; + } + + for (cbuf = ossl_ctx->crypto_head; cbuf;) { + next = cbuf->next; + crypto_buf_del(cbuf); + cbuf = next; + } + + free(ossl_ctx->remote_params); + free(ossl_ctx); +} + +void ngtcp2_crypto_ossl_ctx_set_ssl(ngtcp2_crypto_ossl_ctx *ossl_ctx, + SSL *ssl) { + ossl_ctx->ssl = ssl; +} + +SSL *ngtcp2_crypto_ossl_ctx_get_ssl(ngtcp2_crypto_ossl_ctx *ossl_ctx) { + return ossl_ctx->ssl; +} + +static int crypto_ossl_ctx_write_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + const uint8_t *data, + size_t datalen) { + crypto_buf *cbuf; + const uint8_t *end; + size_t n, left; + + if (datalen == 0) { + return 0; + } + + if (ossl_ctx->crypto_write == NULL) { + ossl_ctx->crypto_head = ossl_ctx->crypto_read = ossl_ctx->crypto_write = + crypto_buf_new(); + if (ossl_ctx->crypto_head == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + } + + for (end = data + datalen; data != end;) { + left = crypto_buf_left(ossl_ctx->crypto_write); + if (left == 0) { + cbuf = crypto_buf_new(); + if (cbuf == NULL) { + return NGTCP2_CRYPTO_ERR_NOMEM; + } + + ossl_ctx->crypto_write->next = cbuf; + ossl_ctx->crypto_write = cbuf; + + left = crypto_buf_left(ossl_ctx->crypto_write); + } + + n = ngtcp2_min_size((size_t)(end - data), left); + crypto_buf_write(ossl_ctx->crypto_write, data, n); + data += n; + } + + return 0; +} + +static void crypto_ossl_ctx_read_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + const uint8_t **pbuf, + size_t *pbytes_read) { + size_t n; + + if (ossl_ctx->crypto_read == NULL) { + *pbuf = NULL; + *pbytes_read = 0; + return; + } + + n = crypto_buf_len(ossl_ctx->crypto_read); + + *pbuf = ossl_ctx->crypto_read->pos; + *pbytes_read = n; + + ossl_ctx->crypto_read->pos += n; + + if (crypto_buf_eof(ossl_ctx->crypto_read) && + ossl_ctx->crypto_read != ossl_ctx->crypto_write) { + ossl_ctx->crypto_read = ossl_ctx->crypto_read->next; + } +} + +static void +crypto_ossl_ctx_release_crypto_data(ngtcp2_crypto_ossl_ctx *ossl_ctx, + size_t released) { + crypto_buf *cbuf = NULL; + + if (released == 0) { + return; + } + + assert(ossl_ctx->crypto_head); + + ossl_ctx->crypto_head_released += released; + + for (; ossl_ctx->crypto_head_released >= sizeof(cbuf->data);) { + assert(ossl_ctx->crypto_head); + + cbuf = ossl_ctx->crypto_head; + ossl_ctx->crypto_head = cbuf->next; + + crypto_buf_del(cbuf); + ossl_ctx->crypto_head_released -= sizeof(cbuf->data); + } + + if (cbuf == ossl_ctx->crypto_read) { + ossl_ctx->crypto_read = ossl_ctx->crypto_head; + + if (cbuf == ossl_ctx->crypto_write) { + assert(ossl_ctx->crypto_head == NULL); + + ossl_ctx->crypto_write = NULL; + } + } +} + +static ngtcp2_crypto_ctx *crypto_ctx_cipher_id(ngtcp2_crypto_ctx *ctx, + uint32_t cipher_id) { + ngtcp2_crypto_aead_init(&ctx->aead, + (void *)crypto_cipher_id_get_aead(cipher_id)); + ctx->md.native_handle = (void *)crypto_cipher_id_get_md(cipher_id); + ctx->hp.native_handle = (void *)crypto_cipher_id_get_hp(cipher_id); + ctx->max_encryption = crypto_cipher_id_get_aead_max_encryption(cipher_id); + ctx->max_decryption_failure = + crypto_cipher_id_get_aead_max_decryption_failure(cipher_id); + + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = tls_native_handle; + SSL *ssl = ossl_ctx->ssl; + const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + uint32_t cipher_id; + + if (cipher == NULL) { + return NULL; + } + + cipher_id = (uint32_t)SSL_CIPHER_get_id(cipher); + + if (!supported_cipher_id(cipher_id)) { + return NULL; + } + + return crypto_ctx_cipher_id(ctx, cipher_id); +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { + return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); + OSSL_PARAM params[3]; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_params(actx, params) || + !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + size_t taglen = crypto_aead_max_overhead(cipher); + OSSL_PARAM params[3]; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen); + + if (cipher_nid == NID_aes_128_ccm) { + params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + NULL, taglen); + params[2] = OSSL_PARAM_construct_end(); + } else { + params[1] = OSSL_PARAM_construct_end(); + } + + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_params(actx, params) || + !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + EVP_CIPHER_CTX_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + EVP_CIPHER_CTX *actx; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + cipher_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + EVP_CIPHER_CTX_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +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; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode), + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *salt, size_t saltlen, + const uint8_t *info, size_t infolen) { + const EVP_MD *prf = md->native_handle; + EVP_KDF *kdf = crypto_kdf_hkdf(); + EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM params[] = { + OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + (char *)EVP_MD_get0_name(prf), 0), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret, + secretlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt, + saltlen), + OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info, + infolen), + OSSL_PARAM_construct_end(), + }; + int rv = 0; + + if (!crypto_initialized) { + EVP_KDF_free(kdf); + } + + if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) { + rv = -1; + } + + EVP_KDF_CTX_free(kctx); + + return rv; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + OSSL_PARAM params[] = { + OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + dest + plaintextlen, taglen), + OSSL_PARAM_construct_end(), + }; + + (void)noncelen; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || + (cipher_nid == NID_aes_128_ccm && + !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || + !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) || + !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || + !EVP_EncryptFinal_ex(actx, dest + len, &len) || + !EVP_CIPHER_CTX_get_params(actx, params)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + const uint8_t *tag; + OSSL_PARAM params[2]; + + (void)noncelen; + + if (taglen > ciphertextlen) { + return -1; + } + + ciphertextlen -= taglen; + tag = ciphertext + ciphertextlen; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + (void *)tag, taglen); + params[1] = OSSL_PARAM_construct_end(); + + if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || + !EVP_CIPHER_CTX_set_params(actx, params) || + (cipher_nid == NID_aes_128_ccm && + !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || + !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) || + !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || + (cipher_nid != NID_aes_128_ccm && + !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + EVP_CIPHER_CTX *actx = hp_ctx->native_handle; + int len; + + (void)hp; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data( + ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, + const uint8_t *data, size_t datalen) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + SSL *ssl = ossl_ctx->ssl; + int rv; + int err; + (void)encryption_level; + + if (crypto_ossl_ctx_write_crypto_data(ossl_ctx, data, datalen) != 0) { + 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_OSSL_ERR_TLS_WANT_CLIENT_HELLO_CB; + case SSL_ERROR_WANT_X509_LOOKUP: + return NGTCP2_CRYPTO_OSSL_ERR_TLS_WANT_X509_LOOKUP; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + ngtcp2_conn_tls_handshake_completed(conn); + } + + rv = SSL_read(ssl, NULL, 0); + 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) { + (void)conn; + (void)tls; + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + ngtcp2_crypto_ossl_ctx *ossl_ctx = tls; + + if (len) { + assert(!ossl_ctx->remote_params); + + ossl_ctx->remote_params = malloc(len); + if (!ossl_ctx->remote_params) { + return -1; + } + + memcpy(ossl_ctx->remote_params, buf, len); + } + + if (SSL_set_quic_tls_transport_params(ossl_ctx->ssl, ossl_ctx->remote_params, + len) != 1) { + return -1; + } + + return 0; +} + +ngtcp2_encryption_level +ngtcp2_crypto_ossl_from_ossl_encryption_level(uint32_t ossl_level) { + switch (ossl_level) { + case OSSL_RECORD_PROTECTION_LEVEL_NONE: + return NGTCP2_ENCRYPTION_LEVEL_INITIAL; + case OSSL_RECORD_PROTECTION_LEVEL_EARLY: + return NGTCP2_ENCRYPTION_LEVEL_0RTT; + case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE: + return NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE; + case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION: + return NGTCP2_ENCRYPTION_LEVEL_1RTT; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +uint32_t ngtcp2_crypto_ossl_from_ngtcp2_encryption_level( + ngtcp2_encryption_level encryption_level) { + switch (encryption_level) { + case NGTCP2_ENCRYPTION_LEVEL_INITIAL: + return OSSL_RECORD_PROTECTION_LEVEL_NONE; + case NGTCP2_ENCRYPTION_LEVEL_HANDSHAKE: + return OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE; + case NGTCP2_ENCRYPTION_LEVEL_1RTT: + return OSSL_RECORD_PROTECTION_LEVEL_APPLICATION; + case NGTCP2_ENCRYPTION_LEVEL_0RTT: + return OSSL_RECORD_PROTECTION_LEVEL_EARLY; + default: + assert(0); + abort(); /* if NDEBUG is set */ + } +} + +int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_random(uint8_t *data, size_t datalen) { + if (RAND_bytes(data, (int)datalen) != 1) { + return -1; + } + + return 0; +} + +static int ossl_yield_secret(SSL *ssl, uint32_t ossl_level, int direction, + const unsigned char *secret, size_t secretlen, + void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + ngtcp2_encryption_level level = + ngtcp2_crypto_ossl_from_ossl_encryption_level(ossl_level); + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + if (direction) { + if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + ossl_ctx->tx_level = level; + + return 1; + } + + if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level, + secret, secretlen) != 0) { + return 0; + } + + return 1; +} + +static int ossl_crypto_send(SSL *ssl, const unsigned char *buf, size_t buflen, + size_t *consumed, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + int rv; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + rv = ngtcp2_conn_submit_crypto_data(conn, ossl_ctx->tx_level, buf, buflen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + *consumed = buflen; + + return 1; +} + +static int ossl_crypto_recv_rcd(SSL *ssl, const unsigned char **buf, + size_t *bytes_read, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + (void)arg; + + if (!conn_ref) { + *buf = NULL; + *bytes_read = 0; + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + crypto_ossl_ctx_read_crypto_data(ossl_ctx, buf, bytes_read); + + return 1; +} + +static int ossl_crypto_release_rcd(SSL *ssl, size_t released, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + ngtcp2_crypto_ossl_ctx *ossl_ctx; + (void)arg; + + /* It is sometimes a bit hard or tedious to keep ngtcp2_conn alive + until SSL_free is called. Instead, we require application to + call SSL_set_app_data(ssl, NULL) before SSL_free(ssl) so that + ngtcp2_conn is never used in this function. */ + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + ossl_ctx = ngtcp2_conn_get_tls_native_handle(conn); + + crypto_ossl_ctx_release_crypto_data(ossl_ctx, released); + + return 1; +} + +static int ossl_got_transport_params(SSL *ssl, const unsigned char *params, + size_t paramslen, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + int rv; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + + rv = + ngtcp2_conn_decode_and_set_remote_transport_params(conn, params, paramslen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return 0; + } + + return 1; +} + +static int ossl_alert(SSL *ssl, uint8_t alert_code, void *arg) { + ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl); + ngtcp2_conn *conn; + (void)arg; + + if (!conn_ref) { + return 1; + } + + conn = conn_ref->get_conn(conn_ref); + + ngtcp2_conn_set_tls_alert(conn, alert_code); + + return 1; +} + +static const OSSL_DISPATCH qtdis[] = { + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND, + (void (*)(void))ossl_crypto_send, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD, + (void (*)(void))ossl_crypto_recv_rcd, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD, + (void (*)(void))ossl_crypto_release_rcd, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET, + (void (*)(void))ossl_yield_secret, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS, + (void (*)(void))ossl_got_transport_params, + }, + { + OSSL_FUNC_SSL_QUIC_TLS_ALERT, + (void (*)(void))ossl_alert, + }, + OSSL_DISPATCH_END, +}; + +static int crypto_ossl_configure_session(SSL *ssl) { + if (!SSL_set_quic_tls_cbs(ssl, qtdis, NULL)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_ossl_configure_server_session(SSL *ssl) { + return crypto_ossl_configure_session(ssl); +} + +int ngtcp2_crypto_ossl_configure_client_session(SSL *ssl) { + return crypto_ossl_configure_session(ssl); +} diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index d2a2fe1fe1be0d..b0cc867cde0b20 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -214,10 +214,18 @@ typedef struct ngtcp2_mem { /** * @macro * - * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 - * second. + * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to + * 1 nanosecond. + */ +#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds + * to 1 microsecond. */ -#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL) +#define NGTCP2_MICROSECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_NANOSECONDS)) /** * @macro @@ -225,23 +233,23 @@ typedef struct ngtcp2_mem { * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds * to 1 millisecond. */ -#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL) +#define NGTCP2_MILLISECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MICROSECONDS)) /** * @macro * - * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds - * to 1 microsecond. + * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 + * second. */ -#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL) +#define NGTCP2_SECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MILLISECONDS)) /** * @macro * - * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to - * 1 nanosecond. + * :macro:`NGTCP2_MINUTES` is a count of tick which corresponds to 1 + * minute. */ -#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL) +#define NGTCP2_MINUTES ((ngtcp2_duration)(60ULL * NGTCP2_SECONDS)) /** * @macrosection @@ -2858,7 +2866,8 @@ typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, * :type:`ngtcp2_rand` is a callback function to get random data of * length |destlen|. Application must fill random |destlen| bytes to * the buffer pointed by |dest|. The generated data is used only in - * non-cryptographic context. + * non-cryptographic context. But it is strongly recommended to use a + * secure random number generator. */ typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx); @@ -2962,6 +2971,28 @@ typedef int (*ngtcp2_update_key)( */ #define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02u +/** + * @functypedef + * + * :type:`ngtcp2_begin_path_validation` is a callback function which + * is called when the path validation has started. |flags| is zero or + * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* + * `. |path| is the path that is + * being validated. |fallback_path|, if not NULL, is the path that is + * used when this validation fails. + * + * Currently, the flags may only contain + * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR`. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags, + const ngtcp2_path *path, + const ngtcp2_path *fallback_path, + void *user_data); + /** * @functypedef * @@ -3253,7 +3284,8 @@ typedef int (*ngtcp2_tls_early_data_rejected)(ngtcp2_conn *conn, void *user_data); #define NGTCP2_CALLBACKS_V1 1 -#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V1 +#define NGTCP2_CALLBACKS_V2 2 +#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V2 /** * @struct @@ -3518,6 +3550,13 @@ typedef struct ngtcp2_callbacks { * is only used by client. */ ngtcp2_tls_early_data_rejected tls_early_data_rejected; + /* The following fields have been added since NGTCP2_CALLBACKS_V2. */ + /** + * :member:`begin_path_validation` is a callback function which is + * invoked when a path validation has started. This field is + * available since v1.14.0. + */ + ngtcp2_begin_path_validation begin_path_validation; } ngtcp2_callbacks; /** @@ -4398,6 +4437,17 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, */ #define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` indicates that a + * non-empty 0 RTT or 1 RTT ack-eliciting packet is padded to the + * minimum length of a sending path MTU or a given packet buffer when + * finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE + * only packets and PMTUD packets are excluded. + */ +#define NGTCP2_WRITE_STREAM_FLAG_PADDING 0x04u + /** * @function * @@ -4522,6 +4572,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned( * include, call this function with |stream_id| as -1 to stop * coalescing and write a packet. * + * If :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` is set in |flags| when + * finalizing a non-empty 0 RTT or 1 RTT ack-eliciting packet, the + * packet is padded to the minimum length of a sending path MTU or a + * given packet buffer. + * * 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. @@ -4586,6 +4641,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( */ #define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` indicates that a + * non-empty 0 RTT or 1 RTT ack-eliciting packet is padded to the + * minimum length of a sending path MTU or a given packet buffer when + * finalizing it. PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE + * only packets and PMTUD packets are excluded. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_PADDING 0x02u + /** * @function * @@ -4667,6 +4733,11 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( * (or `ngtcp2_conn_writev_stream`) until it returns a positive number * (which indicates a complete packet is ready). * + * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` is set in |flags| + * when finalizing a non-empty 0 RTT or 1 RTT ack-eliciting packet, + * the packet is padded to the minimum length of a sending path MTU or + * a given packet buffer. + * * This function returns the number of bytes written in |dest| if it * succeeds, or one of the following negative error codes: * diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index bb983f5cf44be3..533f6d5ef6c55d 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "1.11.0" +#define NGTCP2_VERSION "1.14.0" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x010b00 +#define NGTCP2_VERSION_NUM 0x010e00 #endif /* !defined(NGTCP2_VERSION_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c index a20f04521e36ca..a2ffeb6188a57e 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -69,7 +69,7 @@ static void bbr_on_transmit(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr); -static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr); +static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr); static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr); @@ -157,8 +157,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); -static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); @@ -168,15 +167,16 @@ static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); -static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat); +static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat); -static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack); +static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); -static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); +static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack); static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); @@ -189,24 +189,20 @@ static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat); -static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); - -static int is_inflight_too_high(const ngtcp2_rs *rs); +static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr); static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_rs *rs, ngtcp2_tstamp ts); + ngtcp2_tstamp ts); static void bbr_note_loss(ngtcp2_cc_bbr *bbr); static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); -static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, - const ngtcp2_rs *rs, - const ngtcp2_cc_pkt *pkt); +static uint64_t +bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr, + const ngtcp2_cc_pkt *pkt); static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts); @@ -292,7 +288,7 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, bbr->full_bw_reached = 0; bbr_reset_congestion_signals(bbr); - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); bbr_init_round_counting(bbr); bbr_reset_full_bw(bbr); bbr_init_pacing_rate(bbr, cstat); @@ -326,7 +322,7 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, bbr->bw_probe_up_rounds = 0; bbr->bw_probe_up_acks = 0; - bbr->inflight_hi = UINT64_MAX; + bbr->inflight_longterm = UINT64_MAX; bbr->probe_rtt_expired = 0; bbr->probe_rtt_min_delay = UINT64_MAX; @@ -348,9 +344,9 @@ static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) { bbr->inflight_latest = 0; } -static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) { - bbr->bw_lo = UINT64_MAX; - bbr->inflight_lo = UINT64_MAX; +static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr) { + bbr->bw_shortterm = UINT64_MAX; + bbr->inflight_shortterm = UINT64_MAX; } static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr) { @@ -367,7 +363,7 @@ static void bbr_reset_full_bw(ngtcp2_cc_bbr *bbr) { static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->full_bw_now || bbr->rst->rs.is_app_limited) { + if (bbr->full_bw_now || !bbr->round_start || bbr->rst->rs.is_app_limited) { return; } @@ -378,10 +374,6 @@ static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr, return; } - if (!bbr->round_start) { - return; - } - ++bbr->full_bw_count; bbr->full_bw_now = bbr->full_bw_count >= 3; @@ -399,36 +391,38 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) { if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 || (bbr->in_loss_recovery && bbr->round_count <= bbr->round_count_at_recovery) || - !is_inflight_too_high(&bbr->rst->rs)) { + !bbr_is_inflight_too_high(bbr)) { return; } bbr->full_bw_reached = 1; - bbr->inflight_hi = ngtcp2_max_uint64(bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), - bbr->inflight_latest); + bbr->inflight_longterm = ngtcp2_max_uint64( + bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), bbr->inflight_latest); } static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - cstat->pacing_interval = - (cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS - : cstat->smoothed_rtt) * + cstat->pacing_interval_m = + ((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS + : cstat->smoothed_rtt) + << 10) * 100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd; } static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, uint64_t pacing_gain_h) { - ngtcp2_duration interval; + uint64_t interval_m; if (bbr->bw == 0) { return; } - interval = NGTCP2_SECONDS * 100 * 100 / pacing_gain_h / bbr->bw / - (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT); + interval_m = (NGTCP2_SECONDS << 10) * 100 * 100 / pacing_gain_h / bbr->bw / + (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT); + interval_m = ngtcp2_max_uint64(interval_m, 1); - if (bbr->full_bw_reached || interval < cstat->pacing_interval) { - cstat->pacing_interval = interval; + if (bbr->full_bw_reached || interval_m < cstat->pacing_interval_m) { + cstat->pacing_interval_m = interval_m; } } @@ -546,25 +540,26 @@ static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr, } static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->bw_lo == UINT64_MAX) { - bbr->bw_lo = bbr->max_bw; + if (bbr->bw_shortterm == UINT64_MAX) { + bbr->bw_shortterm = bbr->max_bw; } - if (bbr->inflight_lo == UINT64_MAX) { - bbr->inflight_lo = cstat->cwnd; + if (bbr->inflight_shortterm == UINT64_MAX) { + bbr->inflight_shortterm = cstat->cwnd; } } static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr) { - bbr->bw_lo = ngtcp2_max_uint64( - bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); - bbr->inflight_lo = ngtcp2_max_uint64( + bbr->bw_shortterm = ngtcp2_max_uint64( + bbr->bw_latest, + bbr->bw_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + bbr->inflight_shortterm = ngtcp2_max_uint64( bbr->inflight_latest, - bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); + bbr->inflight_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); } static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr) { - bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_lo); + bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_shortterm); } static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, @@ -712,7 +707,7 @@ static void bbr_start_probe_bw_refill(ngtcp2_cc_bbr *bbr) { ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA, "bbr start ProbeBW_REFILL"); - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); bbr->bw_probe_up_rounds = 0; bbr->bw_probe_up_acks = 0; @@ -738,7 +733,7 @@ static void bbr_start_probe_bw_up(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { bbr->pacing_gain_h = 125; bbr->cwnd_gain_h = 225; - bbr_raise_inflight_hi_slope(bbr, cstat); + bbr_raise_inflight_longterm_slope(bbr, cstat); } static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, @@ -749,7 +744,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, return; } - bbr_adapt_upper_bounds(bbr, cstat, ack, ts); + bbr_adapt_longterm_model(bbr, cstat, ack); if (!bbr_is_in_probe_bw_state(bbr)) { return; @@ -761,7 +756,7 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, return; } - if (bbr_is_time_to_cruise(bbr, cstat, ts)) { + if (bbr_is_time_to_cruise(bbr, cstat)) { bbr_start_probe_bw_cruise(bbr); } @@ -790,30 +785,22 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr, } } -static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - (void)ts; - - if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) { - return 0; - } +static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { + uint64_t inflight = ngtcp2_min_uint64(bbr_inflight_with_headroom(bbr, cstat), + bbr_inflight(bbr, cstat, 100)); - if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) { - return 1; - } - - return 0; + return cstat->bytes_in_flight <= inflight; } static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_hi) { + if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_longterm) { bbr_reset_full_bw(bbr); bbr->full_bw = cstat->delivery_rate_sec; - } else if (bbr->full_bw_now) { - return 1; + + return 0; } - return 0; + return bbr->full_bw_now; } static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr, @@ -826,24 +813,25 @@ static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { uint64_t headroom; uint64_t mpcwnd; - if (bbr->inflight_hi == UINT64_MAX) { + if (bbr->inflight_longterm == UINT64_MAX) { return UINT64_MAX; } - headroom = ngtcp2_max_uint64(cstat->max_tx_udp_payload_size, - bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER / - NGTCP2_BBR_HEADROOM_DENOM); + headroom = + ngtcp2_max_uint64(cstat->max_tx_udp_payload_size, + bbr->inflight_longterm * NGTCP2_BBR_HEADROOM_NUMER / + NGTCP2_BBR_HEADROOM_DENOM); mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size); - if (bbr->inflight_hi > headroom) { - return ngtcp2_max_uint64(bbr->inflight_hi - headroom, mpcwnd); + if (bbr->inflight_longterm > headroom) { + return ngtcp2_max_uint64(bbr->inflight_longterm - headroom, mpcwnd); } return mpcwnd; } -static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat) { +static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat) { uint64_t growth_this_round = cstat->max_tx_udp_payload_size << bbr->bw_probe_up_rounds; @@ -851,12 +839,12 @@ static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1); } -static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack) { +static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { uint64_t delta; - if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) { + if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_longterm) { return; } @@ -867,16 +855,17 @@ static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, bbr->probe_up_cnt * cstat->max_tx_udp_payload_size) { delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; - bbr->inflight_hi += delta; + bbr->inflight_longterm += delta; } if (bbr->round_start) { - bbr_raise_inflight_hi_slope(bbr, cstat); + bbr_raise_inflight_longterm_slope(bbr, cstat); } } -static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { +static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr, + ngtcp2_conn_stat *cstat, + const ngtcp2_cc_ack *ack) { if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING && bbr->round_start) { bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK; @@ -889,17 +878,17 @@ static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, } } - if (!bbr_check_inflight_too_high(bbr, cstat, ts)) { - if (bbr->inflight_hi == UINT64_MAX) { + if (!bbr_is_inflight_too_high(bbr)) { + if (bbr->inflight_longterm == UINT64_MAX) { return; } - if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) { - bbr->inflight_hi = bbr->rst->rs.tx_in_flight; + if (bbr->rst->rs.tx_in_flight > bbr->inflight_longterm) { + bbr->inflight_longterm = bbr->rst->rs.tx_in_flight; } if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) { - bbr_probe_inflight_hi_upward(bbr, cstat, ack); + bbr_probe_inflight_longterm_upward(bbr, cstat, ack); } } } @@ -921,7 +910,7 @@ static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) { bbr->rand(&rand, 1, &bbr->rand_ctx); - bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256); + bbr->rounds_since_bw_probe = (uint64_t)(rand / 128); bbr->rand(&rand, 1, &bbr->rand_ctx); @@ -941,33 +930,21 @@ static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd); } -static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, - ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { - if (is_inflight_too_high(&bbr->rst->rs)) { - if (bbr->bw_probe_samples) { - bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts); - } - - return 1; - } - - return 0; -} - -static int is_inflight_too_high(const ngtcp2_rs *rs) { +static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) { + const ngtcp2_rs *rs = &bbr->rst->rs; return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM > rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER; } static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - const ngtcp2_rs *rs, ngtcp2_tstamp ts) { + const ngtcp2_rs *rs = &bbr->rst->rs; + bbr->bw_probe_samples = 0; if (!rs->is_app_limited) { - bbr->inflight_hi = ngtcp2_max_uint64( + bbr->inflight_longterm = ngtcp2_max_uint64( rs->tx_in_flight, bbr_target_inflight(bbr, cstat) * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM); } @@ -987,7 +964,7 @@ static void bbr_note_loss(ngtcp2_cc_bbr *bbr) { static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { - ngtcp2_rs rs = {0}; + ngtcp2_rs *rs = &bbr->rst->rs; bbr_note_loss(bbr); @@ -995,22 +972,22 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, return; } - rs.tx_in_flight = pkt->tx_in_flight; - /* bbr->rst->lost is not incremented for pkt yet */ - assert(bbr->rst->lost + pkt->pktlen >= pkt->lost); - rs.lost = bbr->rst->lost + pkt->pktlen - pkt->lost; - rs.is_app_limited = pkt->is_app_limited; + rs->tx_in_flight = pkt->tx_in_flight; + assert(bbr->rst->lost >= pkt->lost); + rs->lost = bbr->rst->lost - pkt->lost; + rs->is_app_limited = pkt->is_app_limited; - if (is_inflight_too_high(&rs)) { - rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt); + if (bbr_is_inflight_too_high(bbr)) { + rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt); - bbr_handle_inflight_too_high(bbr, cstat, &rs, ts); + bbr_handle_inflight_too_high(bbr, cstat, ts); } } -static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr, - const ngtcp2_rs *rs, - const ngtcp2_cc_pkt *pkt) { +static uint64_t +bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_rs *rs = &bbr->rst->rs; uint64_t inflight_prev, lost_prev, lost_prefix; (void)bbr; @@ -1136,7 +1113,7 @@ static void bbr_mark_connection_app_limited(ngtcp2_cc_bbr *bbr, } static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) { - bbr_reset_lower_bounds(bbr); + bbr_reset_shortterm_model(bbr); if (bbr->full_bw_reached) { bbr_start_probe_bw_down(bbr, ts); @@ -1204,9 +1181,6 @@ static void bbr_update_max_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { uint64_t inflight; - /* Not documented */ - /* bbr_update_aggregation_budget(bbr); */ - inflight = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h) + bbr->extra_acked; bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight); } @@ -1244,12 +1218,9 @@ static uint64_t bbr_probe_rtt_cwnd(ngtcp2_cc_bbr *bbr, static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - uint64_t probe_rtt_cwnd; - if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) { - probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat); - - cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, probe_rtt_cwnd); + cstat->cwnd = + ngtcp2_min_uint64(cstat->cwnd, bbr_probe_rtt_cwnd(bbr, cstat)); } } @@ -1281,13 +1252,13 @@ static void bbr_bound_cwnd_for_model(ngtcp2_cc_bbr *bbr, if (bbr_is_in_probe_bw_state(bbr) && bbr->state != NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { - cap = bbr->inflight_hi; + cap = bbr->inflight_longterm; } else if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT || bbr->state == NGTCP2_BBR_STATE_PROBE_BW_CRUISE) { cap = bbr_inflight_with_headroom(bbr, cstat); } - cap = ngtcp2_min_uint64(cap, bbr->inflight_lo); + cap = ngtcp2_min_uint64(cap, bbr->inflight_shortterm); cap = ngtcp2_max_uint64(cap, mpcwnd); cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, cap); @@ -1297,10 +1268,9 @@ static void bbr_set_send_quantum(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { size_t send_quantum = 64 * 1024; (void)bbr; - if (cstat->pacing_interval) { - send_quantum = ngtcp2_min_size( - send_quantum, (size_t)(NGTCP2_MILLISECONDS / cstat->pacing_interval)); - } + send_quantum = + ngtcp2_min_size(send_quantum, (size_t)((NGTCP2_MILLISECONDS << 10) / + cstat->pacing_interval_m)); cstat->send_quantum = ngtcp2_max_size(send_quantum, 2 * cstat->max_tx_udp_payload_size); @@ -1420,21 +1390,23 @@ void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, ngtcp2_tstamp initial_ts, ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx) { - memset(bbr, 0, sizeof(*bbr)); - - bbr->cc.log = log; - bbr->cc.on_pkt_lost = bbr_cc_on_pkt_lost; - bbr->cc.congestion_event = bbr_cc_congestion_event; - bbr->cc.on_spurious_congestion = bbr_cc_on_spurious_congestion; - bbr->cc.on_persistent_congestion = bbr_cc_on_persistent_congestion; - bbr->cc.on_ack_recv = bbr_cc_on_ack_recv; - bbr->cc.on_pkt_sent = bbr_cc_on_pkt_sent; - bbr->cc.reset = bbr_cc_reset; - - bbr->rst = rst; - bbr->rand = rand; - bbr->rand_ctx = *rand_ctx; - bbr->initial_cwnd = cstat->cwnd; + *bbr = (ngtcp2_cc_bbr){ + .cc = + { + .log = log, + .on_pkt_lost = bbr_cc_on_pkt_lost, + .congestion_event = bbr_cc_congestion_event, + .on_spurious_congestion = bbr_cc_on_spurious_congestion, + .on_persistent_congestion = bbr_cc_on_persistent_congestion, + .on_ack_recv = bbr_cc_on_ack_recv, + .on_pkt_sent = bbr_cc_on_pkt_sent, + .reset = bbr_cc_reset, + }, + .rst = rst, + .rand = rand, + .rand_ctx = *rand_ctx, + .initial_cwnd = cstat->cwnd, + }; bbr_on_init(bbr, cstat, initial_ts); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h index 74eb2d640bd3f2..e823711a500654 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -86,8 +86,8 @@ typedef struct ngtcp2_cc_bbr { uint64_t inflight_latest; /* Lower bounds */ - uint64_t bw_lo; - uint64_t inflight_lo; + uint64_t bw_shortterm; + uint64_t inflight_shortterm; /* Round counting */ uint64_t next_round_delivered; @@ -123,7 +123,7 @@ typedef struct ngtcp2_cc_bbr { int bw_probe_samples; size_t bw_probe_up_rounds; uint64_t bw_probe_up_acks; - uint64_t inflight_hi; + uint64_t inflight_longterm; int probe_rtt_expired; ngtcp2_duration probe_rtt_min_delay; ngtcp2_tstamp probe_rtt_min_stamp; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h index e87adb119916ca..b2bdafc3875260 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -62,13 +62,17 @@ void ngtcp2_buf_reset(ngtcp2_buf *buf); * written to the underlying buffer. In other words, it returns * buf->end - buf->last. */ -#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last) +static inline size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->last); +} /* * ngtcp2_buf_len returns the number of bytes left to read. In other * words, it returns buf->last - buf->pos. */ -#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) +static inline size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { + return (size_t)(buf->last - buf->pos); +} /* * ngtcp2_buf_cap returns the capacity of the buffer. In other words, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c new file mode 100644 index 00000000000000..7e77186772b762 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.c @@ -0,0 +1,72 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * 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_callbacks.h" + +#include +#include + +#include "ngtcp2_unreachable.h" + +static void callbacks_copy(ngtcp2_callbacks *dest, const ngtcp2_callbacks *src, + int callbacks_version) { + assert(callbacks_version != NGTCP2_CALLBACKS_VERSION); + + memcpy(dest, src, ngtcp2_callbackslen_version(callbacks_version)); +} + +const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest( + ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src) { + if (callbacks_version == NGTCP2_CALLBACKS_VERSION) { + return src; + } + + memset(dest, 0, sizeof(*dest)); + + callbacks_copy(dest, src, callbacks_version); + + return dest; +} + +void ngtcp2_callbacks_convert_to_old(int callbacks_version, + ngtcp2_callbacks *dest, + const ngtcp2_callbacks *src) { + assert(callbacks_version != NGTCP2_CALLBACKS_VERSION); + + callbacks_copy(dest, src, callbacks_version); +} + +size_t ngtcp2_callbackslen_version(int callbacks_version) { + ngtcp2_callbacks callbacks; + + switch (callbacks_version) { + case NGTCP2_CALLBACKS_VERSION: + return sizeof(callbacks); + case NGTCP2_CALLBACKS_V1: + return offsetof(ngtcp2_callbacks, tls_early_data_rejected) + + sizeof(callbacks.tls_early_data_rejected); + default: + ngtcp2_unreachable(); + } +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h new file mode 100644 index 00000000000000..751766bb83e5fa --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_callbacks.h @@ -0,0 +1,73 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF 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_CALLBACKS_H +#define NGTCP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * ngtcp2_callbacks_convert_to_latest converts |src| of version + * |callbacks_version| to the latest version NGTCP2_CALLBACKS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |callbacks_version| == NGTCP2_CALLBACKS_VERSION, no conversion + * is made, and |src| is returned. Otherwise, first |dest| is + * zero-initialized, and then all valid fields in |src| are copied + * into |dest|. Finally, |dest| is returned. + */ +const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest( + ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src); + +/* + * ngtcp2_callbacks_convert_to_old converts |src| of the latest + * version to |dest| of version |callbacks_version|. + * + * |callbacks_version| must not be the latest version + * NGTCP2_CALLBACKS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version + * |callbacks_version| from |src| to |dest|. + */ +void ngtcp2_callbacks_convert_to_old(int callbacks_version, + ngtcp2_callbacks *dest, + const ngtcp2_callbacks *src); + +/* + * ngtcp2_callbackslen_version returns the effective length of + * ngtcp2_callbacks at the version |callbacks_version|. + */ +size_t ngtcp2_callbackslen_version(int callbacks_version); + +#endif /* !defined(NGTCP2_CALLBACKS_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c index 508a5d9ec1d8a7..c16953802afd2a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -59,14 +59,16 @@ ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; } void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) { - memset(reno, 0, sizeof(*reno)); - - reno->cc.log = log; - reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked; - reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event; - reno->cc.on_persistent_congestion = - ngtcp2_cc_reno_cc_on_persistent_congestion; - reno->cc.reset = ngtcp2_cc_reno_cc_reset; + *reno = (ngtcp2_cc_reno){ + .cc = + { + .log = log, + .on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked, + .congestion_event = ngtcp2_cc_reno_cc_congestion_event, + .on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion, + .reset = ngtcp2_cc_reno_cc_reset, + }, + }; reno_cc_reset(reno); } @@ -148,11 +150,10 @@ void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, static void cubic_vars_reset(ngtcp2_cubic_vars *v) { v->cwnd_prior = 0; v->w_max = 0; - v->k = 0; + v->k_m = 0; v->epoch_start = UINT64_MAX; v->w_est = 0; - v->state = NGTCP2_CUBIC_STATE_INITIAL; v->app_limited_start_ts = UINT64_MAX; v->app_limited_duration = 0; v->pending_bytes_delivered = 0; @@ -177,17 +178,18 @@ static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) { void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log, ngtcp2_rst *rst) { - memset(cubic, 0, sizeof(*cubic)); - - cubic->cc.log = log; - cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; - cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event; - cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion; - cubic->cc.on_persistent_congestion = - ngtcp2_cc_cubic_cc_on_persistent_congestion; - cubic->cc.reset = ngtcp2_cc_cubic_cc_reset; - - cubic->rst = rst; + *cubic = (ngtcp2_cc_cubic){ + .cc = + { + .log = log, + .on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv, + .congestion_event = ngtcp2_cc_cubic_cc_congestion_event, + .on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion, + .on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion, + .reset = ngtcp2_cc_cubic_cc_reset, + }, + .rst = rst, + }; cubic_cc_reset(cubic); } @@ -228,28 +230,42 @@ static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic, const ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_duration t = ts - cubic->current.epoch_start; + uint64_t tx_m = (t << 10) / NGTCP2_SECONDS; + int neg = tx_m < cubic->current.k_m; + uint64_t time_delta_m; uint64_t delta; - uint64_t tx = (t << 10) / NGTCP2_SECONDS; - uint64_t kx = (cubic->current.k << 10) / NGTCP2_SECONDS; - uint64_t time_delta; - if (tx < kx) { - return UINT64_MAX; + /* Avoid signed bit-shift */ + if (neg) { + time_delta_m = cubic->current.k_m - tx_m; + } else { + time_delta_m = tx_m - cubic->current.k_m; } - time_delta = tx - kx; + time_delta_m = ngtcp2_min_uint64(time_delta_m, 3600 << 10); + + delta = ((((time_delta_m * time_delta_m) >> 10) * time_delta_m) >> 10) * + cstat->max_tx_udp_payload_size * 4 / 10; + delta >>= 10; - delta = cstat->max_tx_udp_payload_size * - ((((time_delta * time_delta) >> 10) * time_delta) >> 10) * 4 / 10; + if (neg) { + if (cubic->current.w_max < delta) { + /* Negative w_cubic is not interesting. */ + return 0; + } + + return cubic->current.w_max - delta; + } - return cubic->current.w_max + (delta >> 10); + return cubic->current.w_max + delta; } void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) { ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); - uint64_t w_cubic, w_cubic_next, target, m; + uint64_t w_cubic, w_cubic_next; + uint64_t target, m; ngtcp2_duration rtt_thresh; int round_start; int is_app_limited = @@ -259,43 +275,27 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, return; } - if (cubic->current.state == NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE) { - if (is_app_limited) { - if (cubic->current.app_limited_start_ts == UINT64_MAX) { - cubic->current.app_limited_start_ts = ts; - } - - return; - } + if (cstat->cwnd < cstat->ssthresh) { + /* slow-start */ + round_start = ack->pkt_delivered >= cubic->next_round_delivered; + if (round_start) { + cubic->next_round_delivered = cubic->rst->delivered; - if (cubic->current.app_limited_start_ts != UINT64_MAX) { - cubic->current.app_limited_duration += - ts - cubic->current.app_limited_start_ts; - cubic->current.app_limited_start_ts = UINT64_MAX; + cubic->rst->is_cwnd_limited = 0; } - } else if (is_app_limited) { - return; - } - - round_start = ack->pkt_delivered >= cubic->next_round_delivered; - if (round_start) { - cubic->next_round_delivered = cubic->rst->delivered; - cubic->rst->is_cwnd_limited = 0; - } + if (!is_app_limited) { + if (cubic->hs.css_round) { + cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR; + } else { + cstat->cwnd += ack->bytes_delivered; + } - if (cstat->cwnd < cstat->ssthresh) { - /* slow-start */ - if (cubic->hs.css_round) { - cstat->cwnd += ack->bytes_delivered / NGTCP2_HS_CSS_GROWTH_DIVISOR; - } else { - cstat->cwnd += ack->bytes_delivered; + ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, + "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64, + ack->bytes_delivered, cstat->cwnd); } - ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, - "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64, - ack->bytes_delivered, cstat->cwnd); - if (round_start) { cubic->hs.last_round_min_rtt = cubic->hs.current_round_min_rtt; cubic->hs.current_round_min_rtt = UINT64_MAX; @@ -321,7 +321,11 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "HyStart++ exit slow start"); + cubic->current.epoch_start = ts; + cubic->current.w_max = cstat->cwnd; cstat->ssthresh = cstat->cwnd; + cubic->current.cwnd_prior = cstat->cwnd; + cubic->current.w_est = cstat->cwnd; } return; @@ -347,20 +351,18 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, } /* congestion avoidance */ + if (is_app_limited) { + if (cubic->current.app_limited_start_ts == UINT64_MAX) { + cubic->current.app_limited_start_ts = ts; + } - switch (cubic->current.state) { - case NGTCP2_CUBIC_STATE_INITIAL: - m = cstat->max_tx_udp_payload_size * ack->bytes_delivered + - cubic->current.pending_bytes_delivered; - cstat->cwnd += m / cstat->cwnd; - cubic->current.pending_bytes_delivered = m % cstat->cwnd; return; - case NGTCP2_CUBIC_STATE_RECOVERY: - cubic->current.state = NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE; - cubic->current.epoch_start = ts; - break; - default: - break; + } + + if (cubic->current.app_limited_start_ts != UINT64_MAX) { + cubic->current.app_limited_duration += + ts - cubic->current.app_limited_start_ts; + cubic->current.app_limited_start_ts = UINT64_MAX; } w_cubic = cubic_cc_compute_w_cubic(cubic, cstat, @@ -369,7 +371,7 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic, cstat, ts - cubic->current.app_limited_duration + cstat->smoothed_rtt); - if (w_cubic_next == UINT64_MAX || w_cubic_next < cstat->cwnd) { + if (w_cubic_next < cstat->cwnd) { target = cstat->cwnd; } else if (2 * w_cubic_next > 3 * cstat->cwnd) { target = cstat->cwnd * 3 / 2; @@ -387,19 +389,19 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_est += m / cstat->cwnd; } - if (w_cubic == UINT64_MAX || cubic->current.w_est > w_cubic) { + if (cubic->current.w_est > w_cubic) { cstat->cwnd = cubic->current.w_est; } else { m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size + cubic->current.pending_bytes_delivered; - cstat->cwnd += m / cstat->cwnd; cubic->current.pending_bytes_delivered = m % cstat->cwnd; + cstat->cwnd += m / cstat->cwnd; } ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64 - " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64, - ack->bytes_delivered, cstat->cwnd, cubic->current.k, target, + " k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64, + ack->bytes_delivered, cstat->cwnd, cubic->current.k_m, target, cubic->current.w_est); } @@ -409,6 +411,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc); uint64_t flight_size; + uint64_t cwnd_delta; if (in_congestion_recovery(cstat, sent_ts)) { return; @@ -422,8 +425,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cstat->congestion_recovery_start_ts = ts; - cubic->current.state = NGTCP2_CUBIC_STATE_RECOVERY; - cubic->current.epoch_start = UINT64_MAX; + cubic->current.epoch_start = ts; cubic->current.app_limited_start_ts = UINT64_MAX; cubic->current.app_limited_duration = 0; cubic->current.pending_bytes_delivered = 0; @@ -435,13 +437,16 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_max = cstat->cwnd; } + cubic->current.w_max = + ngtcp2_max_uint64(cubic->current.w_max, 2 * cstat->max_tx_udp_payload_size); + cstat->ssthresh = cstat->cwnd * 7 / 10; if (cubic->rst->rs.delivered * 2 < cstat->cwnd) { flight_size = cstat->bytes_in_flight + bytes_lost; cstat->ssthresh = ngtcp2_min_uint64( cstat->ssthresh, - ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size) * 7 / 10); + ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size)); } cstat->ssthresh = @@ -452,15 +457,12 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, cubic->current.w_est = cstat->cwnd; - if (cstat->cwnd < cubic->current.w_max) { - cubic->current.k = - ngtcp2_cbrt(((cubic->current.w_max - cstat->cwnd) << 10) * 10 / 4 / - cstat->max_tx_udp_payload_size) * - NGTCP2_SECONDS; - cubic->current.k >>= 10; - } else { - cubic->current.k = 0; - } + assert(cubic->current.w_max >= cstat->cwnd); + + cwnd_delta = cubic->current.w_max - cstat->cwnd; + + cubic->current.k_m = + ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size); ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA, "reduce cwnd because of packet loss cwnd=%" PRIu64, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h index e3c363a51bb85a..e4e19e1aa6b945 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h @@ -326,27 +326,14 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); -typedef enum ngtcp2_cubic_state { - /* NGTCP2_CUBIC_STATE_INITIAL is the state where CUBIC is in slow - start phase, or congestion avoidance phase before congestion - events occur. */ - NGTCP2_CUBIC_STATE_INITIAL, - /* NGTCP2_CUBIC_STATE_RECOVERY is the state that a connection is in - recovery period. */ - NGTCP2_CUBIC_STATE_RECOVERY, - /* NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE is the state where CUBIC - is in congestion avoidance phase after recovery period ends. */ - NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE, -} ngtcp2_cubic_state; - typedef struct ngtcp2_cubic_vars { uint64_t cwnd_prior; uint64_t w_max; - ngtcp2_duration k; + /* CUBIC K with 10 bits extra precision. */ + uint64_t k_m; ngtcp2_tstamp epoch_start; uint64_t w_est; - ngtcp2_cubic_state state; /* app_limited_start_ts is the timestamp where app limited period started. */ ngtcp2_tstamp app_limited_start_ts; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c index 8bff06c0c1f2da..acbee78aaf435f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -149,3 +149,9 @@ int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, ? 0 : NGTCP2_ERR_INVALID_ARGUMENT; } + +void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid, + const ngtcp2_path_history_entry *ent) { + dcid->flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + dcid->max_udp_payload_size = ent->max_udp_payload_size; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h index c6ab16831a38c4..9321cfb64e6daf 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -176,4 +176,24 @@ int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, const ngtcp2_path *path, const uint8_t *token); +/* TODO It might be performance win if we store congestion state in + this entry, and restore it when migrate back to this path. */ +typedef struct ngtcp2_path_history_entry { + /* ps contains path. */ + ngtcp2_path_storage ps; + /* max_udp_payload_size is the maximum size of UDP datagram payload + that is allowed to be sent to this path. */ + size_t max_udp_payload_size; + /* ts is the timestamp when this entry is added to the path history. + It happens when a local endpoint migrates to the another path. */ + ngtcp2_tstamp ts; +} ngtcp2_path_history_entry; + +/* + * ngtcp2_dcid_apply_validated_path applies the defaults from |ent| + * which contains the validated path and its stored configurations. + */ +void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid, + const ngtcp2_path_history_entry *ent); + #endif /* !defined(NGTCP2_CID_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index 59eb90f6ea1afa..393c2281f1ae13 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -39,6 +39,7 @@ #include "ngtcp2_net.h" #include "ngtcp2_transport_params.h" #include "ngtcp2_settings.h" +#include "ngtcp2_callbacks.h" #include "ngtcp2_tstamp.h" #include "ngtcp2_frame_chain.h" @@ -283,6 +284,33 @@ static int conn_call_remove_connection_id(ngtcp2_conn *conn, return 0; } +static int conn_call_begin_path_validation(ngtcp2_conn *conn, + const ngtcp2_pv *pv) { + int rv; + uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; + const ngtcp2_path *old_path = NULL; + + if (!pv || !conn->callbacks.begin_path_validation) { + return 0; + } + + if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) { + flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { + old_path = &pv->fallback_dcid.ps.path; + } + + rv = conn->callbacks.begin_path_validation(conn, flags, &pv->dcid.ps.path, + old_path, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, ngtcp2_path_validation_result res) { int rv; @@ -668,13 +696,13 @@ static int conn_call_recv_tx_key(ngtcp2_conn *conn, return 0; } +// pktns_init initializes |pktns|. It assumes that the object pointed +// by |pktns| is zero-cleared. static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num, ngtcp2_log *log, ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { - memset(pktns, 0, sizeof(*pktns)); - ngtcp2_gaptr_init(&pktns->rx.pngap, mem); pktns->tx.last_pkt_num = initial_pkt_num - 1; @@ -696,7 +724,7 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, ngtcp2_log *log, ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc, ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) { - *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); + *ppktns = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_pktns)); if (*ppktns == NULL) { return NGTCP2_ERR_NOMEM; } @@ -799,7 +827,7 @@ static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, cstat->congestion_recovery_start_ts = UINT64_MAX; cstat->bytes_in_flight = 0; cstat->delivery_rate_sec = 0; - cstat->pacing_interval = 0; + cstat->pacing_interval_m = 0; cstat->send_quantum = 64 * 1024; } @@ -1032,6 +1060,56 @@ conn_set_local_transport_params(ngtcp2_conn *conn, p->version_info_present = 1; } +static void conn_update_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + uint8_t gap; + + conn->callbacks.rand(&gap, 1, &conn->local.settings.rand_ctx); + + pktns->tx.skip_pkt.next_pkt_num = + pktns->tx.last_pkt_num + 3 + + (int64_t)gap * (1ll << pktns->tx.skip_pkt.exponent++); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64, + pktns->tx.skip_pkt.next_pkt_num); +} + +static int conn_handle_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *rtbent; + ngtcp2_pkt_hd hd; + int rv; + + assert(NGTCP2_PKTNS_ID_APPLICATION == pktns->id); + + if (pktns->tx.last_pkt_num + 1 != pktns->tx.skip_pkt.next_pkt_num) { + return 0; + } + + ngtcp2_pkt_hd_init(&hd, 0, NGTCP2_PKT_1RTT, NULL, NULL, + pktns->tx.skip_pkt.next_pkt_num, 0, 0); + + rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, 0, + NGTCP2_RTB_ENTRY_FLAG_SKIP, + &conn->rtb_entry_objalloc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_rtb_add(&pktns->rtb, rtbent, &conn->cstat); + if (rv != 0) { + ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc, + &conn->frc_objalloc, conn->mem); + return rv; + } + + ++pktns->tx.last_pkt_num; + + conn_update_skip_pkt(conn, pktns); + + return 0; +} + static size_t buflen_align(size_t buflen) { return (buflen + 0x7) & (size_t)~0x7; } @@ -1059,13 +1137,16 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, uint32_t *preferred_versions; ngtcp2_settings settingsbuf; ngtcp2_transport_params paramsbuf; - (void)callbacks_version; + ngtcp2_callbacks callbacksbuf; + uint64_t map_seed; (void)settings_version; settings = ngtcp2_settings_convert_to_latest(&settingsbuf, settings_version, settings); params = ngtcp2_transport_params_convert_to_latest( ¶msbuf, transport_params_version, params); + callbacks = ngtcp2_callbacks_convert_to_latest(&callbacksbuf, + callbacks_version, callbacks); assert(settings->max_window <= NGTCP2_MAX_VARINT); assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); @@ -1167,7 +1248,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); - ngtcp2_map_init(&(*pconn)->strms, mem); + callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed), &settings->rand_ctx); + ngtcp2_map_init(&(*pconn)->strms, map_seed, mem); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); @@ -1251,6 +1333,10 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_unreachable(); } + ngtcp2_static_ringbuf_path_history_init(&(*pconn)->path_history); + + (*pconn)->callbacks = *callbacks; + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, &(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log, &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, @@ -1272,6 +1358,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, &(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem); + conn_update_skip_pkt(*pconn, &(*pconn)->pktns); + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); if (scident == NULL) { rv = NGTCP2_ERR_NOMEM; @@ -1378,7 +1466,6 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->keep_alive.timeout = UINT64_MAX; (*pconn)->oscid = *scid; - (*pconn)->callbacks = *callbacks; (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; @@ -2211,7 +2298,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, if (num_reclaimed < 0) { ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); - return rv; + return num_reclaimed; } if (num_reclaimed) { goto build_pkt; @@ -2220,12 +2307,11 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, those packets have been acknowledged (i.e., retransmission in another packet). For server, in this case, we don't have to send any probe packet. Client needs to send probe packets - until it knows that server has completed address validation or - handshake has been confirmed. */ + until it knows that server has completed address + validation. */ if (pktns->rtb.num_pto_eliciting == 0 && (conn->server || - (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | - NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { + (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { pktns->rtb.probe_pkt_left = 0; ngtcp2_conn_set_loss_detection_timer(conn, ts); /* TODO If packet is empty, we should return now if cwnd is @@ -3036,6 +3122,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_strm *strm; int pkt_empty = 1; uint64_t ndatalen = 0; + uint64_t wdatalen; int send_stream = 0; int stream_blocked = 0; int send_datagram = 0; @@ -3180,6 +3267,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, nfrc->fr.max_data.max_data; } + rv = conn_handle_skip_pkt(conn, pktns, ts); + if (rv != 0) { + return rv; + } + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), version); @@ -3217,6 +3309,17 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, require_padding = require_padding || !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE; /* We don't retransmit PATH_RESPONSE. */ + + /* Include PING to make a packet non-probing as per + https://datatracker.ietf.org/doc/html/rfc9000#section-9.3.3 + + An endpoint that receives a PATH_CHALLENGE on an active + path SHOULD send a non-probing packet in response. */ + lfr.type = NGTCP2_FRAME_PING; + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } } } } @@ -3681,7 +3784,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1); if (num_reclaimed < 0) { - return rv; + return num_reclaimed; } if (num_reclaimed) { goto build_pkt; @@ -3704,15 +3807,18 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, rtb_entry_flags |= conn->pkt.rtb_entry_flags; pkt_empty = conn->pkt.pkt_empty; hd_logged = conn->pkt.hd_logged; + require_padding = conn->pkt.require_padding; } left = ngtcp2_ppe_left(ppe); if (*pfrc == NULL && send_stream && ngtcp2_pq_empty(&conn->tx.strmq) && - (ndatalen = ngtcp2_pkt_stream_max_datalen( + (wdatalen = ngtcp2_pkt_stream_max_datalen( vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, left)) != (size_t)-1 && - (ndatalen || datalen == 0)) { + (wdatalen == ndatalen || wdatalen >= NGTCP2_MIN_STREAM_DATALEN) && + (wdatalen || datalen == 0)) { + ndatalen = wdatalen; datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, vmsg->stream.datacnt, (size_t)ndatalen); @@ -3898,6 +4004,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->pkt.pkt_empty = pkt_empty; conn->pkt.rtb_entry_flags = rtb_entry_flags; conn->pkt.hd_logged = hd_logged; + conn->pkt.require_padding = require_padding; conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; } @@ -3918,6 +4025,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, conn->pkt.pkt_empty = pkt_empty; conn->pkt.rtb_entry_flags = rtb_entry_flags; conn->pkt.hd_logged = hd_logged; + conn->pkt.require_padding = require_padding; conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; assert(vmsg); @@ -3979,7 +4087,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, /* TODO Push STREAM frame back to ngtcp2_strm if there is an error before ngtcp2_rtb_entry is safely created and added. */ - if (require_padding) { + if ((flags & (NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY)) && + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + lfr.padding.len = ngtcp2_ppe_padding_size(ppe, destlen); + } else if (require_padding) { lfr.padding.len = ngtcp2_ppe_dgram_padding(ppe); } else { lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); @@ -4130,6 +4241,12 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( version = conn->negotiated_version; cc.ckm = pktns->crypto.tx.ckm; cc.hp_ctx = pktns->crypto.tx.hp_ctx; + + rv = conn_handle_skip_pkt(conn, pktns, ts); + if (rv != 0) { + return rv; + } + break; default: /* We don't support 0-RTT packet in this function. */ @@ -5033,12 +5150,12 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, /* Just freeing memory is dangerous because we might free twice. */ - rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat); + rv = ngtcp2_rtb_reclaim_on_retry(rtb, conn, &conn->pktns, &conn->cstat); if (rv != 0) { return rv; } - rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat); + rv = ngtcp2_rtb_reclaim_on_retry(in_rtb, conn, in_pktns, &conn->cstat); if (rv != 0) { return rv; } @@ -5100,7 +5217,6 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, pkt_ts, ts); if (num_acked < 0) { - assert(ngtcp2_err_is_fatal((int)num_acked)); return (int)num_acked; } @@ -5432,11 +5548,13 @@ decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp, hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); - for (i = 0; i < hd->pkt_numlen; ++i) { + for (i = 0; i < 4; ++i) { *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; } - hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); + p -= 4; + hd->pkt_num = ngtcp2_get_pkt_num(p, hd->pkt_numlen); + p += hd->pkt_numlen; return p - dest; } @@ -5625,6 +5743,10 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, } } + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); conn_reset_congestion_state(conn, ts); @@ -5699,7 +5821,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, conn->pv = npv; - return 0; + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -6456,6 +6578,7 @@ static int is_unrecoverable_error(int liberr) { case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: case NGTCP2_ERR_TRANSPORT_PARAM: case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE: + case NGTCP2_ERR_INTERNAL: return 1; } @@ -6969,23 +7092,23 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && rx_offset == strm->rx.last_offset; - if (fin || datalen) { - if (fin) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; - } - if (!conn_is_tls_handshake_completed(conn)) { - sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; - } - rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, - (size_t)datalen); - if (rv != 0) { - return rv; - } + assert(fin || datalen); - rv = conn_emit_pending_stream_data(conn, strm, rx_offset); - if (rv != 0) { - return rv; - } + if (fin) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; + } + if (!conn_is_tls_handshake_completed(conn)) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT; + } + rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, + (size_t)datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_stream_data(conn, strm, rx_offset); + if (rv != 0) { + return rv; } } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, @@ -7053,9 +7176,9 @@ static void handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, size_t n) { if ( -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX == UINT64_MAX NGTCP2_MAX_STREAMS < n || -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX == UINT64_MAX */ *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) { *punsent_max_remote_streams = NGTCP2_MAX_STREAMS; } else { @@ -7943,7 +8066,12 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { conn->pv = pv; - return conn_call_activate_dcid(conn, &pv->dcid); + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -8168,13 +8296,14 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, int new_cid_used, ngtcp2_tstamp ts) { ngtcp2_dcid dcid; - ngtcp2_pv *pv; + ngtcp2_pv *pv = NULL; int rv; ngtcp2_duration pto; int require_new_cid; int local_addr_eq; int pref_addr_migration; uint32_t remote_addr_cmp; + const ngtcp2_path_history_entry *validated_path; assert(conn->server); @@ -8198,7 +8327,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, /* Run PMTUD just in case if it is prematurely aborted */ assert(!conn->pmtud); - return conn_start_pmtud(conn); + if (!conn->local.settings.no_pmtud) { + return conn_start_pmtud(conn); + } + + return 0; } remote_addr_cmp = @@ -8268,25 +8401,32 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, dcid.bytes_recv += dgramlen; - pto = conn_compute_pto(conn, &conn->pktns); + validated_path = ngtcp2_conn_find_path_history(conn, path, ts); + if (!validated_path) { + pto = conn_compute_pto(conn, &conn->pktns); - rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), - NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); - if (rv != 0) { - return rv; - } + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + if (rv != 0) { + return rv; + } - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) { - ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid, - conn->pv->fallback_pto); - /* Unset the flag bit so that conn_stop_pv does not retire - DCID. */ - conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; - } else if (!pref_addr_migration) { - ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto); + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) { + ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid, + conn->pv->fallback_pto); + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; + } else if (!pref_addr_migration) { + ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto); + } } - if (!pref_addr_migration) { + if (!pref_addr_migration || validated_path) { + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_CMP_FLAG_ADDR | @@ -8297,6 +8437,17 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn_reset_ecn_validation_state(conn); ngtcp2_conn_stop_pmtud(conn); + + if (validated_path) { + ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path); + + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + } } if (conn->pv) { @@ -8311,7 +8462,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, conn->pv = pv; - return 0; + return conn_call_begin_path_validation(conn, conn->pv); } /* @@ -8485,34 +8636,6 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, return 0; } -/* - * conn_allow_path_change_under_disable_active_migration returns - * nonzero if a packet from |path| is acceptable under - * disable_active_migration is on. - */ -static int -conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn, - const ngtcp2_path *path) { - uint32_t remote_addr_cmp; - - assert(conn->server); - assert(conn->local.transport_params.disable_active_migration); - - /* If local address does not change, it must be passive migration - (NAT rebinding). */ - if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) { - remote_addr_cmp = - ngtcp2_addr_cmp(&conn->dcid.current.ps.path.remote, &path->remote); - - return (remote_addr_cmp | NGTCP2_ADDR_CMP_FLAG_PORT) == - NGTCP2_ADDR_CMP_FLAG_PORT; - } - - /* If local address changes, it must be one of the preferred - addresses. */ - return conn_server_preferred_addr_migration(conn, &path->local); -} - /* * conn_recv_pkt processes a packet contained in the buffer pointed by * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. @@ -8577,30 +8700,6 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, int path_challenge_recved = 0; size_t num_ack_processed = 0; - if (conn->server) { - if (conn->local.transport_params.disable_active_migration && - !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && - !conn_allow_path_change_under_disable_active_migration(conn, path)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "packet is discarded because local active migration is disabled"); - - return NGTCP2_ERR_DISCARD_PKT; - } - - assert(conn->remote.transport_params); - - if (conn->remote.transport_params->disable_active_migration && - !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) && - !conn_server_preferred_addr_migration(conn, &path->local)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_PKT, - "packet is discarded because remote active migration is disabled"); - - return NGTCP2_ERR_DISCARD_PKT; - } - } - if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); if (nread < 0) { @@ -9380,6 +9479,15 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->dcid.current.bytes_recv += dgramlen; } + if (conn->server && conn->local.transport_params.disable_active_migration && + !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) && + !conn_server_preferred_addr_migration(conn, &path->local)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet is discarded because active migration is disabled"); + + return 0; + } + while (pktlen) { nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); if (nread < 0) { @@ -9841,7 +9949,7 @@ static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) { */ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - uint64_t write_datalen, + uint8_t wflags, uint64_t write_datalen, ngtcp2_tstamp ts) { int rv; ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; @@ -9874,8 +9982,8 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (pending_early_datalen) { early_spktlen = conn_retransmit_retry_early( conn, pi, dest + nwrite, destlen - (size_t)nwrite, (size_t)nwrite, - nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING - : NGTCP2_WRITE_PKT_FLAG_NONE, + wflags | (nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE), ts); if (early_spktlen < 0) { @@ -9917,7 +10025,15 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, if (pending_early_datalen && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { nwrite = conn_retransmit_retry_early( - conn, pi, dest, destlen, (size_t)res, NGTCP2_WRITE_PKT_FLAG_NONE, ts); + conn, pi, dest, destlen, (size_t)res, + wflags | ((nwrite && + ngtcp2_pkt_get_type_long( + conn->negotiated_version ? conn->negotiated_version + : conn->client_chosen_version, + *(dest - nwrite)) == NGTCP2_PKT_INITIAL) + ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING + : NGTCP2_WRITE_PKT_FLAG_NONE), + ts); if (nwrite < 0) { return nwrite; } @@ -10069,17 +10185,15 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, * pointed by |dest| if it succeeds, or one of the following negative * error codes: (TBD). */ -static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, - ngtcp2_pkt_info *pi, - uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { +static ngtcp2_ssize +conn_client_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, uint8_t wflags, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { int send_stream = 0; int send_datagram = 0; ngtcp2_ssize spktlen, early_spktlen; uint64_t datalen; uint64_t write_datalen = 0; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; uint32_t version; @@ -10120,7 +10234,8 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, } if (!ppe_pending) { - spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts); + spktlen = + conn_write_handshake(conn, pi, dest, destlen, wflags, write_datalen, ts); if (spktlen < 0) { return spktlen; @@ -10138,16 +10253,12 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, if (spktlen > 0 && ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) { wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - conn->pkt.require_padding = 1; } } else { assert(!conn->pktns.crypto.rx.ckm); assert(!conn->pktns.crypto.tx.ckm); assert(conn->early.ckm); - if (conn->pkt.require_padding) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } spktlen = conn->pkt.hs_spktlen; } @@ -10736,7 +10847,7 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { if (conn->in_pktns) { ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, conn->in_pktns); + ts += conn_compute_pto(conn, conn->in_pktns) * 3; res = ngtcp2_min_uint64(res, ts); } } @@ -10744,14 +10855,14 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { if (conn->hs_pktns) { ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, conn->hs_pktns); + ts += conn_compute_pto(conn, conn->hs_pktns) * 3; res = ngtcp2_min_uint64(res, ts); } } ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); if (ts != UINT64_MAX) { - ts += conn_compute_pto(conn, &conn->pktns); + ts += conn_compute_pto(conn, &conn->pktns) * 3; res = ngtcp2_min_uint64(res, ts); } @@ -10759,18 +10870,18 @@ ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { } void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_duration pto; + ngtcp2_duration timeout; if (conn->in_pktns) { - pto = conn_compute_pto(conn, conn->in_pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts); + timeout = conn_compute_pto(conn, conn->in_pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, timeout, ts); } if (conn->hs_pktns) { - pto = conn_compute_pto(conn, conn->hs_pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts); + timeout = conn_compute_pto(conn, conn->hs_pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, timeout, ts); } - pto = conn_compute_pto(conn, &conn->pktns); - ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); + timeout = conn_compute_pto(conn, &conn->pktns) * 3; + ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, timeout, ts); } /* @@ -11315,27 +11426,34 @@ ngtcp2_ssize ngtcp2_conn_write_stream_versioned( ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen, ngtcp2_tstamp ts) { - ngtcp2_vec datav; + ngtcp2_vec datav, *v; + size_t datacnt; - datav.len = datalen; - datav.base = (uint8_t *)data; + if (datalen == 0) { + v = NULL; + datacnt = 0; + } else { + datav.len = datalen; + datav.base = (uint8_t *)data; + v = &datav; + datacnt = 1; + } return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi, dest, destlen, pdatalen, flags, - stream_id, &datav, 1, ts); + stream_id, v, datacnt, ts); } -static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, - ngtcp2_path *path, - int pkt_info_version, - ngtcp2_pkt_info *pi, uint8_t *dest, - size_t destlen, ngtcp2_vmsg *vmsg, - ngtcp2_tstamp ts) { +static ngtcp2_ssize +conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path, + int pkt_info_version, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, uint8_t wflags, + ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize nwrite; nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest, - destlen, vmsg, ts); + destlen, wflags, vmsg, ts); if (nwrite < 0) { return nwrite; } @@ -11364,6 +11482,7 @@ ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( ngtcp2_vmsg vmsg, *pvmsg; ngtcp2_strm *strm; int64_t datalen; + uint8_t wflags; if (pdatalen) { *pdatalen = -1; @@ -11406,8 +11525,14 @@ ngtcp2_ssize ngtcp2_conn_writev_stream_versioned( pvmsg = NULL; } + if (flags & NGTCP2_WRITE_STREAM_FLAG_PADDING) { + wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY; + } else { + wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + } + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, - destlen, pvmsg, ts); + destlen, wflags, pvmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( @@ -11415,14 +11540,22 @@ ngtcp2_ssize ngtcp2_conn_write_datagram_versioned( ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted, uint32_t flags, uint64_t dgram_id, const uint8_t *data, size_t datalen, ngtcp2_tstamp ts) { - ngtcp2_vec datav; + ngtcp2_vec datav, *v; + size_t datacnt; - datav.len = datalen; - datav.base = (uint8_t *)data; + if (datalen == 0) { + v = NULL; + datacnt = 0; + } else { + datav.len = datalen; + datav.base = (uint8_t *)data; + v = &datav; + datacnt = 1; + } return ngtcp2_conn_writev_datagram_versioned(conn, path, pkt_info_version, pi, dest, destlen, paccepted, flags, - dgram_id, &datav, 1, ts); + dgram_id, v, datacnt, ts); } ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( @@ -11432,6 +11565,7 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( ngtcp2_tstamp ts) { ngtcp2_vmsg vmsg; int64_t datalen; + uint8_t wflags; if (paccepted) { *paccepted = 0; @@ -11444,9 +11578,9 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( datalen = ngtcp2_vec_len_varint(datav, datavcnt); if (datalen == -1 -#if UINT64_MAX > SIZE_MAX +#if SIZE_MAX < UINT64_MAX || (uint64_t)datalen > SIZE_MAX -#endif /* UINT64_MAX > SIZE_MAX */ +#endif /* SIZE_MAX < UINT64_MAX */ ) { return NGTCP2_ERR_INVALID_ARGUMENT; } @@ -11463,19 +11597,25 @@ ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned( vmsg.datagram.datacnt = datavcnt; vmsg.datagram.paccepted = paccepted; + if (flags & NGTCP2_WRITE_DATAGRAM_FLAG_PADDING) { + wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY; + } else { + wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + } + return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest, - destlen, &vmsg, ts); + destlen, wflags, &vmsg, ts); } ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) { + uint8_t wflags, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; size_t origlen; size_t origdestlen = destlen; int rv; - uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; ngtcp2_conn_stat *cstat = &conn->cstat; ngtcp2_ssize res = 0; @@ -11507,7 +11647,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); } - nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); + nwrite = + conn_client_write_handshake(conn, pi, dest, destlen, wflags, vmsg, ts); /* We might be unable to write a packet because of depletion of congestion window budget, perhaps due to packet loss that shrinks the window drastically. */ @@ -11575,7 +11716,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, } } - nwrite = conn_write_handshake(conn, pi, dest, destlen, + nwrite = conn_write_handshake(conn, pi, dest, destlen, wflags, /* write_datalen = */ 0, ts); if (nwrite < 0) { return nwrite; @@ -11651,9 +11792,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (ppe_pending) { res = conn->pkt.hs_spktlen; - if (conn->pkt.require_padding) { - wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; - } /* dest and destlen have already been adjusted in ppe in the first run. They are adjusted for probe packet later. */ nwrite = conn_write_pkt(conn, pi, dest, destlen, (size_t)res, vmsg, @@ -12980,6 +13118,7 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, int rv; ngtcp2_dcid dcid; ngtcp2_pv *pv; + const ngtcp2_path_history_entry *validated_path; assert(!conn->server); @@ -13007,24 +13146,45 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); ngtcp2_dcid_set_path(&dcid, path); + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts); + } + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); - /* TODO It might be better to add a new flag which indicates that a - connection should be closed if this path validation failed. The - current design allows an application to continue, by migrating - into yet another path. */ - rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), - NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + validated_path = ngtcp2_conn_find_path_history(conn, path, ts); + if (validated_path) { + ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path); + + if (!conn->local.settings.no_pmtud) { + rv = conn_start_pmtud(conn); + if (rv != 0) { + return rv; + } + } + } else { + /* TODO It might be better to add a new flag which indicates that + a connection should be closed if this path validation failed. + The current design allows an application to continue, by + migrating into yet another path. */ + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + if (rv != 0) { + return rv; + } + + conn->pv = pv; + } + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { return rv; } - conn->pv = pv; - - return conn_call_activate_dcid(conn, &conn->dcid.current); + return conn_call_begin_path_validation(conn, conn->pv); } int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, @@ -13035,6 +13195,10 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, assert(!conn->server); + if (ngtcp2_conn_find_path_history(conn, path, ts)) { + return ngtcp2_conn_initiate_immediate_migration(conn, path, ts); + } + conn_update_timestamp(conn, ts); rv = conn_initiate_migration_precheck(conn, &path->local); @@ -13060,7 +13224,12 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, conn->pv = pv; - return conn_call_activate_dcid(conn, &pv->dcid); + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + + return conn_call_begin_path_validation(conn, conn->pv); } uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { @@ -13237,7 +13406,7 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, } void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_duration pacing_interval; + uint64_t pacing_interval_m; ngtcp2_duration wait, d; conn_update_timestamp(conn, ts); @@ -13246,24 +13415,24 @@ void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return; } - if (conn->cstat.pacing_interval) { - pacing_interval = conn->cstat.pacing_interval; + if (conn->cstat.pacing_interval_m) { + pacing_interval_m = conn->cstat.pacing_interval_m; } else { /* 1.25 is the under-utilization avoidance factor described in https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */ - pacing_interval = (conn->cstat.first_rtt_sample_ts == UINT64_MAX - ? NGTCP2_MILLISECONDS - : conn->cstat.smoothed_rtt) * - 100 / 125 / conn->cstat.cwnd; + pacing_interval_m = ((conn->cstat.first_rtt_sample_ts == UINT64_MAX + ? NGTCP2_MILLISECONDS + : conn->cstat.smoothed_rtt) + << 10) * + 100 / 125 / conn->cstat.cwnd; + pacing_interval_m = ngtcp2_max_uint64(pacing_interval_m, 1); } - wait = (ngtcp2_duration)(conn->tx.pacing.pktlen * pacing_interval); + wait = (ngtcp2_duration)((conn->tx.pacing.pktlen * pacing_interval_m) >> 10); - if (conn->tx.pacing.compensation >= NGTCP2_MILLISECONDS) { - d = ngtcp2_min_uint64(wait, conn->tx.pacing.compensation); - wait -= d; - conn->tx.pacing.compensation -= d; - } + d = ngtcp2_min_uint64(wait / 2, conn->tx.pacing.compensation); + wait -= d; + conn->tx.pacing.compensation -= d; conn->tx.pacing.next_ts = ts + wait; conn->tx.pacing.pktlen = 0; @@ -13283,6 +13452,37 @@ size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { return strm->tx.loss_count; } +void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts) { + ngtcp2_path_history_entry *ent; + + ent = ngtcp2_ringbuf_push_front(&conn->path_history.rb); + ngtcp2_path_storage_init2(&ent->ps, &dcid->ps.path); + ent->max_udp_payload_size = dcid->max_udp_payload_size; + ent->ts = ts; +} + +const ngtcp2_path_history_entry * +ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + ngtcp2_ringbuf *rb = &conn->path_history.rb; + size_t i, len = ngtcp2_ringbuf_len(rb); + ngtcp2_path_history_entry *ent; + + for (i = 0; i < len; ++i) { + ent = ngtcp2_ringbuf_get(rb, i); + if (ngtcp2_tstamp_elapsed(ent->ts, 10 * NGTCP2_MINUTES, ts)) { + return NULL; + } + + if (ngtcp2_path_eq(path, &ent->ps.path)) { + return ent; + } + } + + return NULL; +} + void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, const ngtcp2_path *path, const uint8_t *data) { @@ -13305,7 +13505,7 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( ngtcp2_crypto_km ckm; ngtcp2_crypto_cc cc; ngtcp2_ppe ppe; - ngtcp2_frame fr = {0}; + ngtcp2_frame fr; int rv; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, @@ -13336,11 +13536,12 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( return NGTCP2_ERR_NOBUF; } - fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; - fr.connection_close.error_code = error_code; - fr.connection_close.frame_type = 0; - fr.connection_close.reasonlen = reasonlen; - fr.connection_close.reason = (uint8_t *)reason; + fr.connection_close = (ngtcp2_connection_close){ + .type = NGTCP2_FRAME_CONNECTION_CLOSE, + .error_code = error_code, + .reasonlen = reasonlen, + .reason = (uint8_t *)reason, + }; rv = ngtcp2_ppe_encode_frame(&ppe, &fr); if (rv != 0) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 0ba8d6efcc6ac1..5979d39654b839 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -110,6 +110,9 @@ typedef enum { NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, but it requests to add padding to the full UDP datagram payload size. */ #define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04u +/* NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY adds padding to the QUIC + packet as much as possible if the packet is not empty. */ +#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08u /* * ngtcp2_max_frame is defined so that it covers the largest ACK @@ -203,6 +206,13 @@ typedef struct ngtcp2_pktns { /* last_pkt_num is the packet number which the local endpoint sent last time.*/ int64_t last_pkt_num; + struct { + /* next_pkt_num is the next packet number to skip. */ + int64_t next_pkt_num; + /* exponent makes gap of skipping packets spread + exponentially. */ + int64_t exponent; + } skip_pkt; ngtcp2_frame_chain *frq; /* non_ack_pkt_start_ts is the timestamp since the local endpoint starts sending continuous non ACK-eliciting packets. */ @@ -308,6 +318,8 @@ typedef struct ngtcp2_early_transport_params { ngtcp2_static_ringbuf_def(path_challenge, 4, sizeof(ngtcp2_path_challenge_entry)) +ngtcp2_static_ringbuf_def(path_history, 4, sizeof(ngtcp2_path_history_entry)) + ngtcp2_objalloc_decl(strm, ngtcp2_strm, oplent) struct ngtcp2_conn { @@ -625,6 +637,10 @@ struct ngtcp2_conn { ngtcp2_cc_cubic cubic; ngtcp2_cc_bbr bbr; }; + /* path_history remembers the paths that have been validated + successfully. The path is added to this history when a local + endpoint migrates to the another path. */ + ngtcp2_static_ringbuf_path_history path_history; const ngtcp2_mem *mem; /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; @@ -801,7 +817,8 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, - ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts); + uint8_t wflags, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts); /* * ngtcp2_conn_write_single_frame_pkt writes a packet which contains @@ -1094,4 +1111,11 @@ void ngtcp2_conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); */ void ngtcp2_conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts); +void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts); + +const ngtcp2_path_history_entry * +ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts); + #endif /* !defined(NGTCP2_CONN_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h index ad2b7329f48df2..be041b9086081d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn_stat.h @@ -116,13 +116,13 @@ typedef struct ngtcp2_conn_stat { */ uint64_t delivery_rate_sec; /** - * :member:`pacing_interval` is the inverse of pacing rate, which is - * the current packet sending rate computed by a congestion + * :member:`pacing_interval_m` is the inverse of pacing rate, which + * is the current packet sending rate computed by a congestion * controller. 0 if a congestion controller does not set pacing * interval. Even if this value is set to 0, the library paces - * packets. + * packets. The unit of this value is 1/1024 of nanoseconds. */ - ngtcp2_duration pacing_interval; + uint64_t pacing_interval_m; /** * :member:`send_quantum` is the maximum size of a data aggregate * scheduled and transmitted together. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c index 8a8d7733797176..170b9f803cccec 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c @@ -157,10 +157,11 @@ int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest, return 0; } -static int verify_stateless_reset(const ngtcp2_ringbuf *rb, - const ngtcp2_path *path, - const uint8_t *token) { +int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path, + const uint8_t *token) { const ngtcp2_dcid *dcid; + const ngtcp2_ringbuf *rb = &dtr->bound.rb; size_t i, len = ngtcp2_ringbuf_len(rb); for (i = 0; i < len; ++i) { @@ -173,19 +174,6 @@ static int verify_stateless_reset(const ngtcp2_ringbuf *rb, return NGTCP2_ERR_INVALID_ARGUMENT; } -int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, - const ngtcp2_path *path, - const uint8_t *token) { - int rv; - - rv = verify_stateless_reset(&dtr->retired.rb, path, token); - if (rv == 0) { - return 0; - } - - return verify_stateless_reset(&dtr->bound.rb, path, token); -} - static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound, uint64_t seq, const ngtcp2_cid *cid, const uint8_t *token) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h index 17942389b814d1..63043427bc0900 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h @@ -340,4 +340,4 @@ int ngtcp2_dcidtr_unused_empty(const ngtcp2_dcidtr *dtr); */ int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr); -#endif /* NGTCP2_DCIDTR_H */ +#endif /* !defined(NGTCP2_DCIDTR_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h index de78bcb8070f53..d5c3f61d2acc7b 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -292,8 +292,11 @@ void ngtcp2_ksl_clear(ngtcp2_ksl *ksl); /* * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. */ -#define ngtcp2_ksl_nth_node(KSL, BLK, N) \ - ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) +static inline ngtcp2_ksl_node *ngtcp2_ksl_nth_node(const ngtcp2_ksl *ksl, + const ngtcp2_ksl_blk *blk, + size_t n) { + return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} #ifndef WIN32 /* @@ -315,18 +318,21 @@ void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, * |it| points to. It is undefined to call this function when * ngtcp2_ksl_it_end(it) returns nonzero. */ -#define ngtcp2_ksl_it_get(IT) \ - ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data +static inline void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { + return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} /* * ngtcp2_ksl_it_next advances the iterator by one. It is undefined * if this function is called when ngtcp2_ksl_it_end(it) returns * nonzero. */ -#define ngtcp2_ksl_it_next(IT) \ - (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ - ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ - : 0) +static inline void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) { + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} /* * ngtcp2_ksl_it_prev moves backward the iterator by one. It is @@ -339,8 +345,9 @@ void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it); * ngtcp2_ksl_it_end returns nonzero if |it| points to the one beyond * the last node. */ -#define ngtcp2_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) +static inline int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} /* * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first @@ -354,8 +361,9 @@ int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); * It is undefined to call this function when ngtcp2_ksl_it_end(it) * returns nonzero. */ -#define ngtcp2_ksl_it_key(IT) \ - ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) +static inline ngtcp2_ksl_key *ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) { + return (ngtcp2_ksl_key *)ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->key; +} /* * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c index fc4eb443517d40..e006c22e4e7ff0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -594,9 +594,9 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { return; } - memset(&shd, 0, sizeof(shd)); - - shd.type = NGTCP2_PKT_STATELESS_RESET; + shd = (ngtcp2_pkt_hd){ + .type = NGTCP2_PKT_STATELESS_RESET, + }; log->log_printf( log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c index 5e4726e63ff7ef..246c779f2908ca 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -33,10 +33,11 @@ #define NGTCP2_INITIAL_HASHBITS 4 -void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { +void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; + map->seed = seed; map->size = 0; } @@ -77,8 +78,14 @@ int ngtcp2_map_each(const ngtcp2_map *map, int (*func)(void *data, void *ptr), return 0; } -static size_t hash(ngtcp2_map_key_type key, size_t bits) { - return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +static size_t map_hash(const ngtcp2_map *map, ngtcp2_map_key_type key) { + /* hasher from + https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs + We do not perform finalization here because we use top bits + anyway. */ + key += map->seed; + key *= 0xf1357aea2e62a9c5ull; + return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits)); } static void map_bucket_swap(ngtcp2_map_bucket *a, ngtcp2_map_bucket *b) { @@ -109,28 +116,28 @@ void ngtcp2_map_print_distance(const ngtcp2_map *map) { continue; } - idx = hash(bkt->key, map->hashbits); + idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, - hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); + map_hash(map, bkt->key), bkt->key, idx, bkt->psl); } } #endif /* !defined(WIN32) */ -static int insert(ngtcp2_map_bucket *table, size_t hashbits, - ngtcp2_map_key_type key, void *data) { - size_t idx = hash(key, hashbits); +static int map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { + size_t idx = map_hash(map, key); ngtcp2_map_bucket b = { .key = key, .data = data, }; ngtcp2_map_bucket *bkt; - size_t mask = (1u << hashbits) - 1; + size_t mask = (1u << map->hashbits) - 1; for (;;) { - bkt = &table[idx]; + bkt = &map->table[idx]; if (bkt->data == NULL) { *bkt = b; + ++map->size; return 0; } @@ -151,15 +158,19 @@ static int insert(ngtcp2_map_bucket *table, size_t hashbits, static int map_resize(ngtcp2_map *map, size_t new_hashbits) { size_t i; - ngtcp2_map_bucket *new_table; ngtcp2_map_bucket *bkt; size_t tablelen; int rv; + ngtcp2_map new_map = { + .table = ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, + sizeof(ngtcp2_map_bucket)), + .mem = map->mem, + .seed = map->seed, + .hashbits = new_hashbits, + }; (void)rv; - new_table = - ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(ngtcp2_map_bucket)); - if (new_table == NULL) { + if (new_map.table == NULL) { return NGTCP2_ERR_NOMEM; } @@ -172,15 +183,15 @@ static int map_resize(ngtcp2_map *map, size_t new_hashbits) { continue; } - rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + rv = map_insert(&new_map, bkt->key, bkt->data); assert(0 == rv); } } ngtcp2_mem_free(map->mem, map->table); + map->table = new_map.table; map->hashbits = new_hashbits; - map->table = new_table; return 0; } @@ -190,10 +201,10 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { assert(data); - /* Load factor is 0.75 */ + /* Load factor is 7/8 */ /* Under the very initial condition, that is map->size == 0 and - map->hashbits == 0, 4 > 3 still holds nicely. */ - if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { + map->hashbits == 0, 8 > 7 still holds nicely. */ + if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { if (map->hashbits) { rv = map_resize(map, map->hashbits + 1); if (rv != 0) { @@ -207,13 +218,11 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) { } } - rv = insert(map->table, map->hashbits, key, data); + rv = map_insert(map, key, data); if (rv != 0) { return rv; } - ++map->size; - return 0; } @@ -227,7 +236,7 @@ void *ngtcp2_map_find(const ngtcp2_map *map, ngtcp2_map_key_type key) { return NULL; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { @@ -256,7 +265,7 @@ int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) { return NGTCP2_ERR_INVALID_ARGUMENT; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h index 9d882fb20088d8..0c1a76552aa4c4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -47,6 +47,7 @@ typedef struct ngtcp2_map_bucket { typedef struct ngtcp2_map { ngtcp2_map_bucket *table; const ngtcp2_mem *mem; + uint64_t seed; size_t size; size_t hashbits; } ngtcp2_map; @@ -54,7 +55,7 @@ typedef struct ngtcp2_map { /* * ngtcp2_map_init initializes the map |map|. */ -void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); +void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem); /* * ngtcp2_map_free deallocates any resources allocated for |map|. The diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c index d78978492ca8e7..af8a059d08f7bb 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -304,11 +304,11 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, } ngtcp2_get_uvarint(&vi, p); -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX < UINT64_MAX if (vi > SIZE_MAX) { return NGTCP2_ERR_INVALID_ARGUMENT; } -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX < UINT64_MAX */ longlen = (size_t)vi; } @@ -2080,8 +2080,10 @@ ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, } for (i = 0; i < fr->datacnt; ++i) { - assert(fr->data[i].len); - assert(fr->data[i].base); + if (fr->data[i].len == 0) { + continue; + } + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); } @@ -2421,9 +2423,7 @@ size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, left -= n; if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX len = ngtcp2_min_uint64(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 8)); } @@ -2453,9 +2453,9 @@ size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { left -= n; if (left > 8 + 1073741823 && len > 1073741823) { -#if SIZE_MAX > UINT32_MAX +#if SIZE_MAX == UINT64_MAX len = ngtcp2_min_size(len, 4611686018427387903lu); -#endif /* SIZE_MAX > UINT32_MAX */ +#endif /* SIZE_MAX == UINT64_MAX */ return ngtcp2_min_size(len, left - 8); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 756076e7a7f258..ec72708e0426e7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -137,6 +137,13 @@ v2. */ #define NGTCP2_PKT_TYPE_RETRY_V2 0x0 +/* NGTCP2_MIN_STREAM_DATALEN is the minimum length of STREAM frame to + avoid too small frame. It is not always enforced for various + reasons. For example, due to flow control, we might have fewer + bytes available to send. Therefore, it is only applied when the + length of data to send is larger than this limit. */ +#define NGTCP2_MIN_STREAM_DATALEN 256 + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; uint8_t *token; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c index 13ef2b24908905..4d193125ae8e89 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -47,9 +47,10 @@ void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { ngtcp2_ssize rv; ngtcp2_buf *buf = &ppe->buf; + size_t buf_left = ngtcp2_buf_left(buf); ngtcp2_crypto_cc *cc = ppe->cc; - if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return NGTCP2_ERR_NOBUF; } @@ -62,13 +63,13 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN; - rv = ngtcp2_pkt_encode_hd_long( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + rv = ngtcp2_pkt_encode_hd_long(buf->last, buf_left - cc->aead.max_overhead, + hd); } else { ppe->pkt_num_offset = 1 + hd->dcid.datalen; - rv = ngtcp2_pkt_encode_hd_short( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + rv = ngtcp2_pkt_encode_hd_short(buf->last, buf_left - cc->aead.max_overhead, + hd); } if (rv < 0) { @@ -87,14 +88,14 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { ngtcp2_ssize rv; ngtcp2_buf *buf = &ppe->buf; + size_t buf_left = ngtcp2_buf_left(buf); ngtcp2_crypto_cc *cc = ppe->cc; - if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return NGTCP2_ERR_NOBUF; } - rv = ngtcp2_pkt_encode_frame( - buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr); + rv = ngtcp2_pkt_encode_frame(buf->last, buf_left - cc->aead.max_overhead, fr); if (rv < 0) { return (int)rv; } @@ -172,18 +173,13 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe) { ngtcp2_crypto_cc *cc = ppe->cc; + size_t buf_left = ngtcp2_buf_left(&ppe->buf); - if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) { + if (buf_left <= cc->aead.max_overhead) { return 0; } - return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead; -} - -size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe) { - ngtcp2_crypto_cc *cc = ppe->cc; - - return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead; + return buf_left - cc->aead.max_overhead; } size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { @@ -208,6 +204,10 @@ size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { assert(ngtcp2_buf_left(buf) >= len + cc->aead.max_overhead); + if (len == 0) { + return 0; + } + buf->last = ngtcp2_setmem(buf->last, 0, len); return len; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h index 660b1482b56671..9874b36800540d 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h @@ -110,12 +110,6 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt); */ size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe); -/* - * ngtcp2_ppe_pktlen returns the provisional packet length. It - * includes AEAD overhead. - */ -size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe); - /* * ngtcp2_ppe_dgram_padding is equivalent to call * ngtcp2_ppe_dgram_padding_size(ppe, NGTCP2_MAX_UDP_PAYLOAD_SIZE). diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index d490524805b1e9..73efb5255fcf44 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -110,7 +110,9 @@ void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len); void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset); /* ngtcp2_ringbuf_len returns the number of elements stored. */ -#define ngtcp2_ringbuf_len(RB) ((RB)->len) +static inline size_t ngtcp2_ringbuf_len(const ngtcp2_ringbuf *rb) { + return rb->len; +} /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c index 181691f3e69401..6f44c8ac8de673 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -38,7 +38,6 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { rs->prior_ts = UINT64_MAX; rs->tx_in_flight = 0; rs->lost = 0; - rs->prior_lost = 0; rs->send_elapsed = 0; rs->ack_elapsed = 0; rs->last_end_seq = -1; @@ -58,7 +57,6 @@ void ngtcp2_rst_reset(ngtcp2_rst *rst) { rst->app_limited = 0; rst->is_cwnd_limited = 0; rst->lost = 0; - rst->valid_after_seq = rst->last_seq; } void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, @@ -89,7 +87,6 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { rs->interval = ngtcp2_max_uint64(rs->send_elapsed, rs->ack_elapsed); rs->delivered = rst->delivered - rs->prior_delivered; - rs->lost = rst->lost - rs->prior_lost; if (rs->interval < cstat->min_rtt) { rs->interval = UINT64_MAX; @@ -103,31 +100,23 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; } -static int rst_is_newest_pkt(const ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, - const ngtcp2_rs *rs) { - return ent->ts > rst->first_sent_ts || - (ent->ts == rst->first_sent_ts && ent->rst.end_seq > rs->last_end_seq); +static int is_newest_pkt(const ngtcp2_rtb_entry *ent, const ngtcp2_rs *rs) { + return ent->rst.end_seq > rs->last_end_seq; } void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, ngtcp2_tstamp ts) { ngtcp2_rs *rs = &rst->rs; - if (ent->rst.end_seq <= rst->valid_after_seq) { - return; - } - rst->delivered += ent->pktlen; rst->delivered_ts = ts; - if (rs->prior_ts == UINT64_MAX || rst_is_newest_pkt(rst, ent, rs)) { + if (rs->prior_ts == UINT64_MAX || is_newest_pkt(ent, rs)) { rs->prior_delivered = ent->rst.delivered; rs->prior_ts = ent->rst.delivered_ts; rs->is_app_limited = ent->rst.is_app_limited; rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; - rs->tx_in_flight = ent->rst.tx_in_flight; - rs->prior_lost = ent->rst.lost; rs->last_end_seq = ent->rst.end_seq; rst->first_sent_ts = ent->ts; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index c2580306cc59fe..1bb624de79b53f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -48,7 +48,6 @@ typedef struct ngtcp2_rs { ngtcp2_tstamp prior_ts; uint64_t tx_in_flight; uint64_t lost; - uint64_t prior_lost; ngtcp2_duration send_elapsed; ngtcp2_duration ack_elapsed; int64_t last_end_seq; @@ -73,10 +72,6 @@ typedef struct ngtcp2_rst { across all packet number spaces, we can replace this with a packet number. */ int64_t last_seq; - /* valid_after_seq is the sequence number, and ignore a packet if - the sequence number of the packet is less than or equal to this - number. */ - int64_t valid_after_seq; int is_cwnd_limited; } ngtcp2_rst; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c index f7a7f5724b7ac3..b50f482bc7e8c0 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -43,16 +43,19 @@ ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent) static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd, ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, size_t pktlen, uint16_t flags) { - memset(ent, 0, sizeof(*ent)); - - ent->hd.pkt_num = hd->pkt_num; - ent->hd.type = hd->type; - ent->hd.flags = hd->flags; - ent->frc = frc; - ent->ts = ts; - ent->lost_ts = UINT64_MAX; - ent->pktlen = pktlen; - ent->flags = flags; + *ent = (ngtcp2_rtb_entry){ + .hd = + { + .pkt_num = hd->pkt_num, + .type = hd->type, + .flags = hd->flags, + }, + .frc = frc, + .ts = ts, + .lost_ts = UINT64_MAX, + .pktlen = pktlen, + .flags = flags, + }; } int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent, @@ -102,7 +105,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_rst *rst, ngtcp2_cc *cc, rtb->cc_pkt_num = cc_pkt_num; rtb->cc_bytes_in_flight = 0; rtb->num_lost_pkts = 0; - rtb->num_lost_pmtud_pkts = 0; + rtb->num_lost_ignore_pkts = 0; } void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { @@ -153,9 +156,10 @@ static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, assert(rtb->num_lost_pkts); --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - assert(rtb->num_lost_pmtud_pkts); - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + assert(rtb->num_lost_ignore_pkts); + --rtb->num_lost_ignore_pkts; } return 0; @@ -244,21 +248,16 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags, fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); range = ngtcp2_range_intersect(&range, &gap); - if (ngtcp2_range_len(&range) == 0) { - if (!fr->stream.fin) { + if (ngtcp2_range_len(&range) == 0 && !fr->stream.fin && /* 0 length STREAM frame with offset == 0 must be retransmitted if no non-empty data are sent to this stream, fin flag is not set, and no data in this stream are acknowledged. */ - if (fr->stream.offset != 0 || fr->stream.datacnt != 0 || - strm->tx.offset || - (strm->flags & - (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED))) { - continue; - } - } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { - continue; - } + (fr->stream.offset != 0 || fr->stream.datacnt != 0 || + strm->tx.offset || + (strm->flags & + (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED)))) { + continue; } if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) && @@ -443,22 +442,29 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_cc *cc = rtb->cc; ngtcp2_cc_pkt pkt; - ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, - ent->ts); + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + 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 (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } } - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - ++rtb->num_lost_pmtud_pkts; - } else if (rtb->cc->on_pkt_lost) { - cc->on_pkt_lost(cc, cstat, - ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, - pktns->id, ent->ts, ent->rst.lost, - ent->rst.tx_in_flight, - ent->rst.is_app_limited), - ts); + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + ++rtb->num_lost_ignore_pkts; + } else if (ent->hd.pkt_num >= rtb->cc_pkt_num) { + rtb->rst->lost += ent->pktlen; + + if (rtb->cc->on_pkt_lost) { + cc->on_pkt_lost(cc, cstat, + ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, + pktns->id, ent->ts, ent->rst.lost, + ent->rst.tx_in_flight, + ent->rst.is_app_limited), + ts); + } } if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { @@ -705,6 +711,8 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, ngtcp2_cc *cc = rtb->cc; ngtcp2_cc_pkt pkt; + assert(ent->hd.pkt_num >= rtb->cc_pkt_num); + ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); if (cc->on_pkt_acked) { @@ -786,7 +794,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, size_t ecn_acked = 0; int verify_ecn = 0; ngtcp2_cc_ack cc_ack = {0}; - size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts; + size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_ignore_pkts; cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight; cc_ack.rtt = UINT64_MAX; @@ -830,6 +838,11 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) { + rv = NGTCP2_ERR_PROTO; + goto fail; + } + if (largest_ack == pkt_num) { largest_pkt_sent_ts = ent->ts; } @@ -839,7 +852,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; } for (i = 0; i < fr->rangecnt; ++i) { @@ -859,12 +871,16 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) { + rv = NGTCP2_ERR_PROTO; + goto fail; + } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { ack_eliciting_pkt_acked = 1; } rtb_remove(rtb, &it, &acked_ent, ent, cstat); - ++num_acked; } } @@ -873,7 +889,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, ngtcp2_max_uint64(pkt_ts - largest_pkt_sent_ts, NGTCP2_NANOSECONDS); rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts); - if (rv == 0 && cc->new_rtt_sample) { + if (rv == 0 && cc->new_rtt_sample && + rtb->largest_acked_tx_pkt_num >= rtb->cc_pkt_num) { cc->new_rtt_sample(cc, cstat, ts); } } @@ -895,9 +912,12 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, cc_ack.bytes_delivered += ent->pktlen; cc_ack.pkt_delivered = ent->rst.delivered; + + rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); + + ++num_acked; } - rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); acked_ent = ent->next; ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); @@ -912,13 +932,14 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, for (ent = acked_ent; ent; ent = acked_ent) { rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts); acked_ent = ent->next; + ++num_acked; ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); } } if (rtb->cc->on_spurious_congestion && num_lost_pkts && - rtb->num_lost_pkts == rtb->num_lost_pmtud_pkts) { + rtb->num_lost_pkts == rtb->num_lost_ignore_pkts) { rtb->cc->on_spurious_congestion(cc, cstat, ts); } @@ -933,8 +954,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, } } - rtb->rst->lost += cc_ack.bytes_lost; - cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts; if (num_acked && cc->on_ack_recv) { cc->on_ack_recv(cc, cstat, &cc_ack, ts); @@ -1189,8 +1208,9 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + --rtb->num_lost_ignore_pkts; } rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); @@ -1200,27 +1220,28 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { } } -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, + ngtcp2_duration timeout, ngtcp2_tstamp ts) { ngtcp2_ksl_it it; ngtcp2_rtb_entry *ent; int rv; (void)rv; - if (ngtcp2_ksl_len(&rtb->ents) == 0) { + if (rtb->num_lost_pkts == 0) { return; } it = ngtcp2_ksl_end(&rtb->ents); - for (;;) { + for (; rtb->num_lost_pkts;) { assert(ngtcp2_ksl_it_end(&it)); ngtcp2_ksl_it_prev(&it); ent = ngtcp2_ksl_it_get(&it); if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) || - ts - ent->lost_ts < pto) { + ts - ent->lost_ts < timeout) { return; } @@ -1229,18 +1250,15 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, --rtb->num_lost_pkts; - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; + if (ent->flags & + (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + --rtb->num_lost_ignore_pkts; } rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); - - if (ngtcp2_ksl_len(&rtb->ents) == 0) { - return; - } } } @@ -1263,60 +1281,14 @@ ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb) { return ent->lost_ts; } -static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, - ngtcp2_rtb_entry *ent) { - ngtcp2_frame_chain **pfrc, *frc; +static int rtb_reclaim_frame_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain **pfrc = &ent->frc, *frc; ngtcp2_stream *sfr; ngtcp2_strm *strm; int rv; - ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, - ent->ts); - - if (rtb->qlog) { - ngtcp2_qlog_pkt_lost(rtb->qlog, ent); - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 - " is a probe packet, no retransmission is necessary", - ent->hd.pkt_num); - return 0; - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { - --rtb->num_lost_pkts; - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) { - --rtb->num_lost_pmtud_pkts; - } - - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 - " was declared lost and has already been retransmitted", - ent->hd.pkt_num); - return 0; - } - - if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { - ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, - "pkn=%" PRId64 " has already been reclaimed on PTO", - ent->hd.pkt_num); - return 0; - } - - if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && - (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || - !conn->callbacks.lost_datagram)) { - /* PADDING only (or PADDING + ACK ) packets will have NULL - ent->frc. */ - return 0; - } - - pfrc = &ent->frc; - for (; *pfrc;) { switch ((*pfrc)->fr.type) { case NGTCP2_FRAME_STREAM: @@ -1390,14 +1362,12 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, return 0; } -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { +int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { ngtcp2_rtb_entry *ent; - ngtcp2_ksl_it it; + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rtb->ents); int rv; - it = ngtcp2_ksl_begin(&rtb->ents); - for (; !ngtcp2_ksl_it_end(&it);) { ent = ngtcp2_ksl_it_get(&it); @@ -1405,7 +1375,42 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num); assert(0 == rv); - rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) { + 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); + } + } + + /* We never send PING only probe packet because we should have + CRYPTO data or just nothing. If we have nothing, then we do + not send probe packet. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)); + + /* We never get ACK before Retry packet. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(0 == rtb->num_lost_pkts); + assert(0 == rtb->num_lost_ignore_pkts); + + /* PMTUD probe must not be sent before handshake completion. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + continue; + } + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && + (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) || + !conn->callbacks.lost_datagram)) { + continue; + } + + rv = rtb_reclaim_frame_on_retry(rtb, conn, pktns, ent); ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc, rtb->frc_objalloc, rtb->mem); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 3a9397eac5bb6f..14684a458a60e6 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -80,6 +80,9 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; /* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry includes a packet which elicits PTO probe packets. */ #define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u +/* NGTCP2_RTB_ENTRY_FLAG_SKIP indicates that the entry has the skipped + packet number. */ +#define NGTCP2_RTB_ENTRY_FLAG_SKIP 0x200u typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; @@ -187,10 +190,11 @@ typedef struct ngtcp2_rtb { /* num_lost_pkts is the number entries in ents which has NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ size_t num_lost_pkts; - /* num_lost_pmtud_pkts is the number of entries in ents which have - both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and - NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */ - size_t num_lost_pmtud_pkts; + /* num_lost_ignore_pkts is the number of entries in ents which have + NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set, and should be + excluded from lost byte count. If only those packets are lost, + congestion event is not triggered. */ + size_t num_lost_ignore_pkts; } ngtcp2_rtb; /* @@ -258,7 +262,8 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, /* * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. */ -void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, + ngtcp2_duration timeout, ngtcp2_tstamp ts); /* @@ -269,12 +274,12 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb); /* - * ngtcp2_rtb_remove_all removes all packets from |rtb|, and prepends - * all frames to |*pfrc|. Even when this function fails, some frames - * might be prepended to |*pfrc|, and the caller should handle them. + * ngtcp2_rtb_reclaim_on_retry is called when Retry packet is + * received. It removes all packets from |rtb|, and retransmittable + * frames are reclaimed for retransmission. */ -int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, - ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); +int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); /* * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c index 8ea969c4addbdc..70ec6ee2fe9ea7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -120,7 +120,7 @@ uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm) { /* strm_rob_heavily_fragmented returns nonzero if the number of gaps in |rob| exceeds the limit. */ static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) { - return ngtcp2_ksl_len(&rob->gapksl) >= 5000; + return ngtcp2_ksl_len(&rob->gapksl) >= 1000; } int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, @@ -138,11 +138,16 @@ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, } } + rv = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); + if (rv != 0) { + return rv; + } + if (strm_rob_heavily_fragmented(strm->rx.rob)) { return NGTCP2_ERR_INTERNAL; } - return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); + return 0; } void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { @@ -196,6 +201,8 @@ int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { if (rv != 0) { return rv; } + } else if (ngtcp2_ksl_len(strm->tx.streamfrq) >= 1000) { + return NGTCP2_ERR_INTERNAL; } return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset, @@ -398,7 +405,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, datalen = ngtcp2_vec_len(fr->data, fr->datacnt); /* datalen could be zero if 0 length STREAM has been sent */ - if (left == 0 && datalen) { + /* We might see more data in the queue, then left < datalen could be + true. We only see the first one for now. */ + if ((fr->type == NGTCP2_FRAME_STREAM && + (left < datalen && left < NGTCP2_MIN_STREAM_DATALEN)) || + (left == 0 && datalen)) { rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); if (rv != 0) { assert(ngtcp2_err_is_fatal(rv)); @@ -737,7 +748,16 @@ int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { } } - return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); + rv = ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ksl_len(&strm->tx.acked_offset->gap) >= 1000) { + return NGTCP2_ERR_INTERNAL; + } + + return 0; } void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c index ca517532e3a695..d652ece4e07a88 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c @@ -523,12 +523,12 @@ int ngtcp2_transport_params_decode_versioned(int transport_params_version, } /* Set default values */ - memset(params, 0, sizeof(*params)); - params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; - params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; - params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; - params->active_connection_id_limit = - NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + *params = (ngtcp2_transport_params){ + .max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE, + .ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT, + .max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY, + .active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT, + }; p = end = data; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h index 55e735d164edcb..d90a3204a9f2b4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -33,13 +33,6 @@ #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|. diff --git a/src/quic/guard.h b/src/quic/guard.h index d84694b2887fa7..2e7bd441057bf9 100644 --- a/src/quic/guard.h +++ b/src/quic/guard.h @@ -5,7 +5,7 @@ // QUIC is only available in Openssl 3.5.x and later. It was not introduced in // Node.js until 3.5.1... prior to that we will not compile any of the QUIC // related code. -#if OPENSSL_VERSION_NUMBER < 0x30500010 +#if OPENSSL_VERSION_NUMBER < 0x30500010 || OPENSSL_IS_BORINGSSL #define OPENSSL_NO_QUIC = 1 #endif #else From 1ffd8d89fd9b7a5d9e4804708d923ed0360b97b8 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 27 Jul 2025 13:42:38 -0700 Subject: [PATCH 3/4] deps: update nghttp3 to 1.11.0 Signed-off-by: James M Snell --- .../nghttp3/lib/includes/nghttp3/nghttp3.h | 117 +- .../nghttp3/lib/includes/nghttp3/version.h | 4 +- deps/ngtcp2/nghttp3/lib/nghttp3_buf.c | 12 + deps/ngtcp2/nghttp3/lib/nghttp3_buf.h | 19 +- deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c | 75 + deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h | 74 + deps/ngtcp2/nghttp3/lib/nghttp3_conn.c | 405 +++-- deps/ngtcp2/nghttp3/lib/nghttp3_conn.h | 45 +- deps/ngtcp2/nghttp3/lib/nghttp3_conv.c | 2 - deps/ngtcp2/nghttp3/lib/nghttp3_conv.h | 8 +- deps/ngtcp2/nghttp3/lib/nghttp3_frame.c | 50 +- deps/ngtcp2/nghttp3/lib/nghttp3_frame.h | 89 +- deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c | 39 +- deps/ngtcp2/nghttp3/lib/nghttp3_http.c | 32 +- deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c | 77 +- deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h | 141 +- deps/ngtcp2/nghttp3/lib/nghttp3_macro.h | 36 +- deps/ngtcp2/nghttp3/lib/nghttp3_map.c | 67 +- deps/ngtcp2/nghttp3/lib/nghttp3_map.h | 3 +- deps/ngtcp2/nghttp3/lib/nghttp3_pq.c | 16 - deps/ngtcp2/nghttp3/lib/nghttp3_pq.h | 11 - deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c | 202 ++- deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h | 29 +- .../nghttp3/lib/nghttp3_qpack_huffman.c | 4 +- deps/ngtcp2/nghttp3/lib/nghttp3_range.c | 2 +- deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c | 17 +- deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h | 4 +- deps/ngtcp2/nghttp3/lib/nghttp3_settings.c | 95 ++ deps/ngtcp2/nghttp3/lib/nghttp3_settings.h | 74 + deps/ngtcp2/nghttp3/lib/nghttp3_stream.c | 310 ++-- deps/ngtcp2/nghttp3/lib/nghttp3_stream.h | 14 +- deps/ngtcp2/nghttp3/lib/nghttp3_version.c | 7 +- deps/ngtcp2/nghttp3/lib/sfparse/COPYING | 22 - deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c | 1517 ----------------- deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h | 312 ++-- deps/ngtcp2/ngtcp2.gyp | 4 +- 36 files changed, 1642 insertions(+), 2293 deletions(-) create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_settings.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_settings.h delete mode 100644 deps/ngtcp2/nghttp3/lib/sfparse/COPYING delete mode 100644 deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h index 6cf947487bf899..83820fc983bd84 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -1116,11 +1116,43 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; * * :macro:`NGHTTP3_ERR_NOMEM` * Out of memory. + * + * See also `nghttp3_qpack_encoder_new2`. This function calls + * `nghttp3_qpack_encoder_new2` with the given parameters and 0 as + * seed. */ NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, size_t hard_max_dtable_capacity, const nghttp3_mem *mem); +/** + * @function + * + * `nghttp3_qpack_encoder_new2` initializes QPACK encoder. |pencoder| + * must be non-NULL pointer. |hard_max_dtable_capacity| is the upper + * bound of the dynamic table capacity. |seed| must be unpredictable + * value, and is used to seed the internal data structure. |mem| is a + * memory allocator. This function allocates memory for + * :type:`nghttp3_qpack_encoder` itself, and assigns its pointer to + * |*pencoder| if it succeeds. + * + * The maximum dynamic table capacity is still 0. In order to change + * the maximum dynamic table capacity, call + * `nghttp3_qpack_encoder_set_max_dtable_capacity`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * + * This function is available since v1.11.0. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, + uint64_t seed, + const nghttp3_mem *mem); + /** * @function * @@ -1605,7 +1637,8 @@ NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( typedef struct nghttp3_conn nghttp3_conn; #define NGHTTP3_SETTINGS_V1 1 -#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V1 +#define NGHTTP3_SETTINGS_V2 2 +#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V2 /** * @struct @@ -1652,6 +1685,21 @@ typedef struct nghttp3_settings { * Datagrams (see :rfc:`9297`). */ uint8_t h3_datagram; + /* The following fields have been added since NGHTTP3_SETTINGS_V2. */ + /** + * :member:`origin_list`, if set, must contain a serialized HTTP/3 + * ORIGIN frame (see :rfc:`9412`) payload. The ORIGIN frame payload + * is a sequence of zero or more of a length prefixed byte string. + * The length is encoded in 2 bytes in network byte order. If + * :member:`origin_list->len ` is zero, an empty + * ORIGIN frame is sent. An application must keep the buffer + * pointed by :member:`origin_list->base ` alive + * until the :type:`nghttp3_conn` to which this field was passed is + * freed by `nghttp3_conn_del`. The object pointed to by this field + * is copied internally, and does not need to be kept alive. Only + * server uses this field. This field is available since v1.11.0. + */ + const nghttp3_vec *origin_list; } nghttp3_settings; /** @@ -1891,8 +1939,47 @@ typedef int (*nghttp3_recv_settings)(nghttp3_conn *conn, const nghttp3_settings *settings, void *conn_user_data); +/** + * @functypedef + * + * :type:`nghttp3_recv_origin` is a callback function which is invoked + * when a single origin in ORIGIN frame is received. |origin| is a + * received origin of length |originlen|. |originlen| never be 0. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_origin)(nghttp3_conn *conn, const uint8_t *origin, + size_t originlen, void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_origin` is a callback function which is invoked + * when an ORIGIN frame has been completely processed. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_origin)(nghttp3_conn *conn, void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_rand` is a callback function which is invoked when + * unpredictable data of |destlen| bytes are needed. The + * implementation must write unpredictable data of |destlen| bytes + * into the buffer pointed by |dest|. + */ +typedef void (*nghttp3_rand)(uint8_t *dest, size_t destlen); + #define NGHTTP3_CALLBACKS_V1 1 -#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V1 +#define NGHTTP3_CALLBACKS_V2 2 +#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V2 /** * @struct @@ -1986,6 +2073,28 @@ typedef struct nghttp3_callbacks { * when SETTINGS frame is received. */ nghttp3_recv_settings recv_settings; + /* The following fields have been added since NGHTTP3_CALLBACKS_V2. */ + /** + * :member:`recv_origin` is a callback function which is invoked + * when a single origin in an ORIGIN frame is received. This field + * is available since v1.11.0. + */ + nghttp3_recv_origin recv_origin; + /** + * :member:`end_origin` is a callback function which is invoked when + * an ORIGIN frame has been completely processed. This field is + * available since v1.11.0. + */ + nghttp3_end_origin end_origin; + /** + * :member:`rand` is a callback function which is invoked when + * unpredictable data are needed. Although this field is optional + * due to the backward compatibility, it is recommended to specify + * this field to harden the runtime behavior against suspicious + * activities of a remote endpoint. This field is available since + * v1.11.0. + */ + nghttp3_rand rand; } nghttp3_callbacks; /** @@ -2106,7 +2215,7 @@ NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, * 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 + * 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. * @@ -2480,8 +2589,6 @@ typedef struct nghttp3_data_reader { * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` - * |stream_id| identifies unidirectional stream. * :macro:`NGHTTP3_ERR_CONN_CLOSING` * Connection is shutting down, and no new stream is allowed. * :macro:`NGHTTP3_ERR_STREAM_IN_USE` diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h index 7f6cb8acffe672..bd442b8be636ee 100644 --- a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h @@ -31,7 +31,7 @@ * * Version number of the nghttp3 library release. */ -#define NGHTTP3_VERSION "1.6.0" +#define NGHTTP3_VERSION "1.11.0" /** * @macro @@ -41,6 +41,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGHTTP3_VERSION_NUM 0x010600 +#define NGHTTP3_VERSION_NUM 0x010b00 #endif /* !defined(NGHTTP3_VERSION_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c index aae075a73cc4be..57cf744680e0a2 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c @@ -50,6 +50,10 @@ size_t nghttp3_buf_cap(const nghttp3_buf *buf) { return (size_t)(buf->end - buf->begin); } +size_t nghttp3_buf_offset(const nghttp3_buf *buf) { + return (size_t)(buf->pos - 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) { @@ -87,4 +91,12 @@ void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, nghttp3_buf_type type) { tbuf->buf = *buf; tbuf->type = type; + tbuf->buf.begin = tbuf->buf.pos; +} + +void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk) { + tbuf->buf = *chunk; + tbuf->type = NGHTTP3_BUF_TYPE_SHARED; + tbuf->buf.begin = tbuf->buf.pos = tbuf->buf.last; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h index 9fa067de91b949..abc59b92a97a73 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h @@ -42,6 +42,12 @@ void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); */ size_t nghttp3_buf_cap(const nghttp3_buf *buf); +/* + * nghttp3_buf_offset returns the distance from tbuf->begin to + * tbuf->pos. In other words, it returns buf->pos - buf->begin. + */ +size_t nghttp3_buf_offset(const nghttp3_buf *buf); + int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); /* @@ -57,8 +63,12 @@ typedef enum nghttp3_buf_type { memory. */ NGHTTP3_BUF_TYPE_SHARED, /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a - memory which comes from outside of the library. */ + memory which comes from outside of the library. When + acknowledged, acked_data callback is called. */ NGHTTP3_BUF_TYPE_ALIEN, + /* NGHTTP3_BUF_TYPE_ALIEN_NO_ACK is like NGHTTP3_BUF_TYPE_ALIEN, but + acked_data callback is not called. */ + NGHTTP3_BUF_TYPE_ALIEN_NO_ACK, } nghttp3_buf_type; typedef struct nghttp3_typed_buf { @@ -69,6 +79,13 @@ typedef struct nghttp3_typed_buf { void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, nghttp3_buf_type type); +/* + * nghttp3_typed_buf_shared_init initializes |tbuf| of type + * NGHTTP3_BUF_TYPE_SHARED. + */ +void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk); + void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf); #endif /* !defined(NGHTTP3_BUF_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c new file mode 100644 index 00000000000000..5db8376ad229dd --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.c @@ -0,0 +1,75 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 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_callbacks.h" + +#include +#include + +#include "nghttp3_unreachable.h" + +static void callbacks_copy(nghttp3_callbacks *dest, + const nghttp3_callbacks *src, + int callbacks_version) { + assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION); + + memcpy(dest, src, nghttp3_callbackslen_version(callbacks_version)); +} + +const nghttp3_callbacks * +nghttp3_callbacks_convert_to_latest(nghttp3_callbacks *dest, + int callbacks_version, + const nghttp3_callbacks *src) { + if (callbacks_version == NGHTTP3_CALLBACKS_VERSION) { + return src; + } + + memset(dest, 0, sizeof(*dest)); + + callbacks_copy(dest, src, callbacks_version); + + return dest; +} + +void nghttp3_callbacks_convert_to_old(int callbacks_version, + nghttp3_callbacks *dest, + const nghttp3_callbacks *src) { + assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION); + + callbacks_copy(dest, src, callbacks_version); +} + +size_t nghttp3_callbackslen_version(int callbacks_version) { + nghttp3_callbacks callbacks; + + switch (callbacks_version) { + case NGHTTP3_CALLBACKS_VERSION: + return sizeof(callbacks); + case NGHTTP3_CALLBACKS_V1: + return offsetof(nghttp3_callbacks, recv_settings) + + sizeof(callbacks.recv_settings); + default: + nghttp3_unreachable(); + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h new file mode 100644 index 00000000000000..6bebb3a98dd8ca --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_callbacks.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 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_CALLBACKS_H +#define NGHTTP3_CALLBACKS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * nghttp3_callbacks_convert_to_latest converts |src| of version + * |callbacks_version| to the latest version + * NGHTTP3_CALLBACKS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |callbacks_version| == NGHTTP3_CALLBACKS_VERSION, no conversion + * is made, and |src| is returned. Otherwise, first |dest| is + * zero-initialized, and then all valid fields in |src| are copied + * into |dest|. Finally, |dest| is returned. + */ +const nghttp3_callbacks *nghttp3_callbacks_convert_to_latest( + nghttp3_callbacks *dest, int callbacks_version, const nghttp3_callbacks *src); + +/* + * nghttp3_callbacks_convert_to_old converts |src| of the latest + * version to |dest| of version |callbacks_version|. + * + * |callbacks_version| must not be the latest version + * NGHTTP3_CALLBACKS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version + * |callbacks_version| from |src| to |dest|. + */ +void nghttp3_callbacks_convert_to_old(int callbacks_version, + nghttp3_callbacks *dest, + const nghttp3_callbacks *src); + +/* + * nghttp3_callbackslen_version returns the effective length of + * nghttp3_callbacks at the version |callbacks_version|. + */ +size_t nghttp3_callbackslen_version(int callbacks_version); + +#endif /* !defined(NGHTTP3_CALLBACKS_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c index f70b4f5472de64..8c0e94dc768a29 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c @@ -34,12 +34,10 @@ #include "nghttp3_conv.h" #include "nghttp3_http.h" #include "nghttp3_unreachable.h" +#include "nghttp3_settings.h" +#include "nghttp3_callbacks.h" -/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the - dynamic table capacity that QPACK encoder is willing to use. */ -#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 - -nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent); +nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent) /* * conn_remote_stream_uni returns nonzero if |stream_id| is remote @@ -207,6 +205,32 @@ static int conn_call_recv_settings(nghttp3_conn *conn) { return 0; } +static int conn_call_recv_origin(nghttp3_conn *conn, const uint8_t *origin, + size_t originlen) { + if (!conn->callbacks.recv_origin) { + return 0; + } + + if (conn->callbacks.recv_origin(conn, origin, originlen, conn->user_data) != + 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_origin(nghttp3_conn *conn) { + if (!conn->callbacks.end_origin) { + return 0; + } + + if (conn->callbacks.end_origin(conn, conn->user_data) != 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 = @@ -233,11 +257,22 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, const nghttp3_callbacks *callbacks, int settings_version, const nghttp3_settings *settings, const nghttp3_mem *mem, void *user_data) { - int rv; nghttp3_conn *conn; + nghttp3_settings settings_latest; + nghttp3_callbacks callbacks_latest; + uint64_t map_seed; size_t i; (void)callbacks_version; - (void)settings_version; + + settings = nghttp3_settings_convert_to_latest(&settings_latest, + settings_version, settings); + callbacks = nghttp3_callbacks_convert_to_latest(&callbacks_latest, + callbacks_version, callbacks); + + assert(settings->max_field_section_size <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_max_dtable_capacity <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_encoder_max_dtable_capacity <= NGHTTP3_VARINT_MAX); + assert(settings->qpack_blocked_streams <= NGHTTP3_VARINT_MAX); if (mem == NULL) { mem = nghttp3_mem_default(); @@ -252,20 +287,19 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem); nghttp3_objalloc_stream_init(&conn->stream_objalloc, 8, mem); - nghttp3_map_init(&conn->streams, mem); - - rv = - nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity, - settings->qpack_blocked_streams, mem); - if (rv != 0) { - goto qdec_init_fail; + if (callbacks->rand) { + callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed)); + } else { + map_seed = 0; } - rv = nghttp3_qpack_encoder_init( - &conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem); - if (rv != 0) { - goto qenc_init_fail; - } + nghttp3_map_init(&conn->streams, map_seed, mem); + + nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity, + settings->qpack_blocked_streams, mem); + + nghttp3_qpack_encoder_init( + &conn->qenc, settings->qpack_encoder_max_dtable_capacity, ++map_seed, mem); nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); @@ -277,8 +311,14 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, conn->callbacks = *callbacks; conn->local.settings = *settings; - if (!server) { + if (server) { + if (settings->origin_list) { + conn->local.settings.origin_list = &conn->local.origin_list; + conn->local.origin_list = *settings->origin_list; + } + } else { conn->local.settings.enable_connect_protocol = 0; + conn->local.settings.origin_list = NULL; } nghttp3_settings_default(&conn->remote.settings); conn->mem = mem; @@ -291,16 +331,6 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version, *pconn = conn; return 0; - -qenc_init_fail: - nghttp3_qpack_decoder_free(&conn->qdec); -qdec_init_fail: - nghttp3_map_free(&conn->streams); - nghttp3_objalloc_free(&conn->stream_objalloc); - nghttp3_objalloc_free(&conn->out_chunk_objalloc); - nghttp3_mem_free(mem, conn); - - return rv; } int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn, @@ -374,6 +404,8 @@ void nghttp3_conn_del(nghttp3_conn *conn) { nghttp3_objalloc_free(&conn->stream_objalloc); nghttp3_objalloc_free(&conn->out_chunk_objalloc); + nghttp3_mem_free(conn->mem, conn->rx.originbuf); + nghttp3_mem_free(conn->mem, conn); } @@ -399,6 +431,9 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, size_t bidi_nproc; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + stream = nghttp3_conn_find_stream(conn, stream_id); if (stream == NULL) { /* TODO Assert idtr */ @@ -434,6 +469,10 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, return rv; } } + } else if (!nghttp3_client_stream_uni(stream_id)) { + /* server does not expect to receive new server initiated + bidirectional or unidirectional stream from client. */ + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; } else { /* unidirectional stream */ if (srclen == 0 && fin) { @@ -448,7 +487,7 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; - } else if (nghttp3_stream_uni(stream_id)) { + } else if (nghttp3_server_stream_uni(stream_id)) { if (srclen == 0 && fin) { return 0; } @@ -461,17 +500,16 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, 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. */ + /* client doesn't expect to receive new bidirectional stream or + client initiated unidirectional 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; - } - } + assert(nghttp3_client_stream_bidi(stream_id) || + nghttp3_client_stream_uni(stream_id)); + } else { + assert(nghttp3_client_stream_bidi(stream_id) || + nghttp3_server_stream_uni(stream_id)); } if (srclen == 0 && !fin) { @@ -608,6 +646,9 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, break; case NGHTTP3_STREAM_TYPE_UNKNOWN: nconsumed = (nghttp3_ssize)srclen; + if (fin) { + break; + } rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR); if (rv != 0) { @@ -625,6 +666,11 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, return nread + nconsumed; } +static void conn_reset_rx_originlen(nghttp3_conn *conn) { + conn->rx.originlen_offset = 0; + conn->rx.originlen = 0; +} + static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) { return (int64_t)len >= rstate->left; } @@ -661,11 +707,11 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - rstate->fr.hd.type = rvint->acc; + rstate->fr.type = rvint->acc; nghttp3_varint_read_state_reset(rvint); rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH; if (p == end) { - break; + return (nghttp3_ssize)nconsumed; } /* Fall through */ case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH: @@ -681,19 +727,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return (nghttp3_ssize)nconsumed; } - rstate->left = rstate->fr.hd.length = rvint->acc; + rstate->left = rvint->acc; nghttp3_varint_read_state_reset(rvint); if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) { - if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) { + if (rstate->fr.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) { + } else if (rstate->fr.type == NGHTTP3_FRAME_SETTINGS) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } - switch (rstate->fr.hd.type) { + switch (rstate->fr.type) { case NGHTTP3_FRAME_SETTINGS: /* SETTINGS frame might be empty. */ if (rstate->left == 0) { @@ -734,6 +780,31 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID: /* We do not support push */ return NGHTTP3_ERR_H3_ID_ERROR; + case NGHTTP3_FRAME_ORIGIN: + if (conn->server || + (!conn->callbacks.recv_origin && !conn->callbacks.end_origin)) { + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + + /* ORIGIN frame might be empty */ + if (rstate->left == 0) { + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + + break; + } + + conn_reset_rx_originlen(conn); + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN; + + break; case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */ case NGHTTP3_FRAME_DATA: case NGHTTP3_FRAME_HEADERS: @@ -971,8 +1042,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, break; } + conn->rx.pri_fieldbuflen = 0; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE; + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + /* Fall through */ case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE: /* We need to buffer Priority Field Value because it might be @@ -1027,9 +1104,134 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, return rv; } - conn->rx.pri_fieldbuflen = 0; + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN: + /* client side only */ + len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); + + assert(len > 0); + + for (;;) { + nread = 0; + + for (; conn->rx.originlen_offset < sizeof(conn->rx.originlen) && + (size_t)nread < len; + ++conn->rx.originlen_offset, ++nread) { + conn->rx.originlen <<= 8; + conn->rx.originlen += *p++; + } + + nconsumed += (size_t)nread; + rstate->left -= nread; + len -= (size_t)nread; + + if (conn->rx.originlen_offset < sizeof(conn->rx.originlen)) { + /* Needs another byte to parse Origin-Len */ + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + return (nghttp3_ssize)nconsumed; + } + + if (conn->rx.originlen == 0 || rstate->left < conn->rx.originlen) { + /* While this seems OK in very loose RFC, allowing this + sounds like an atack vector. */ + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + if (len < conn->rx.originlen) { + /* ASCII-Origin does not fit into this buffer. Needs + buffering. */ + if (conn->rx.originbuf == NULL) { + conn->rx.originbuf = nghttp3_mem_malloc(conn->mem, UINT16_MAX); + if (conn->rx.originbuf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + } + + memcpy(conn->rx.originbuf, p, len); + + /* No need to update p because we will return very soon. */ + nconsumed += len; + rstate->left -= (int64_t)len; + + conn->rx.originbuflen = len; + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN; + + return (nghttp3_ssize)nconsumed; + } + + rv = conn_call_recv_origin(conn, p, conn->rx.originlen); + if (rv != 0) { + return rv; + } + + p += conn->rx.originlen; + nconsumed += conn->rx.originlen; + rstate->left -= conn->rx.originlen; + + if (rstate->left == 0) { + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + + break; + } + + len -= conn->rx.originlen; + + conn_reset_rx_originlen(conn); + + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + } + + break; + case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN: + /* client side only */ + len = nghttp3_min_size(conn->rx.originlen - conn->rx.originbuflen, + (size_t)(end - p)); + + assert(len > 0); + + memcpy(conn->rx.originbuf + conn->rx.originbuflen, p, len); + + conn->rx.originbuflen += len; + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (conn->rx.originbuflen < conn->rx.originlen) { + return (nghttp3_ssize)nconsumed; + } + + rv = conn_call_recv_origin(conn, conn->rx.originbuf, conn->rx.originlen); + if (rv != 0) { + return rv; + } + + if (rstate->left) { + conn_reset_rx_originlen(conn); + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN; + + break; + } + + rv = conn_call_end_origin(conn); + if (rv != 0) { + return rv; + } nghttp3_stream_read_state_reset(rstate); + break; case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); @@ -1251,7 +1453,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, goto almost_done; } - rstate->fr.hd.type = rvint->acc; + rstate->fr.type = rvint->acc; nghttp3_varint_read_state_reset(rvint); rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH; if (p == end) { @@ -1271,10 +1473,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, goto almost_done; } - rstate->left = rstate->fr.hd.length = rvint->acc; + rstate->left = rvint->acc; nghttp3_varint_read_state_reset(rvint); - switch (rstate->fr.hd.type) { + switch (rstate->fr.type) { case NGHTTP3_FRAME_DATA: rv = nghttp3_stream_transit_rx_http_state( stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); @@ -1374,10 +1576,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nread = nghttp3_conn_on_headers(conn, stream, p, len, (int64_t)len == rstate->left); if (nread < 0) { - if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { - goto http_header_error; - } - return nread; } @@ -1416,10 +1614,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, } if (rv != 0) { - if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) { - goto http_header_error; - } - return rv; } @@ -1458,24 +1652,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, nghttp3_stream_read_state_reset(rstate); - break; - - http_header_error: - stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR; - - busy = 1; - rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; - - rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); - if (rv != 0) { - return rv; - } - - rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR); - if (rv != 0) { - return rv; - } - break; case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p)); @@ -1792,6 +1968,8 @@ conn_on_priority_update_stream(nghttp3_conn *conn, stream->node.pri = fr->pri; stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED; + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; return 0; } @@ -1808,7 +1986,7 @@ conn_on_priority_update_stream(nghttp3_conn *conn, int nghttp3_conn_on_priority_update(nghttp3_conn *conn, const nghttp3_frame_priority_update *fr) { assert(conn->server); - assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE); + assert(fr->type == NGHTTP3_FRAME_PRIORITY_UPDATE); return conn_on_priority_update_stream(conn, fr); } @@ -1836,7 +2014,7 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, nghttp3_stream *stream; int rv; nghttp3_stream_callbacks callbacks = { - conn_stream_acked_data, + .acked_data = conn_stream_acked_data, }; rv = nghttp3_stream_new(&stream, stream_id, &callbacks, @@ -1874,6 +2052,8 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { nghttp3_frame_entry frent; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); assert(!conn->server || nghttp3_server_stream_uni(stream_id)); assert(conn->server || nghttp3_client_stream_uni(stream_id)); @@ -1895,10 +2075,27 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { return rv; } - frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; + frent.fr.type = NGHTTP3_FRAME_SETTINGS; frent.aux.settings.local_settings = &conn->local.settings; - return nghttp3_stream_frq_add(stream, &frent); + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + + if (conn->local.settings.origin_list) { + assert(conn->server); + + frent.fr.origin.type = NGHTTP3_FRAME_ORIGIN; + frent.fr.origin.origin_list = *conn->local.settings.origin_list; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + } + + return 0; } int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, @@ -1906,6 +2103,10 @@ int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, nghttp3_stream *stream; int rv; + assert(qenc_stream_id >= 0); + assert(qenc_stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + assert(qdec_stream_id >= 0); + assert(qdec_stream_id <= (int64_t)NGHTTP3_MAX_VARINT); 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)); @@ -2118,14 +2319,14 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, const nghttp3_data_reader *dr) { int rv; nghttp3_nv *nnva; - nghttp3_frame_entry frent = {0}; + 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.type = NGHTTP3_FRAME_HEADERS; frent.fr.headers.nva = nnva; frent.fr.headers.nvlen = nvlen; @@ -2136,7 +2337,7 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, } if (dr) { - frent.fr.hd.type = NGHTTP3_FRAME_DATA; + frent.fr.type = NGHTTP3_FRAME_DATA; frent.aux.data.dr = *dr; rv = nghttp3_stream_frq_add(stream, &frent); @@ -2194,13 +2395,11 @@ int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, assert(!conn->server); assert(conn->tx.qenc); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); assert(nghttp3_client_stream_bidi(stream_id)); - /* TODO Should we check that stream_id is client stream_id? */ /* TODO Check GOAWAY last stream ID */ - if (nghttp3_stream_uni(stream_id)) { - return NGHTTP3_ERR_INVALID_ARGUMENT; - } if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { return NGHTTP3_ERR_CONN_CLOSING; @@ -2288,12 +2487,12 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, } int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; int rv; assert(conn->tx.ctrl); - frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + frent.fr.type = NGHTTP3_FRAME_GOAWAY; frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID : NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID; @@ -2311,12 +2510,12 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { } int nghttp3_conn_shutdown(nghttp3_conn *conn) { - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; int rv; assert(conn->tx.ctrl); - frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + frent.fr.type = NGHTTP3_FRAME_GOAWAY; if (conn->server) { frent.fr.goaway.id = nghttp3_min_int64((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); @@ -2454,6 +2653,9 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) { nghttp3_stream *stream; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (!nghttp3_client_stream_bidi(stream_id)) { return 0; } @@ -2515,6 +2717,9 @@ uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, nghttp3_stream *stream; int uni = 0; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (!nghttp3_client_stream_bidi(stream_id)) { uni = conn_remote_stream_uni(conn, stream_id); if (!uni) { @@ -2542,6 +2747,8 @@ int nghttp3_conn_get_stream_priority_versioned(nghttp3_conn *conn, (void)pri_version; assert(conn->server); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2562,10 +2769,12 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, const uint8_t *data, size_t datalen) { nghttp3_stream *stream; - nghttp3_frame_entry frent = {0}; + nghttp3_frame_entry frent; uint8_t *buf = NULL; assert(!conn->server); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2585,7 +2794,7 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn, memcpy(buf, data, datalen); } - frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE; + frent.fr.type = NGHTTP3_FRAME_PRIORITY_UPDATE; frent.fr.priority_update.pri_elem_id = stream_id; frent.fr.priority_update.data = buf; frent.fr.priority_update.datalen = datalen; @@ -2603,6 +2812,8 @@ int nghttp3_conn_set_server_stream_priority_versioned(nghttp3_conn *conn, assert(conn->server); assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); assert(pri->inc == 0 || pri->inc == 1); + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); if (!nghttp3_client_stream_bidi(stream_id)) { return NGHTTP3_ERR_INVALID_ARGUMENT; @@ -2626,13 +2837,3 @@ int nghttp3_conn_is_drained(nghttp3_conn *conn) { nghttp3_stream_outq_write_done(conn->tx.ctrl) && nghttp3_ringbuf_len(&conn->tx.ctrl->frq) == 0; } - -void nghttp3_settings_default_versioned(int settings_version, - nghttp3_settings *settings) { - (void)settings_version; - - memset(settings, 0, sizeof(nghttp3_settings)); - settings->max_field_section_size = NGHTTP3_VARINT_MAX; - settings->qpack_encoder_max_dtable_capacity = - NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; -} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h index 1218ba508ba46a..80c26f80322c60 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h @@ -38,8 +38,6 @@ #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 @@ -76,7 +74,7 @@ typedef struct nghttp3_chunk { nghttp3_opl_entry oplent; } nghttp3_chunk; -nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent); +nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent) struct nghttp3_conn { nghttp3_objalloc out_chunk_objalloc; @@ -95,6 +93,11 @@ struct nghttp3_conn { uint16_t flags; struct { + /* origin_list contains the shallow copy of + nghttp3_settings.origin_list passed from an application if this + object is initialized as server. settings.origin_list may + point to the address of this field. */ + nghttp3_vec origin_list; nghttp3_settings settings; struct { /* max_pushes is the number of push IDs that local endpoint can @@ -125,12 +128,36 @@ struct nghttp3_conn { int64_t max_stream_id_bidi; - /* pri_fieldbuf is a buffer to store incoming Priority Field Value - in PRIORITY_UPDATE frame. */ - uint8_t pri_fieldbuf[8]; - /* pri_fieldlen is the number of bytes written into - pri_fieldbuf. */ - size_t pri_fieldbuflen; + union { + struct { + /* pri_fieldbuf is a buffer to store incoming Priority Field Value + in PRIORITY_UPDATE frame. */ + uint8_t pri_fieldbuf[8]; + /* pri_fieldlen is the number of bytes written into + pri_fieldbuf. */ + size_t pri_fieldbuflen; + }; + /* ORIGIN frame */ + struct { + /* originlen_offset is the offset to Origin-Len that is going + to be added to originlen. If this value equals + sizeof(originlen), Origin-Len is fully read, and the length + of ASCII-Origin is determined. */ + size_t originlen_offset; + /* originlen is Origin-Len of ASCII-Origin currently read. */ + uint16_t originlen; + }; + }; + + /* originbuf points to the buffer that contains ASCII-Origin that + is not fully available in a single input buffer. If it is + fully available in the input buffer, it is emitted to an + application without using this field. Otherwise, partial + ASCII-Origin is copied to this field, and the complete + ASCII-Origin is emitted when the assembly finishes. */ + uint8_t *originbuf; + /* originbuflen is the length of bytes written to originbuf. */ + size_t originbuflen; } rx; struct { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c index 6439a6d782960c..9b32ca3f405b9f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c @@ -66,8 +66,6 @@ const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p) { } } -int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } - size_t nghttp3_get_varintlen(const uint8_t *p) { return (size_t)(1u << (*p >> 6)); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h index 40f5d4de782883..8895fe8d3415d6 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h @@ -56,6 +56,8 @@ #include +#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) + #if HAVE_DECL_BE64TOH # define nghttp3_ntohl64(N) be64toh(N) # define nghttp3_htonl64(N) htobe64(N) @@ -135,12 +137,6 @@ STIN uint16_t ntohs(uint16_t netshort) { */ const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p); -/* - * nghttp3_get_varint_fb reads first byte of encoded variable-length - * integer from |p|. - */ -int64_t nghttp3_get_varint_fb(const uint8_t *p); - /* * nghttp3_get_varintlen returns the required number of bytes to read * variable-length integer starting at |p|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c index 1d87e448d887cf..da88b54f96de65 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c @@ -31,21 +31,22 @@ #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); +uint8_t *nghttp3_frame_write_hd(uint8_t *p, int64_t type, int64_t payloadlen) { + p = nghttp3_put_varint(p, type); + p = nghttp3_put_varint(p, payloadlen); return p; } -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { - return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(hd->length); +size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen) { + return nghttp3_put_varintlen(type) + nghttp3_put_varintlen(payloadlen); } uint8_t *nghttp3_frame_write_settings(uint8_t *p, - const nghttp3_frame_settings *fr) { + const nghttp3_frame_settings *fr, + int64_t payloadlen) { size_t i; - p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); for (i = 0; i < fr->niv; ++i) { p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id); @@ -71,9 +72,9 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } -uint8_t *nghttp3_frame_write_goaway(uint8_t *p, - const nghttp3_frame_goaway *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, const nghttp3_frame_goaway *fr, + int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); p = nghttp3_put_varint(p, fr->id); return p; @@ -89,10 +90,9 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } -uint8_t * -nghttp3_frame_write_priority_update(uint8_t *p, - const nghttp3_frame_priority_update *fr) { - p = nghttp3_frame_write_hd(p, &fr->hd); +uint8_t *nghttp3_frame_write_priority_update( + uint8_t *p, const nghttp3_frame_priority_update *fr, int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); p = nghttp3_put_varint(p, fr->pri_elem_id); if (fr->datalen) { p = nghttp3_cpymem(p, fr->data, fr->datalen); @@ -107,7 +107,27 @@ size_t nghttp3_frame_write_priority_update_len( *ppayloadlen = (int64_t)payloadlen; - return nghttp3_put_varintlen(fr->hd.type) + + return nghttp3_put_varintlen(fr->type) + + nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_origin(uint8_t *p, const nghttp3_frame_origin *fr, + int64_t payloadlen) { + p = nghttp3_frame_write_hd(p, fr->type, payloadlen); + if (fr->origin_list.len) { + p = nghttp3_cpymem(p, fr->origin_list.base, fr->origin_list.len); + } + + return p; +} + +size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen, + const nghttp3_frame_origin *fr) { + size_t payloadlen = fr->origin_list.len; + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varintlen(fr->type) + nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h index e216967d740b86..cca7ed0d6c95c7 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h @@ -44,6 +44,8 @@ /* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */ #define NGHTTP3_FRAME_PRIORITY_UPDATE 0x0f0700 #define NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID 0x0f0701 +/* ORIGIN: https://datatracker.ietf.org/doc/html/rfc9412 */ +#define NGHTTP3_FRAME_ORIGIN 0x0c /* Frame types that are reserved for HTTP/2, and must not be used in HTTP/3. */ @@ -52,17 +54,12 @@ #define NGHTTP3_H2_FRAME_WINDOW_UPDATE 0x08 #define NGHTTP3_H2_FRAME_CONTINUATION 0x9 -typedef struct nghttp3_frame_hd { - int64_t type; - int64_t length; -} nghttp3_frame_hd; - typedef struct nghttp3_frame_data { - nghttp3_frame_hd hd; + int64_t type; } nghttp3_frame_data; typedef struct nghttp3_frame_headers { - nghttp3_frame_hd hd; + int64_t type; nghttp3_nv *nva; size_t nvlen; } nghttp3_frame_headers; @@ -84,20 +81,20 @@ typedef struct nghttp3_settings_entry { } nghttp3_settings_entry; typedef struct nghttp3_frame_settings { - nghttp3_frame_hd hd; + int64_t type; size_t niv; nghttp3_settings_entry iv[1]; } nghttp3_frame_settings; typedef struct nghttp3_frame_goaway { - nghttp3_frame_hd hd; + int64_t type; int64_t id; } nghttp3_frame_goaway; typedef struct nghttp3_frame_priority_update { - nghttp3_frame_hd hd; - /* pri_elem_id is stream ID if hd.type == - NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type == + int64_t type; + /* pri_elem_id is stream ID if type == + NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if type == NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined otherwise. */ int64_t pri_elem_id; @@ -114,42 +111,54 @@ typedef struct nghttp3_frame_priority_update { }; } nghttp3_frame_priority_update; +typedef struct nghttp3_frame_origin { + int64_t type; + /* These fields are only used by server to send ORIGIN frame. + Client never use them. */ + nghttp3_vec origin_list; +} nghttp3_frame_origin; + typedef union nghttp3_frame { - nghttp3_frame_hd hd; + int64_t type; nghttp3_frame_data data; nghttp3_frame_headers headers; nghttp3_frame_settings settings; nghttp3_frame_goaway goaway; nghttp3_frame_priority_update priority_update; + nghttp3_frame_origin origin; } nghttp3_frame; /* - * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This - * function assumes that |dest| has enough space to write |hd|. + * nghttp3_frame_write_hd writes frame header consisting of |type| and + * |payloadlen| to |dest|. This function assumes that |dest| has + * enough space to write the frame header. * * This function returns |dest| plus the number of bytes written. */ -uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); +uint8_t *nghttp3_frame_write_hd(uint8_t *dest, int64_t type, + int64_t payloadlen); /* * nghttp3_frame_write_hd_len returns the number of bytes required to - * write |hd|. hd->length must be set. + * write a frame header consisting of |type| and |payloadlen|. */ -size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); +size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen); /* * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written. */ uint8_t *nghttp3_frame_write_settings(uint8_t *dest, - const nghttp3_frame_settings *fr); + const nghttp3_frame_settings *fr, + int64_t payloadlen); /* * 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|. + * required to write |fr|. This function stores the frame payload + * length in |*ppayloadlen|. */ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, const nghttp3_frame_settings *fr); @@ -157,16 +166,18 @@ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, /* * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|. * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written. */ uint8_t *nghttp3_frame_write_goaway(uint8_t *dest, - const nghttp3_frame_goaway *fr); + const nghttp3_frame_goaway *fr, + int64_t payloadlen); /* * nghttp3_frame_write_goaway_len returns the number of bytes required - * to write |fr|. fr->hd.length is ignored. This function stores - * payload length in |*ppayloadlen|. + * to write |fr|. This function stores the frame payload length in + * |*ppayloadlen|. */ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, const nghttp3_frame_goaway *fr); @@ -174,22 +185,40 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, /* * nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame * |fr| to |dest|. This function assumes that |dest| has enough space - * to write |fr|. + * to write |fr|. |payloadlen| is the length of the frame payload. * * This function returns |dest| plus the number of bytes written; */ -uint8_t * -nghttp3_frame_write_priority_update(uint8_t *dest, - const nghttp3_frame_priority_update *fr); +uint8_t *nghttp3_frame_write_priority_update( + uint8_t *dest, const nghttp3_frame_priority_update *fr, int64_t payloadlen); /* * nghttp3_frame_write_priority_update_len returns the number of bytes - * required to write |fr|. fr->hd.length is ignored. This function - * stores payload length in |*ppayloadlen|. + * required to write |fr|. This function stores the frame payload + * length in |*ppayloadlen|. */ size_t nghttp3_frame_write_priority_update_len( int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr); +/* + * nghttp3_frame_write_origin writes ORIGIN frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * |payloadlen| is the length of the frame payload. + * + * This function returns |dest| plus the number of bytes written; + */ +uint8_t *nghttp3_frame_write_origin(uint8_t *dest, + const nghttp3_frame_origin *fr, + int64_t payloadlen); + +/* + * nghttp3_frame_write_origin_len returns the number of bytes required + * to write |fr|. This function stores the frame payload length in + * |*ppayloadlen|. + */ +size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen, + const nghttp3_frame_origin *fr); + /* * nghttp3_nva_copy copies name/value pairs from |nva|, which contains * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c index 20eed5faa2bcba..e8ab509c8f9a11 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c @@ -29,14 +29,16 @@ #include void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { - nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range), - mem); + nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, + nghttp3_ksl_range_search, sizeof(nghttp3_range), mem); gaptr->mem = mem; } static int gaptr_gap_init(nghttp3_gaptr *gaptr) { - nghttp3_range range = {0, UINT64_MAX}; + nghttp3_range range = { + .end = UINT64_MAX, + }; return nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); } @@ -52,7 +54,11 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen) { int rv; - nghttp3_range k, m, l, r, q = {offset, offset + datalen}; + nghttp3_range k, m, l, r; + nghttp3_range q = { + .begin = offset, + .end = offset + datalen, + }; nghttp3_ksl_it it; if (nghttp3_ksl_len(&gaptr->gap) == 0) { @@ -62,8 +68,8 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, } } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); for (; !nghttp3_ksl_it_end(&it);) { k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); @@ -112,7 +118,10 @@ uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, uint64_t offset) { - nghttp3_range q = {offset, offset + 1}; + nghttp3_range q = { + .begin = offset, + .end = offset + 1, + }; nghttp3_ksl_it it; if (nghttp3_ksl_len(&gaptr->gap) == 0) { @@ -120,8 +129,8 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, return r; } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); assert(!nghttp3_ksl_it_end(&it)); @@ -130,7 +139,10 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, uint64_t datalen) { - nghttp3_range q = {offset, offset + datalen}; + nghttp3_range q = { + .begin = offset, + .end = offset + datalen, + }; nghttp3_ksl_it it; nghttp3_range m; @@ -138,8 +150,11 @@ int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, return 0; } - it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, - nghttp3_ksl_range_exclusive_compar); + it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_search); + + assert(!nghttp3_ksl_it_end(&it)); + m = nghttp3_range_intersect(&q, (nghttp3_range *)nghttp3_ksl_it_key(&it)); return nghttp3_range_len(&m) == 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c index 38092cfb7c322c..9a8f06ea11da9b 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c @@ -69,11 +69,11 @@ static int64_t parse_uint(const uint8_t *s, size_t len) { } for (i = 0; i < len; ++i) { if ('0' <= s[i] && s[i] <= '9') { - if (n > INT64_MAX / 10) { + if (n > (int64_t)NGHTTP3_MAX_VARINT / 10) { return -1; } n *= 10; - if (n > INT64_MAX - (s[i] - '0')) { + if (n > (int64_t)NGHTTP3_MAX_VARINT - (s[i] - '0')) { return -1; } n += s[i] - '0'; @@ -124,17 +124,17 @@ static int is_ws(uint8_t c) { int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, size_t valuelen) { nghttp3_pri pri = *dest; - sf_parser sfp; - sf_vec key; - sf_value val; + sfparse_parser sfp; + sfparse_vec key; + sfparse_value val; int rv; - sf_parser_init(&sfp, value, valuelen); + sfparse_parser_init(&sfp, value, valuelen); for (;;) { - rv = sf_parser_dict(&sfp, &key, &val); + rv = sfparse_parser_dict(&sfp, &key, &val); if (rv != 0) { - if (rv == SF_ERR_EOF) { + if (rv == SFPARSE_ERR_EOF) { break; } @@ -147,7 +147,7 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, switch (key.base[0]) { case 'i': - if (val.type != SF_TYPE_BOOLEAN) { + if (val.type != SFPARSE_TYPE_BOOLEAN) { return NGHTTP3_ERR_INVALID_ARGUMENT; } @@ -155,7 +155,8 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, break; case 'u': - if (val.type != SF_TYPE_INTEGER || val.integer < NGHTTP3_URGENCY_HIGH || + if (val.type != SFPARSE_TYPE_INTEGER || + val.integer < NGHTTP3_URGENCY_HIGH || NGHTTP3_URGENCY_LOW < val.integer) { return NGHTTP3_ERR_INVALID_ARGUMENT; } @@ -197,7 +198,7 @@ static char VALID_AUTHORITY_CHARS[] = { 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 */, + 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 */, @@ -548,6 +549,9 @@ static int http_request_on_header(nghttp3_http_state *http, break; case NGHTTP3_QPACK_TOKEN_PRIORITY: if (!nghttp3_check_header_value(nv->value->base, nv->value->len)) { + http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY; + http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; } @@ -991,7 +995,11 @@ int nghttp3_check_header_value(const uint8_t *value, size_t len) { case 0: return 1; case 1: - return !is_ws(*value); + if (is_ws(*value)) { + return 0; + } + + break; default: if (is_ws(*value) || is_ws(*(value + len - 1))) { return 0; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c index a3b5fbcb05f4f3..33dec06388a70a 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c @@ -34,9 +34,9 @@ #include "nghttp3_mem.h" #include "nghttp3_range.h" -static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; +static nghttp3_ksl_blk null_blk; -nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent); +nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent) static size_t ksl_nodelen(size_t keylen) { assert(keylen >= sizeof(uint64_t)); @@ -59,7 +59,8 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, } void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, - size_t keylen, const nghttp3_mem *mem) { + nghttp3_ksl_search search, size_t keylen, + const nghttp3_mem *mem) { size_t nodelen = ksl_nodelen(keylen); nghttp3_objalloc_init(&ksl->blkalloc, @@ -68,6 +69,7 @@ void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, ksl->head = NULL; ksl->front = ksl->back = NULL; ksl->compar = compar; + ksl->search = search; ksl->n = 0; ksl->keylen = keylen; ksl->nodelen = nodelen; @@ -274,20 +276,6 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, ++blk->n; } -static size_t ksl_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, - const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { - size_t i; - nghttp3_ksl_node *node; - - for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; - i < blk->n && compar((nghttp3_ksl_key *)node->key, key); - ++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) - ; - - return i; -} - int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, const nghttp3_ksl_key *key, void *data) { nghttp3_ksl_blk *blk; @@ -312,7 +300,7 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, blk = ksl->head; for (;;) { - i = ksl_search(ksl, blk, key, ksl->compar); + i = ksl->search(ksl, blk, key); if (blk->leaf) { if (i < blk->n && @@ -571,7 +559,7 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, } for (;;) { - i = ksl_search(ksl, blk, key, ksl->compar); + i = ksl->search(ksl, blk, key); if (i == blk->n) { if (it) { @@ -642,12 +630,12 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key) { - return nghttp3_ksl_lower_bound_compar(ksl, key, ksl->compar); + return nghttp3_ksl_lower_bound_search(ksl, key, ksl->search); } -nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, +nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar) { + nghttp3_ksl_search search) { nghttp3_ksl_blk *blk = ksl->head; nghttp3_ksl_it it; size_t i; @@ -658,7 +646,7 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, } for (;;) { - i = ksl_search(ksl, blk, key, compar); + i = search(ksl, blk, key); if (blk->leaf) { if (i == blk->n && blk->next) { @@ -702,7 +690,7 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, assert(ksl->head); for (;;) { - i = ksl_search(ksl, blk, old_key, ksl->compar); + i = ksl->search(ksl, blk, old_key); assert(i < blk->n); node = nghttp3_ksl_nth_node(ksl, blk, i); @@ -825,9 +813,50 @@ int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, return a->begin < b->begin; } +nghttp3_ksl_search_def(range, nghttp3_ksl_range_compar) + +size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_range_search(ksl, blk, key); +} + int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { const nghttp3_range *a = lhs, *b = rhs; return a->begin < b->begin && !(nghttp3_max_uint64(a->begin, b->begin) < nghttp3_min_uint64(a->end, b->end)); } + +nghttp3_ksl_search_def(range_exclusive, nghttp3_ksl_range_exclusive_compar) + +size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_range_exclusive_search(ksl, blk, key); +} + +int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return *(uint64_t *)lhs < *(uint64_t *)rhs; +} + +nghttp3_ksl_search_def(uint64_less, nghttp3_ksl_uint64_less) + +size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_uint64_less_search(ksl, blk, key); +} + +int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return *(int64_t *)lhs > *(int64_t *)rhs; +} + +nghttp3_ksl_search_def(int64_greater, nghttp3_ksl_int64_greater) + +size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key) { + return ksl_int64_greater_search(ksl, blk, key); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h index e15e227ce50fb0..be7469baa99c44 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h @@ -104,7 +104,7 @@ struct nghttp3_ksl_blk { }; }; -nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent); +nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent) /* * nghttp3_ksl_compar is a function type which returns nonzero if key @@ -115,6 +115,35 @@ typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs, typedef struct nghttp3_ksl nghttp3_ksl; +/* + * nghttp3_ksl_search is a function to search for the first element in + * |blk|->nodes which is not ordered before |key|. It returns the + * index of such element. It returns |blk|->n if there is no such + * element. + */ +typedef size_t (*nghttp3_ksl_search)(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_search_def is a macro to implement nghttp3_ksl_search + * with COMPAR which is supposed to be nghttp3_ksl_compar. + */ +#define nghttp3_ksl_search_def(NAME, COMPAR) \ + static size_t ksl_##NAME##_search(const nghttp3_ksl *ksl, \ + nghttp3_ksl_blk *blk, \ + const nghttp3_ksl_key *key) { \ + size_t i; \ + nghttp3_ksl_node *node; \ + \ + for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; \ + i < blk->n && COMPAR((nghttp3_ksl_key *)node->key, key); ++i, \ + node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) \ + ; \ + \ + return i; \ + } + typedef struct nghttp3_ksl_it nghttp3_ksl_it; /* @@ -138,6 +167,7 @@ struct nghttp3_ksl { /* back points to the last leaf block. */ nghttp3_ksl_blk *back; nghttp3_ksl_compar compar; + nghttp3_ksl_search search; /* n is the number of elements stored. */ size_t n; /* keylen is the size of key */ @@ -149,11 +179,13 @@ struct nghttp3_ksl { /* * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare - * function. |keylen| is the length of key and must be at least + * function. |search| is a search function which must use |compar|. + * |keylen| is the length of key and must be at least * sizeof(uint64_t). */ void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, - size_t keylen, const nghttp3_mem *mem); + nghttp3_ksl_search search, size_t keylen, + const nghttp3_mem *mem); /* * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is @@ -218,12 +250,12 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key); /* - * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound, - * but it takes custom function |compar| to do lower bound search. + * nghttp3_ksl_lower_bound_search works like nghttp3_ksl_lower_bound, + * but it takes custom function |search| to do lower bound search. */ -nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl, +nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl, const nghttp3_ksl_key *key, - nghttp3_ksl_compar compar); + nghttp3_ksl_search search); /* * nghttp3_ksl_update_key replaces the key of nodes which has @@ -263,8 +295,11 @@ void nghttp3_ksl_clear(nghttp3_ksl *ksl); /* * nghttp3_ksl_nth_node returns the |n|th node under |blk|. */ -#define nghttp3_ksl_nth_node(KSL, BLK, N) \ - ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) +static inline nghttp3_ksl_node *nghttp3_ksl_nth_node(const nghttp3_ksl *ksl, + const nghttp3_ksl_blk *blk, + size_t n) { + return (nghttp3_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n); +} #ifndef WIN32 /* @@ -286,18 +321,21 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, * |it| points to. It is undefined to call this function when * nghttp3_ksl_it_end(it) returns nonzero. */ -#define nghttp3_ksl_it_get(IT) \ - nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data +static inline void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { + return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} /* * nghttp3_ksl_it_next advances the iterator by one. It is undefined * if this function is called when nghttp3_ksl_it_end(it) returns * nonzero. */ -#define nghttp3_ksl_it_next(IT) \ - (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ - ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ - : 0) +static inline void nghttp3_ksl_it_next(nghttp3_ksl_it *it) { + if (++it->i == it->blk->n && it->blk->next) { + it->blk = it->blk->next; + it->i = 0; + } +} /* * nghttp3_ksl_it_prev moves backward the iterator by one. It is @@ -310,8 +348,9 @@ void nghttp3_ksl_it_prev(nghttp3_ksl_it *it); * nghttp3_ksl_it_end returns nonzero if |it| points to the one beyond * the last node. */ -#define nghttp3_ksl_it_end(IT) \ - ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) +static inline int nghttp3_ksl_it_end(const nghttp3_ksl_it *it) { + return it->blk->n == it->i && it->blk->next == NULL; +} /* * nghttp3_ksl_it_begin returns nonzero if |it| points to the first @@ -325,27 +364,75 @@ int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); * It is undefined to call this function when nghttp3_ksl_it_end(it) * returns nonzero. */ -#define nghttp3_ksl_it_key(IT) \ - ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) +static inline nghttp3_ksl_key *nghttp3_ksl_it_key(const nghttp3_ksl_it *it) { + return (nghttp3_ksl_key *)nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->key; +} /* * nghttp3_ksl_range_compar is an implementation of - * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to - * nghttp3_range object and the function returns nonzero if (const - * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range - * *)(rhs->ptr)->begin. + * nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range + * object, and the function returns nonzero if ((const nghttp3_range + * *)lhs)->begin < ((const nghttp3_range *)rhs)->begin. */ int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs); +/* + * nghttp3_ksl_range_search is an implementation of nghttp3_ksl_search + * that uses nghttp3_ksl_range_compar. + */ +size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + /* * 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. + * nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range + * object, and the function returns nonzero if ((const nghttp3_range + * *)lhs)->begin < ((const nghttp3_range *)rhs)->begin, and the 2 + * ranges do not intersect. */ int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs); +/* + * nghttp3_ksl_range_exclusive_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_range_exclusive_compar. + */ +size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_uint64_less is an implementation of nghttp3_ksl_compar. + * |lhs| and |rhs| must point to uint64_t objects, and the function + * returns nonzero if *(uint64_t *)|lhs| < *(uint64_t *)|rhs|. + */ +int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_uint64_less_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_uint64_less. + */ +size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_int64_greater is an implementation of + * nghttp3_ksl_compar. |lhs| and |rhs| must point to int64_t objects, + * and the function returns nonzero if *(int64_t *)|lhs| > *(int64_t + * *)|rhs|. + */ +int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_int64_greater_search is an implementation of + * nghttp3_ksl_search that uses nghttp3_ksl_int64_greater. + */ +size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key); + #endif /* !defined(NGHTTP3_KSL_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h index a4e1dfea3cda00..ffe0ea70924327 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h @@ -48,27 +48,27 @@ #define nghttp3_max_def(SUFFIX, T) \ static inline T nghttp3_max_##SUFFIX(T a, T b) { return a < b ? b : a; } -nghttp3_max_def(int8, int8_t); -nghttp3_max_def(int16, int16_t); -nghttp3_max_def(int32, int32_t); -nghttp3_max_def(int64, int64_t); -nghttp3_max_def(uint8, uint8_t); -nghttp3_max_def(uint16, uint16_t); -nghttp3_max_def(uint32, uint32_t); -nghttp3_max_def(uint64, uint64_t); -nghttp3_max_def(size, size_t); +nghttp3_max_def(int8, int8_t) +nghttp3_max_def(int16, int16_t) +nghttp3_max_def(int32, int32_t) +nghttp3_max_def(int64, int64_t) +nghttp3_max_def(uint8, uint8_t) +nghttp3_max_def(uint16, uint16_t) +nghttp3_max_def(uint32, uint32_t) +nghttp3_max_def(uint64, uint64_t) +nghttp3_max_def(size, size_t) #define nghttp3_min_def(SUFFIX, T) \ static inline T nghttp3_min_##SUFFIX(T a, T b) { return a < b ? a : b; } -nghttp3_min_def(int8, int8_t); -nghttp3_min_def(int16, int16_t); -nghttp3_min_def(int32, int32_t); -nghttp3_min_def(int64, int64_t); -nghttp3_min_def(uint8, uint8_t); -nghttp3_min_def(uint16, uint16_t); -nghttp3_min_def(uint32, uint32_t); -nghttp3_min_def(uint64, uint64_t); -nghttp3_min_def(size, size_t); +nghttp3_min_def(int8, int8_t) +nghttp3_min_def(int16, int16_t) +nghttp3_min_def(int32, int32_t) +nghttp3_min_def(int64, int64_t) +nghttp3_min_def(uint8, uint8_t) +nghttp3_min_def(uint16, uint16_t) +nghttp3_min_def(uint32, uint32_t) +nghttp3_min_def(uint64, uint64_t) +nghttp3_min_def(size, size_t) #endif /* !defined(NGHTTP3_MACRO_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c index cc5e42a2caf63f..f3165c81e61153 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c @@ -32,12 +32,13 @@ #include "nghttp3_conv.h" -#define NGHTTP3_INITIAL_TABLE_LENBITS 4 +#define NGHTTP3_INITIAL_HASHBITS 4 -void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { +void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem) { map->mem = mem; map->hashbits = 0; map->table = NULL; + map->seed = seed; map->size = 0; } @@ -78,8 +79,14 @@ int nghttp3_map_each(const nghttp3_map *map, int (*func)(void *data, void *ptr), return 0; } -static size_t hash(nghttp3_map_key_type key, size_t bits) { - return (size_t)((key * 11400714819323198485llu) >> (64 - bits)); +static size_t map_hash(const nghttp3_map *map, nghttp3_map_key_type key) { + /* hasher from + https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs + We do not perform finalization here because we use top bits + anyway. */ + key += map->seed; + key *= 0xf1357aea2e62a9c5ull; + return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits)); } static void map_bucket_swap(nghttp3_map_bucket *a, nghttp3_map_bucket *b) { @@ -110,24 +117,28 @@ void nghttp3_map_print_distance(const nghttp3_map *map) { continue; } - idx = hash(bkt->key, map->hashbits); + idx = map_hash(map, bkt->key); fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i, - hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl); + map_hash(map, bkt->key), bkt->key, idx, bkt->psl); } } #endif /* !defined(WIN32) */ -static int insert(nghttp3_map_bucket *table, size_t hashbits, - nghttp3_map_key_type key, void *data) { - size_t idx = hash(key, hashbits); - nghttp3_map_bucket b = {0, key, data}, *bkt; - size_t mask = (1u << hashbits) - 1; +static int map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { + size_t idx = map_hash(map, key); + nghttp3_map_bucket b = { + .key = key, + .data = data, + }; + nghttp3_map_bucket *bkt; + size_t mask = (1u << map->hashbits) - 1; for (;;) { - bkt = &table[idx]; + bkt = &map->table[idx]; if (bkt->data == NULL) { *bkt = b; + ++map->size; return 0; } @@ -148,15 +159,19 @@ static int insert(nghttp3_map_bucket *table, size_t hashbits, static int map_resize(nghttp3_map *map, size_t new_hashbits) { size_t i; - nghttp3_map_bucket *new_table; nghttp3_map_bucket *bkt; size_t tablelen; int rv; + nghttp3_map new_map = { + .table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits, + sizeof(nghttp3_map_bucket)), + .mem = map->mem, + .seed = map->seed, + .hashbits = new_hashbits, + }; (void)rv; - new_table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits, - sizeof(nghttp3_map_bucket)); - if (new_table == NULL) { + if (new_map.table == NULL) { return NGHTTP3_ERR_NOMEM; } @@ -169,15 +184,15 @@ static int map_resize(nghttp3_map *map, size_t new_hashbits) { continue; } - rv = insert(new_table, new_hashbits, bkt->key, bkt->data); + rv = map_insert(&new_map, bkt->key, bkt->data); assert(0 == rv); } } nghttp3_mem_free(map->mem, map->table); + map->table = new_map.table; map->hashbits = new_hashbits; - map->table = new_table; return 0; } @@ -187,30 +202,28 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) { assert(data); - /* Load factor is 0.75 */ + /* Load factor is 7/8 */ /* Under the very initial condition, that is map->size == 0 and - map->hashbits == 0, 4 > 3 still holds nicely. */ - if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) { + map->hashbits == 0, 8 > 7 still holds nicely. */ + if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) { if (map->hashbits) { rv = map_resize(map, map->hashbits + 1); if (rv != 0) { return rv; } } else { - rv = map_resize(map, NGHTTP3_INITIAL_TABLE_LENBITS); + rv = map_resize(map, NGHTTP3_INITIAL_HASHBITS); if (rv != 0) { return rv; } } } - rv = insert(map->table, map->hashbits, key, data); + rv = map_insert(map, key, data); if (rv != 0) { return rv; } - ++map->size; - return 0; } @@ -224,7 +237,7 @@ void *nghttp3_map_find(const nghttp3_map *map, nghttp3_map_key_type key) { return NULL; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { @@ -253,7 +266,7 @@ int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - idx = hash(key, map->hashbits); + idx = map_hash(map, key); mask = (1u << map->hashbits) - 1; for (;;) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h index 2b1a6ecab5cf4c..7496b486e17885 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h @@ -48,6 +48,7 @@ typedef struct nghttp3_map_bucket { typedef struct nghttp3_map { nghttp3_map_bucket *table; const nghttp3_mem *mem; + uint64_t seed; size_t size; size_t hashbits; } nghttp3_map; @@ -55,7 +56,7 @@ typedef struct nghttp3_map { /* * nghttp3_map_init initializes the map |map|. */ -void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); +void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem); /* * nghttp3_map_free deallocates any resources allocated for |map|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c index feefcd6fc717c3..e35bcac4e43b8f 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c @@ -164,20 +164,4 @@ 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/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h index 3813b529473075..c4e47b16708b53 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h @@ -112,17 +112,6 @@ int nghttp3_pq_empty(const nghttp3_pq *pq); */ size_t nghttp3_pq_size(const nghttp3_pq *pq); -typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); - -/* - * nghttp3_pq_each applies |fun| to each item in |pq|. The |arg| is - * passed as arg parameter to callback function. This function must - * not change the ordering key. If the return value from callback is - * nonzero, this function returns 1 immediately without iterating - * remaining items. Otherwise this function returns 0. - */ -int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg); - /* * nghttp3_pq_remove removes |item| from |pq|. |pq| must contain * |item| otherwise the behavior is undefined. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c index a1c7e7487c1a34..d2bac4830042ef 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c @@ -41,7 +41,12 @@ #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} +#define MAKE_STATIC_ENT(I, T, H) \ + { \ + .absidx = I, \ + .token = T, \ + .hash = H, \ + } /* Generated by mkstatichdtbl.py */ static nghttp3_qpack_static_entry token_stable[] = { @@ -166,9 +171,19 @@ static nghttp3_qpack_static_entry token_stable[] = { /* Make scalar initialization form of nghttp3_qpack_static_entry */ #define MAKE_STATIC_HD(N, V, T) \ { \ - {NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ - T, \ + .name = \ + { \ + .base = (uint8_t *)(N), \ + .len = sizeof((N)) - 1, \ + .ref = -1, \ + }, \ + .value = \ + { \ + .base = (uint8_t *)(V), \ + .len = sizeof((V)) - 1, \ + .ref = -1, \ + }, \ + .token = T, \ } static nghttp3_qpack_static_header stable[] = { @@ -828,29 +843,12 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, * ctx->max_dtable_capacity and it is initialized to 0. * |hard_max_dtable_capacity| is the upper bound of * ctx->max_dtable_capacity. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -static int qpack_context_init(nghttp3_qpack_context *ctx, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem) { - int rv; - size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; - size_t len2; - - for (len2 = 1; len2 < len; len2 <<= 1) - ; - - rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *), - mem); - if (rv != 0) { - return rv; - } +static void qpack_context_init(nghttp3_qpack_context *ctx, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem) { + nghttp3_ringbuf_init(&ctx->dtable, 0, sizeof(nghttp3_qpack_entry *), mem); ctx->mem = mem; ctx->dtable_size = 0; @@ -860,8 +858,6 @@ static int qpack_context_init(nghttp3_qpack_context *ctx, ctx->max_blocked_streams = max_blocked_streams; ctx->next_absidx = 0; ctx->bad = 0; - - return 0; } static void qpack_context_free(nghttp3_qpack_context *ctx) { @@ -898,19 +894,17 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs, return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id); } -int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem) { - int rv; +nghttp3_ksl_search_def(max_cnt_greater, max_cnt_greater) - rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); - if (rv != 0) { - return rv; - } +void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem) { + qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem); - nghttp3_map_init(&encoder->streams, mem); + nghttp3_map_init(&encoder->streams, seed, mem); nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + ksl_max_cnt_greater_search, sizeof(nghttp3_blocked_streams_key), mem); qpack_map_init(&encoder->dtable_map); @@ -925,8 +919,6 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; nghttp3_qpack_read_state_reset(&encoder->rstate); - - return 0; } static int map_stream_free(void *data, void *ptr) { @@ -1149,6 +1141,9 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, int blocked_stream; nghttp3_qpack_stream *stream; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (encoder->ctx.bad) { return NGHTTP3_ERR_QPACK_FATAL; } @@ -1444,7 +1439,14 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, 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_lookup_result sres = { + .index = -1, + .pb_index = -1, + }; + nghttp3_qpack_lookup_result dres = { + .index = -1, + .pb_index = -1, + }; nghttp3_qpack_entry *new_ent = NULL; int static_entry; int just_index = 0; @@ -1608,8 +1610,10 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, 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_lookup_result res = { + .index = (nghttp3_ssize)token_stable[token].absidx, + .pb_index = -1, + }; nghttp3_qpack_static_entry *ent; nghttp3_qpack_static_header *hdr; size_t i; @@ -1639,7 +1643,10 @@ nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, int allow_blocking) { - nghttp3_qpack_lookup_result res = {-1, 0, -1}; + nghttp3_qpack_lookup_result res = { + .index = -1, + .pb_index = -1, + }; int exact_match = 0; nghttp3_qpack_entry *match, *pb_match; @@ -1694,7 +1701,6 @@ static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, 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)); @@ -1702,12 +1708,8 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, 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_ringbuf_init(&stream->refs, 0, + sizeof(nghttp3_qpack_header_block_ref *), mem); nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); @@ -1759,17 +1761,23 @@ int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, int rv; if (nghttp3_ringbuf_full(&stream->refs)) { - rv = nghttp3_ringbuf_reserve(&stream->refs, - nghttp3_ringbuf_len(&stream->refs) * 2); + rv = nghttp3_ringbuf_reserve( + &stream->refs, + nghttp3_max_size(4, nghttp3_ringbuf_len(&stream->refs) * 2)); if (rv != 0) { return rv; } } + rv = nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); + 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); + return 0; } void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { @@ -1834,7 +1842,7 @@ static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, int h = 0; hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (hlen * 4 < nv->valuelen * 3) { + if (hlen < nv->valuelen) { h = 1; len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; } else { @@ -1925,7 +1933,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, int nh = 0, vh = 0; nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); - if (nhlen * 4 < nv->namelen * 3) { + if (nhlen < nv->namelen) { nh = 1; len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen; } else { @@ -1933,7 +1941,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, } vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); - if (vhlen * 4 < nv->valuelen * 3) { + if (vhlen < nv->valuelen) { vh = 1; len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; } else { @@ -2083,8 +2091,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, hash); if (nghttp3_ringbuf_full(&ctx->dtable)) { - rv = nghttp3_ringbuf_reserve(&ctx->dtable, - nghttp3_ringbuf_len(&ctx->dtable) * 2); + rv = nghttp3_ringbuf_reserve( + &ctx->dtable, + nghttp3_max_size(128, nghttp3_ringbuf_len(&ctx->dtable) * 2)); if (rv != 0) { goto fail; } @@ -2259,10 +2268,11 @@ void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream *stream) { nghttp3_blocked_streams_key bsk = { - nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), - nghttp3_qpack_header_block_ref, max_cnts_pe) - ->max_cnt, - (uint64_t)stream->stream_id}; + .max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + .id = (uint64_t)stream->stream_id, + }; return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); } @@ -2270,10 +2280,11 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, nghttp3_qpack_stream *stream) { nghttp3_blocked_streams_key bsk = { - nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), - nghttp3_qpack_header_block_ref, max_cnts_pe) - ->max_cnt, - (uint64_t)stream->stream_id}; + .max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + .id = (uint64_t)stream->stream_id, + }; nghttp3_ksl_it it; /* This is purely debugging purpose only */ @@ -2287,7 +2298,9 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, uint64_t max_cnt) { - nghttp3_blocked_streams_key bsk = {max_cnt, 0}; + nghttp3_blocked_streams_key bsk = { + .max_cnt = max_cnt, + }; nghttp3_ksl_it it; it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); @@ -2667,17 +2680,12 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { rstate->huffman_encoded = 0; } -int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem) { - int rv; - - rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, - max_blocked_streams, mem); - if (rv != 0) { - return rv; - } +void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem) { + qpack_context_init(&decoder->ctx, hard_max_dtable_capacity, + max_blocked_streams, mem); decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; decoder->opcode = 0; @@ -2687,8 +2695,6 @@ int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, nghttp3_qpack_read_state_reset(&decoder->rstate); nghttp3_buf_init(&decoder->dbuf); - - return 0; } void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { @@ -3171,6 +3177,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; return rv; } @@ -3197,6 +3204,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; nghttp3_rcbuf_decref(qnv.name); return rv; @@ -3250,7 +3258,9 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); nghttp3_rcbuf_decref(qnv.value); + decoder->rstate.value = NULL; nghttp3_rcbuf_decref(qnv.name); + decoder->rstate.name = NULL; return rv; } @@ -3818,6 +3828,9 @@ int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, uint8_t *p; int rv; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + if (qpack_decoder_dbuf_overflow(decoder)) { return NGHTTP3_ERR_QPACK_FATAL; } @@ -4059,10 +4072,9 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, sctx->rstate.value = NULL; } -int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem) { - int rv; +int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem) { nghttp3_qpack_encoder *p; p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder)); @@ -4070,16 +4082,19 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem); - if (rv != 0) { - return rv; - } + nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, seed, mem); *pencoder = p; return 0; } +int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t hard_max_dtable_capacity, + const nghttp3_mem *mem) { + return nghttp3_qpack_encoder_new2(pencoder, hard_max_dtable_capacity, 0, mem); +} + void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) { const nghttp3_mem *mem; @@ -4098,6 +4113,9 @@ int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, const nghttp3_mem *mem) { nghttp3_qpack_stream_context *p; + assert(stream_id >= 0); + assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT); + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context)); if (p == NULL) { return NGHTTP3_ERR_NOMEM; @@ -4127,7 +4145,6 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, size_t hard_max_dtable_capacity, size_t max_blocked_streams, const nghttp3_mem *mem) { - int rv; nghttp3_qpack_decoder *p; p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); @@ -4135,11 +4152,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, return NGHTTP3_ERR_NOMEM; } - rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, - max_blocked_streams, mem); - if (rv != 0) { - return rv; - } + nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, max_blocked_streams, + mem); *pdecoder = p; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h index d2bb8a3581135b..77d264a533fba4 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h @@ -269,17 +269,12 @@ struct nghttp3_qpack_encoder { /* * nghttp3_qpack_encoder_init initializes |encoder|. * |hard_max_dtable_capacity| is the upper bound of the dynamic table - * capacity. |mem| is a memory allocator. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. + * capacity. |seed| is used to initialize nghttp3_map. |mem| is a + * memory allocator. */ -int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, - size_t hard_max_dtable_capacity, - const nghttp3_mem *mem); +void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_capacity, uint64_t seed, + const nghttp3_mem *mem); /* * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. @@ -800,17 +795,11 @@ struct nghttp3_qpack_decoder { * |hard_max_dtable_capacity| is the upper bound of the dynamic table * capacity. |max_blocked_streams| is the maximum number of stream * which can be blocked. |mem| is a memory allocator. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP3_ERR_NOMEM - * Out of memory. */ -int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, - size_t hard_max_dtable_capacity, - size_t max_blocked_streams, - const nghttp3_mem *mem); +void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t hard_max_dtable_capacity, + size_t max_blocked_streams, + const nghttp3_mem *mem); /* * nghttp3_qpack_decoder_free frees memory allocated for |decoder|. diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c index 3398f3f5080e60..dbc9a764e7bf26 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c @@ -88,7 +88,9 @@ nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, int fin) { uint8_t *p = dest; const uint8_t *end = src + srclen; - nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; + nghttp3_qpack_huffman_decode_node node = { + .fstate = ctx->fstate, + }; const nghttp3_qpack_huffman_decode_node *t = &node; uint8_t c; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c index af810a2c5929db..b5f95bb83fc852 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c @@ -33,7 +33,7 @@ void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) { nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, const nghttp3_range *b) { - nghttp3_range r = {0, 0}; + nghttp3_range r = {0}; uint64_t begin = nghttp3_max_uint64(a->begin, b->begin); uint64_t end = nghttp3_min_uint64(a->end, b->end); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c index 7d3ab39bf82a7f..ccd1d3e99ab55e 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c @@ -33,18 +33,21 @@ #include "nghttp3_macro.h" +#ifndef NDEBUG static int ispow2(size_t n) { -#if defined(_MSC_VER) && !defined(__clang__) && \ - (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941)) +# if defined(DISABLE_POPCNT) || \ + (defined(_MSC_VER) && !defined(__clang__) && \ + (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941))) return n && !(n & (n - 1)); -#elif defined(WIN32) +# elif defined(WIN32) return 1 == __popcnt((unsigned int)n); -#else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ - (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +# else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ + (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ return 1 == __builtin_popcount((unsigned int)n); -#endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ - (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ +# endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \ + (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */ } +#endif /* !defined(NDEBUG) */ int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, const nghttp3_mem *mem) { diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h index b154290a51d5a5..691c40b93d61e0 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h @@ -103,7 +103,9 @@ void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset); /* nghttp3_ringbuf_len returns the number of elements stored. */ -#define nghttp3_ringbuf_len(RB) ((RB)->len) +static inline size_t nghttp3_ringbuf_len(const nghttp3_ringbuf *rb) { + return rb->len; +} /* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c new file mode 100644 index 00000000000000..2b7cd2892b3cc9 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.c @@ -0,0 +1,95 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 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_settings.h" + +#include +#include + +#include "nghttp3_conv.h" +#include "nghttp3_unreachable.h" + +/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the + dynamic table capacity that QPACK encoder is willing to use. */ +#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096 + +void nghttp3_settings_default_versioned(int settings_version, + nghttp3_settings *settings) { + size_t len = nghttp3_settingslen_version(settings_version); + + memset(settings, 0, len); + + switch (settings_version) { + case NGHTTP3_SETTINGS_VERSION: + case NGHTTP3_SETTINGS_V1: + settings->max_field_section_size = NGHTTP3_VARINT_MAX; + settings->qpack_encoder_max_dtable_capacity = + NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY; + + break; + } +} + +static void settings_copy(nghttp3_settings *dest, const nghttp3_settings *src, + int settings_version) { + assert(settings_version != NGHTTP3_SETTINGS_VERSION); + + memcpy(dest, src, nghttp3_settingslen_version(settings_version)); +} + +const nghttp3_settings * +nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version, + const nghttp3_settings *src) { + if (settings_version == NGHTTP3_SETTINGS_VERSION) { + return src; + } + + nghttp3_settings_default(dest); + + settings_copy(dest, src, settings_version); + + return dest; +} + +void nghttp3_settings_convert_to_old(int settings_version, + nghttp3_settings *dest, + const nghttp3_settings *src) { + assert(settings_version != NGHTTP3_SETTINGS_VERSION); + + settings_copy(dest, src, settings_version); +} + +size_t nghttp3_settingslen_version(int settings_version) { + nghttp3_settings settings; + + switch (settings_version) { + case NGHTTP3_SETTINGS_VERSION: + return sizeof(settings); + case NGHTTP3_SETTINGS_V1: + return offsetof(nghttp3_settings, h3_datagram) + + sizeof(settings.h3_datagram); + default: + nghttp3_unreachable(); + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h new file mode 100644 index 00000000000000..0632aafc489ee5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_settings.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2025 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_SETTINGS_H +#define NGHTTP3_SETTINGS_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +/* + * nghttp3_settings_convert_to_latest converts |src| of version + * |settings_version| to the latest version NGHTTP3_SETTINGS_VERSION. + * + * |dest| must point to the latest version. |src| may be the older + * version, and if so, it may have fewer fields. Accessing those + * fields causes undefined behavior. + * + * If |settings_version| == NGHTTP3_SETTINGS_VERSION, no conversion is + * made, and |src| is returned. Otherwise, first |dest| is + * initialized via nghttp3_settings_default, and then all valid fields + * in |src| are copied into |dest|. Finally, |dest| is returned. + */ +const nghttp3_settings * +nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version, + const nghttp3_settings *src); + +/* + * nghttp3_settings_convert_to_old converts |src| of the latest + * version to |dest| of version |settings_version|. + * + * |settings_version| must not be the latest version + * NGHTTP3_SETTINGS_VERSION. + * + * |dest| points to the older version, and it may have fewer fields. + * Accessing those fields causes undefined behavior. + * + * This function copies all valid fields in version |settings_version| + * from |src| to |dest|. + */ +void nghttp3_settings_convert_to_old(int settings_version, + nghttp3_settings *dest, + const nghttp3_settings *src); + +/* + * nghttp3_settingslen_version returns the effective length of + * nghttp3_settings at the version |settings_version|. + */ +size_t nghttp3_settingslen_version(int settings_version); + +#endif /* !defined(NGHTTP3_SETTINGS_H) */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c index 328cddd488fd6f..d6f8fac3c528bb 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c @@ -44,7 +44,7 @@ /* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */ #define NGHTTP3_MIN_RBLEN 4 -nghttp3_objalloc_def(stream, nghttp3_stream, oplent); +nghttp3_objalloc_def(stream, nghttp3_stream, oplent) int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, const nghttp3_stream_callbacks *callbacks, @@ -57,10 +57,19 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, return NGHTTP3_ERR_NOMEM; } - memset(stream, 0, sizeof(*stream)); - - stream->out_chunk_objalloc = out_chunk_objalloc; - stream->stream_objalloc = stream_objalloc; + *stream = (nghttp3_stream){ + .out_chunk_objalloc = out_chunk_objalloc, + .stream_objalloc = stream_objalloc, + .qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX, + .mem = mem, + .rx = + { + .http.status_code = -1, + .http.content_length = -1, + .http.pri.urgency = NGHTTP3_DEFAULT_URGENCY, + }, + .error_code = NGHTTP3_H3_NO_ERROR, + }; nghttp3_tnode_init(&stream->node, stream_id); @@ -71,13 +80,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); - stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; - stream->mem = mem; - stream->rx.http.status_code = -1; - stream->rx.http.content_length = -1; - stream->rx.http.pri.urgency = NGHTTP3_DEFAULT_URGENCY; - stream->error_code = NGHTTP3_H3_NO_ERROR; - if (callbacks) { stream->callbacks = *callbacks; } @@ -140,7 +142,7 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { for (i = 0; i < len; ++i) { frent = nghttp3_ringbuf_get(frq, i); - switch (frent->fr.hd.type) { + switch (frent->fr.type) { case NGHTTP3_FRAME_HEADERS: nghttp3_frame_headers_free(&frent->fr.headers, mem); break; @@ -181,42 +183,45 @@ void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) { nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, const uint8_t *begin, const uint8_t *end, int fin) { - const uint8_t *orig_begin = begin; - size_t len; + size_t len, vlen; + uint8_t *p; assert(begin != end); if (rvint->left == 0) { assert(rvint->acc == 0); - len = nghttp3_get_varintlen(begin); - if (len <= (size_t)(end - begin)) { + vlen = nghttp3_get_varintlen(begin); + len = nghttp3_min_size(vlen, (size_t)(end - begin)); + if (vlen <= len) { nghttp3_get_varint(&rvint->acc, begin); - return (nghttp3_ssize)len; + return (nghttp3_ssize)vlen; } if (fin) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - rvint->acc = nghttp3_get_varint_fb(begin++); - rvint->left = len - 1; - } - - len = nghttp3_min_size(rvint->left, (size_t)(end - begin)); - end = begin + len; + p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - vlen); + memcpy(p, begin, len); + *p &= 0x3f; + rvint->left = vlen - len; - for (; begin != end;) { - rvint->acc = (rvint->acc << 8) + *begin++; + return (nghttp3_ssize)len; } + len = nghttp3_min_size(rvint->left, (size_t)(end - begin)); + p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - rvint->left); + memcpy(p, begin, len); rvint->left -= len; - if (fin && rvint->left) { + if (rvint->left == 0) { + rvint->acc = (int64_t)nghttp3_ntohl64((uint64_t)rvint->acc); + } else if (fin) { return NGHTTP3_ERR_INVALID_ARGUMENT; } - return (nghttp3_ssize)(begin - orig_begin); + return (nghttp3_ssize)len; } int nghttp3_stream_frq_add(nghttp3_stream *stream, @@ -250,7 +255,7 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) { frent = nghttp3_ringbuf_get(frq, 0); - switch (frent->fr.hd.type) { + switch (frent->fr.type) { case NGHTTP3_FRAME_SETTINGS: rv = nghttp3_stream_write_settings(stream, frent); if (rv != 0) { @@ -290,6 +295,13 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { nghttp3_frame_priority_update_free(&frent->fr.priority_update, stream->mem); break; + case NGHTTP3_FRAME_ORIGIN: + rv = nghttp3_stream_write_origin(stream, frent); + if (rv != 0) { + return rv; + } + + break; default: /* TODO Not implemented */ break; @@ -301,12 +313,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) { return 0; } -static void typed_buf_shared_init(nghttp3_typed_buf *tbuf, - const nghttp3_buf *chunk) { - nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED); - tbuf->buf.pos = tbuf->buf.last; -} - int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { size_t len = nghttp3_put_varintlen((int64_t)stream->type); nghttp3_buf *chunk; @@ -319,7 +325,7 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); tbuf.buf.last = chunk->last; @@ -336,12 +342,17 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, struct { nghttp3_frame_settings settings; nghttp3_settings_entry iv[15]; - } fr; + } fr = { + .settings = + { + .type = NGHTTP3_FRAME_SETTINGS, + .niv = 3, + }, + }; nghttp3_settings_entry *iv; nghttp3_settings *local_settings = frent->aux.settings.local_settings; + int64_t payloadlen; - fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS; - fr.settings.niv = 3; iv = &fr.settings.iv[0]; iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; @@ -365,7 +376,7 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, ++fr.settings.niv; } - len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); + len = nghttp3_frame_write_settings_len(&payloadlen, &fr.settings); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -373,9 +384,10 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings); + chunk->last = + nghttp3_frame_write_settings(chunk->last, &fr.settings, payloadlen); tbuf.buf.last = chunk->last; @@ -389,8 +401,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; + int64_t payloadlen; - len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr); + len = nghttp3_frame_write_goaway_len(&payloadlen, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -398,9 +411,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_goaway(chunk->last, fr); + chunk->last = nghttp3_frame_write_goaway(chunk->last, fr, payloadlen); tbuf.buf.last = chunk->last; @@ -414,8 +427,9 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream, int rv; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; + int64_t payloadlen; - len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr); + len = nghttp3_frame_write_priority_update_len(&payloadlen, fr); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -423,12 +437,52 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); + + chunk->last = + nghttp3_frame_write_priority_update(chunk->last, fr, payloadlen); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_origin(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_origin *fr = &frent->fr.origin; + nghttp3_buf *chunk; + nghttp3_buf buf; + nghttp3_typed_buf tbuf; + int rv; + + rv = nghttp3_stream_ensure_chunk( + stream, nghttp3_frame_write_hd_len(fr->type, (int64_t)fr->origin_list.len)); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr); + chunk->last = + nghttp3_frame_write_hd(chunk->last, fr->type, (int64_t)fr->origin_list.len); tbuf.buf.last = chunk->last; + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + + if (fr->origin_list.len == 0) { + return 0; + } + + nghttp3_buf_wrap_init(&buf, (uint8_t *)fr->origin_list.base, + fr->origin_list.len); + buf.last = buf.end; + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN_NO_ACK); + return nghttp3_stream_outq_add(stream, &tbuf); } @@ -455,26 +509,25 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, size_t len; nghttp3_buf *chunk; nghttp3_typed_buf tbuf; - nghttp3_frame_hd hd; uint8_t raw_pbuf[16]; size_t pbuflen, rbuflen, ebuflen; + int64_t payloadlen; nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf)); rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, stream->node.id, nva, nvlen); if (rv != 0) { - goto fail; + return rv; } pbuflen = nghttp3_buf_len(&pbuf); rbuflen = nghttp3_buf_len(rbuf); ebuflen = nghttp3_buf_len(ebuf); - hd.type = frame_type; - hd.length = (int64_t)(pbuflen + rbuflen); + payloadlen = (int64_t)(pbuflen + rbuflen); - len = nghttp3_frame_write_hd_len(&hd) + pbuflen; + len = nghttp3_frame_write_hd_len(frame_type, payloadlen) + pbuflen; if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { len += rbuflen; @@ -482,13 +535,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { - goto fail; + return rv; } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + chunk->last = nghttp3_frame_write_hd(chunk->last, frame_type, payloadlen); chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); nghttp3_buf_init(&pbuf); @@ -498,13 +551,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE); rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_buf_init(rbuf); } else if (rbuflen) { @@ -513,7 +566,7 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_outq_add(stream, &tbuf); if (rv != 0) { - goto fail; + return rv; } nghttp3_buf_reset(rbuf); } @@ -532,18 +585,18 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen); if (rv != 0) { - goto fail; + return rv; } chunk = nghttp3_stream_get_chunk(qenc_stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_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; + return rv; } nghttp3_buf_reset(ebuf); } @@ -553,10 +606,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream, assert(0 == nghttp3_buf_len(ebuf)); return 0; - -fail: - - return rv; } int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, @@ -570,7 +619,6 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, nghttp3_conn *conn = stream->conn; int64_t datalen; uint32_t flags = 0; - nghttp3_frame_hd hd; nghttp3_vec vec[8]; nghttp3_vec *v; nghttp3_ssize sveccnt; @@ -624,10 +672,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } } - hd.type = NGHTTP3_FRAME_DATA; - hd.length = datalen; - - len = nghttp3_frame_write_hd_len(&hd); + len = nghttp3_frame_write_hd_len(NGHTTP3_FRAME_DATA, datalen); rv = nghttp3_stream_ensure_chunk(stream, len); if (rv != 0) { @@ -635,9 +680,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); - chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + chunk->last = + nghttp3_frame_write_hd(chunk->last, NGHTTP3_FRAME_DATA, datalen); tbuf.buf.last = chunk->last; @@ -690,7 +736,7 @@ int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) { } chunk = nghttp3_stream_get_chunk(stream); - typed_buf_shared_init(&tbuf, chunk); + nghttp3_typed_buf_shared_init(&tbuf, chunk); nghttp3_qpack_decoder_write_decoder(qdec, chunk); @@ -717,17 +763,16 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream, 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) { + dest->buf.end == tbuf->buf.end && 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; + + assert(dest->buf.end == tbuf->buf.end); return 0; } @@ -817,34 +862,24 @@ size_t nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, nghttp3_ringbuf *outq = &stream->outq; size_t len = nghttp3_ringbuf_len(outq); size_t i = stream->outq_idx; - uint64_t offset = stream->outq_offset; size_t buflen; nghttp3_vec *vbegin = vec, *vend = vec + veccnt; nghttp3_typed_buf *tbuf; assert(veccnt > 0); - if (i < len) { + for (; i < len && vec != vend; ++i) { tbuf = nghttp3_ringbuf_get(outq, i); buflen = nghttp3_buf_len(&tbuf->buf); - if (offset < buflen) { - vec->base = tbuf->buf.pos + offset; - vec->len = (size_t)(buflen - offset); - ++vec; - } else { - /* This is the only case that satisfies offset >= buflen */ - assert(0 == offset); - assert(0 == buflen); + if (buflen == 0) { + continue; } - ++i; + vec->base = tbuf->buf.pos; + vec->len = buflen; - 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); - } + ++vec; } /* TODO Rework this if we have finished implementing HTTP @@ -859,26 +894,27 @@ void nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { nghttp3_ringbuf *outq = &stream->outq; size_t i; size_t len = nghttp3_ringbuf_len(outq); - uint64_t offset = stream->outq_offset + n; size_t buflen; nghttp3_typed_buf *tbuf; + stream->unsent_bytes -= n; + 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; + if (n < buflen) { + tbuf->buf.pos += n; + + break; } - break; + tbuf->buf.pos = tbuf->buf.last; + n -= buflen; } - assert(i < len || offset == 0); + assert(i < len || n == 0); - stream->unsent_bytes -= n; stream->outq_idx = i; - stream->outq_offset = offset; } int nghttp3_stream_outq_write_done(nghttp3_stream *stream) { @@ -898,13 +934,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream, nghttp3_buf_free(&tbuf->buf, stream->mem); break; case NGHTTP3_BUF_TYPE_ALIEN: + case NGHTTP3_BUF_TYPE_ALIEN_NO_ACK: break; case NGHTTP3_BUF_TYPE_SHARED: assert(nghttp3_ringbuf_len(chunks)); chunk = nghttp3_ringbuf_get(chunks, 0); - assert(chunk->begin == tbuf->buf.begin); assert(chunk->end == tbuf->buf.end); if (chunk->last == tbuf->buf.last) { @@ -927,14 +963,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream, int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { nghttp3_ringbuf *outq = &stream->outq; size_t buflen; - size_t npopped = 0; uint64_t nack; nghttp3_typed_buf *tbuf; int rv; for (; nghttp3_ringbuf_len(outq);) { tbuf = nghttp3_ringbuf_get(outq, 0); - buflen = nghttp3_buf_len(&tbuf->buf); + buflen = (size_t)(tbuf->buf.last - tbuf->buf.begin); /* For NGHTTP3_BUF_TYPE_ALIEN, we never add 0 length buffer. */ if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN && stream->ack_offset < offset && @@ -949,17 +984,13 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { } } - if (offset >= stream->ack_base + buflen) { + if (stream->outq_idx > 0 && offset >= stream->ack_base + buflen) { stream_pop_outq_entry(stream, tbuf); stream->ack_base += buflen; stream->ack_offset = stream->ack_base; - ++npopped; - if (stream->outq_idx + 1 == npopped) { - stream->outq_offset = 0; - break; - } + --stream->outq_idx; continue; } @@ -967,13 +998,6 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) { 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; @@ -1045,19 +1069,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, switch (stream->rx.hstate) { case NGHTTP3_HTTP_STATE_NONE: - return NGHTTP3_ERR_H3_INTERNAL_ERROR; + nghttp3_unreachable(); 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: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; + + return 0; case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END; return 0; case NGHTTP3_HTTP_STATE_REQ_HEADERS_END: @@ -1080,12 +1102,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_DATA_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END; return 0; case NGHTTP3_HTTP_STATE_REQ_DATA_END: @@ -1108,12 +1128,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END; return 0; case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END: @@ -1129,7 +1147,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; return 0; case NGHTTP3_HTTP_STATE_REQ_END: - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; case NGHTTP3_HTTP_STATE_RESP_INITIAL: if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; @@ -1137,9 +1155,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; return 0; case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END; return 0; case NGHTTP3_HTTP_STATE_RESP_HEADERS_END: @@ -1169,12 +1185,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_DATA_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_DATA_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END; return 0; case NGHTTP3_HTTP_STATE_RESP_DATA_END: @@ -1197,17 +1211,15 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; default: - return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + nghttp3_unreachable(); } case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: - if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; - } + assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event); stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END; return 0; case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END: if (event != NGHTTP3_HTTP_EVENT_MSG_END) { - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; } rv = nghttp3_http_on_remote_end_stream(stream); if (rv != 0) { @@ -1216,7 +1228,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; return 0; case NGHTTP3_HTTP_STATE_RESP_END: - return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; default: nghttp3_unreachable(); } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h index 7d296febf9135f..759cf687a0c108 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h @@ -69,6 +69,8 @@ typedef enum nghttp3_ctrl_stream_state { NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID, NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE, + NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN, + NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN, } nghttp3_ctrl_stream_state; typedef enum nghttp3_req_stream_state { @@ -129,10 +131,6 @@ typedef struct nghttp3_stream_read_state { /* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server received PRIORITY_UPDATE frame for this stream. */ #define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u -/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that - NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while - processing incoming HTTP fields. */ -#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u typedef enum nghttp3_stream_http_state { NGHTTP3_HTTP_STATE_NONE, @@ -223,9 +221,6 @@ struct nghttp3_stream { uint64_t unsent_bytes; /* outq_idx is an index into outq where next write is made. */ size_t outq_idx; - /* outq_offset is write offset relative to the element at outq_idx - in outq. */ - uint64_t outq_offset; /* ack_base is the number of bytes acknowledged by a remote endpoint where the first element in outq is positioned at. */ uint64_t ack_base; @@ -255,7 +250,7 @@ struct nghttp3_stream { }; }; -nghttp3_objalloc_decl(stream, nghttp3_stream, oplent); +nghttp3_objalloc_decl(stream, nghttp3_stream, oplent) typedef struct nghttp3_frame_entry { nghttp3_frame fr; @@ -322,6 +317,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream, int nghttp3_stream_write_priority_update(nghttp3_stream *stream, nghttp3_frame_entry *frent); +int nghttp3_stream_write_origin(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); diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c index 939821d84eac3d..b88e2255b2556d 100644 --- a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c @@ -28,8 +28,11 @@ #include -static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, - NGHTTP3_VERSION}; +static nghttp3_info version = { + .age = NGHTTP3_VERSION_AGE, + .version_num = NGHTTP3_VERSION_NUM, + .version_str = NGHTTP3_VERSION, +}; const nghttp3_info *nghttp3_version(int least_version) { if (least_version > NGHTTP3_VERSION_NUM) { diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/COPYING b/deps/ngtcp2/nghttp3/lib/sfparse/COPYING deleted file mode 100644 index 8212d82d83ab74..00000000000000 --- a/deps/ngtcp2/nghttp3/lib/sfparse/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2023 sfparse contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c deleted file mode 100644 index d0328cf40c21ea..00000000000000 --- a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.c +++ /dev/null @@ -1,1517 +0,0 @@ -/* - * sfparse - * - * Copyright (c) 2023 sfparse contributors - * Copyright (c) 2019 nghttp3 contributors - * Copyright (c) 2015 nghttp2 contributors - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include "sfparse.h" - -#include -#include -#include - -#define SF_STATE_DICT 0x08u -#define SF_STATE_LIST 0x10u -#define SF_STATE_ITEM 0x18u - -#define SF_STATE_INNER_LIST 0x04u - -#define SF_STATE_BEFORE 0x00u -#define SF_STATE_BEFORE_PARAMS 0x01u -#define SF_STATE_PARAMS 0x02u -#define SF_STATE_AFTER 0x03u - -#define SF_STATE_OP_MASK 0x03u - -#define SF_SET_STATE_AFTER(NAME) (SF_STATE_##NAME | SF_STATE_AFTER) -#define SF_SET_STATE_BEFORE_PARAMS(NAME) \ - (SF_STATE_##NAME | SF_STATE_BEFORE_PARAMS) -#define SF_SET_STATE_INNER_LIST_BEFORE(NAME) \ - (SF_STATE_##NAME | SF_STATE_INNER_LIST | SF_STATE_BEFORE) - -#define SF_STATE_DICT_AFTER SF_SET_STATE_AFTER(DICT) -#define SF_STATE_DICT_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(DICT) -#define SF_STATE_DICT_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(DICT) - -#define SF_STATE_LIST_AFTER SF_SET_STATE_AFTER(LIST) -#define SF_STATE_LIST_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(LIST) -#define SF_STATE_LIST_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(LIST) - -#define SF_STATE_ITEM_AFTER SF_SET_STATE_AFTER(ITEM) -#define SF_STATE_ITEM_BEFORE_PARAMS SF_SET_STATE_BEFORE_PARAMS(ITEM) -#define SF_STATE_ITEM_INNER_LIST_BEFORE SF_SET_STATE_INNER_LIST_BEFORE(ITEM) - -#define SF_STATE_INITIAL 0x00u - -#define DIGIT_CASES \ - case '0': \ - case '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9' - -#define LCALPHA_CASES \ - case 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f': \ - case 'g': \ - case 'h': \ - case 'i': \ - case 'j': \ - case 'k': \ - case 'l': \ - case 'm': \ - case 'n': \ - case 'o': \ - case 'p': \ - case 'q': \ - case 'r': \ - case 's': \ - case 't': \ - case 'u': \ - case 'v': \ - case 'w': \ - case 'x': \ - case 'y': \ - case 'z' - -#define UCALPHA_CASES \ - case 'A': \ - case 'B': \ - case 'C': \ - case 'D': \ - case 'E': \ - case 'F': \ - case 'G': \ - case 'H': \ - case 'I': \ - case 'J': \ - case 'K': \ - case 'L': \ - case 'M': \ - case 'N': \ - case 'O': \ - case 'P': \ - case 'Q': \ - case 'R': \ - case 'S': \ - case 'T': \ - case 'U': \ - case 'V': \ - case 'W': \ - case 'X': \ - case 'Y': \ - case 'Z' - -#define ALPHA_CASES \ - UCALPHA_CASES: \ - LCALPHA_CASES - -#define TOKEN_CASES \ - case '!': \ - case '#': \ - case '$': \ - case '%': \ - case '&': \ - case '\'': \ - case '*': \ - case '+': \ - case '-': \ - case '.': \ - case '/': \ - DIGIT_CASES: \ - case ':': \ - UCALPHA_CASES: \ - case '^': \ - case '_': \ - case '`': \ - LCALPHA_CASES: \ - case '|': \ - case '~' - -#define LCHEXALPHA_CASES \ - case 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f' - -#define X00_1F_CASES \ - case 0x00: \ - case 0x01: \ - case 0x02: \ - case 0x03: \ - case 0x04: \ - case 0x05: \ - case 0x06: \ - case 0x07: \ - case 0x08: \ - case 0x09: \ - case 0x0a: \ - case 0x0b: \ - case 0x0c: \ - case 0x0d: \ - case 0x0e: \ - case 0x0f: \ - case 0x10: \ - case 0x11: \ - case 0x12: \ - case 0x13: \ - case 0x14: \ - case 0x15: \ - case 0x16: \ - case 0x17: \ - case 0x18: \ - case 0x19: \ - case 0x1a: \ - case 0x1b: \ - case 0x1c: \ - case 0x1d: \ - case 0x1e: \ - case 0x1f - -#define X20_21_CASES \ - case ' ': \ - case '!' - -#define X23_5B_CASES \ - case '#': \ - case '$': \ - case '%': \ - case '&': \ - case '\'': \ - case '(': \ - case ')': \ - case '*': \ - case '+': \ - case ',': \ - case '-': \ - case '.': \ - case '/': \ - DIGIT_CASES: \ - case ':': \ - case ';': \ - case '<': \ - case '=': \ - case '>': \ - case '?': \ - case '@': \ - UCALPHA_CASES: \ - case '[' - -#define X5D_7E_CASES \ - case ']': \ - case '^': \ - case '_': \ - case '`': \ - LCALPHA_CASES: \ - case '{': \ - case '|': \ - case '}': \ - case '~' - -#define X7F_FF_CASES \ - case 0x7f: \ - case 0x80: \ - case 0x81: \ - case 0x82: \ - case 0x83: \ - case 0x84: \ - case 0x85: \ - case 0x86: \ - case 0x87: \ - case 0x88: \ - case 0x89: \ - case 0x8a: \ - case 0x8b: \ - case 0x8c: \ - case 0x8d: \ - case 0x8e: \ - case 0x8f: \ - case 0x90: \ - case 0x91: \ - case 0x92: \ - case 0x93: \ - case 0x94: \ - case 0x95: \ - case 0x96: \ - case 0x97: \ - case 0x98: \ - case 0x99: \ - case 0x9a: \ - case 0x9b: \ - case 0x9c: \ - case 0x9d: \ - case 0x9e: \ - case 0x9f: \ - case 0xa0: \ - case 0xa1: \ - case 0xa2: \ - case 0xa3: \ - case 0xa4: \ - case 0xa5: \ - case 0xa6: \ - case 0xa7: \ - case 0xa8: \ - case 0xa9: \ - case 0xaa: \ - case 0xab: \ - case 0xac: \ - case 0xad: \ - case 0xae: \ - case 0xaf: \ - case 0xb0: \ - case 0xb1: \ - case 0xb2: \ - case 0xb3: \ - case 0xb4: \ - case 0xb5: \ - case 0xb6: \ - case 0xb7: \ - case 0xb8: \ - case 0xb9: \ - case 0xba: \ - case 0xbb: \ - case 0xbc: \ - case 0xbd: \ - case 0xbe: \ - case 0xbf: \ - case 0xc0: \ - case 0xc1: \ - case 0xc2: \ - case 0xc3: \ - case 0xc4: \ - case 0xc5: \ - case 0xc6: \ - case 0xc7: \ - case 0xc8: \ - case 0xc9: \ - case 0xca: \ - case 0xcb: \ - case 0xcc: \ - case 0xcd: \ - case 0xce: \ - case 0xcf: \ - case 0xd0: \ - case 0xd1: \ - case 0xd2: \ - case 0xd3: \ - case 0xd4: \ - case 0xd5: \ - case 0xd6: \ - case 0xd7: \ - case 0xd8: \ - case 0xd9: \ - case 0xda: \ - case 0xdb: \ - case 0xdc: \ - case 0xdd: \ - case 0xde: \ - case 0xdf: \ - case 0xe0: \ - case 0xe1: \ - case 0xe2: \ - case 0xe3: \ - case 0xe4: \ - case 0xe5: \ - case 0xe6: \ - case 0xe7: \ - case 0xe8: \ - case 0xe9: \ - case 0xea: \ - case 0xeb: \ - case 0xec: \ - case 0xed: \ - case 0xee: \ - case 0xef: \ - case 0xf0: \ - case 0xf1: \ - case 0xf2: \ - case 0xf3: \ - case 0xf4: \ - case 0xf5: \ - case 0xf6: \ - case 0xf7: \ - case 0xf8: \ - case 0xf9: \ - case 0xfa: \ - case 0xfb: \ - case 0xfc: \ - case 0xfd: \ - case 0xfe: \ - case 0xff - -static int is_ws(uint8_t c) { - switch (c) { - case ' ': - case '\t': - return 1; - default: - return 0; - } -} - -static int parser_eof(sf_parser *sfp) { return sfp->pos == sfp->end; } - -static void parser_discard_ows(sf_parser *sfp) { - for (; !parser_eof(sfp) && is_ws(*sfp->pos); ++sfp->pos) - ; -} - -static void parser_discard_sp(sf_parser *sfp) { - for (; !parser_eof(sfp) && *sfp->pos == ' '; ++sfp->pos) - ; -} - -static void parser_set_op_state(sf_parser *sfp, uint32_t op) { - sfp->state &= ~SF_STATE_OP_MASK; - sfp->state |= op; -} - -static void parser_unset_inner_list_state(sf_parser *sfp) { - sfp->state &= ~SF_STATE_INNER_LIST; -} - -static int parser_key(sf_parser *sfp, sf_vec *dest) { - const uint8_t *base; - - switch (*sfp->pos) { - case '*': - LCALPHA_CASES: - break; - default: - return SF_ERR_PARSE_ERROR; - } - - base = sfp->pos++; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - case '_': - case '-': - case '.': - case '*': - DIGIT_CASES: - LCALPHA_CASES: - continue; - } - - break; - } - - if (dest) { - dest->base = (uint8_t *)base; - dest->len = (size_t)(sfp->pos - dest->base); - } - - return 0; -} - -static int parser_number(sf_parser *sfp, sf_value *dest) { - int sign = 1; - int64_t value = 0; - size_t len = 0; - size_t fpos = 0; - - if (*sfp->pos == '-') { - ++sfp->pos; - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - sign = -1; - } - - assert(!parser_eof(sfp)); - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - DIGIT_CASES: - if (++len > 15) { - return SF_ERR_PARSE_ERROR; - } - - value *= 10; - value += *sfp->pos - '0'; - - continue; - } - - break; - } - - if (len == 0) { - return SF_ERR_PARSE_ERROR; - } - - if (parser_eof(sfp) || *sfp->pos != '.') { - if (dest) { - dest->type = SF_TYPE_INTEGER; - dest->flags = SF_VALUE_FLAG_NONE; - dest->integer = value * sign; - } - - return 0; - } - - /* decimal */ - - if (len > 12) { - return SF_ERR_PARSE_ERROR; - } - - fpos = len; - - ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - DIGIT_CASES: - if (++len > 15) { - return SF_ERR_PARSE_ERROR; - } - - value *= 10; - value += *sfp->pos - '0'; - - continue; - } - - break; - } - - if (fpos == len || len - fpos > 3) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - dest->type = SF_TYPE_DECIMAL; - dest->flags = SF_VALUE_FLAG_NONE; - dest->decimal.numer = value * sign; - - switch (len - fpos) { - case 1: - dest->decimal.denom = 10; - - break; - case 2: - dest->decimal.denom = 100; - - break; - case 3: - dest->decimal.denom = 1000; - - break; - } - } - - return 0; -} - -static int parser_date(sf_parser *sfp, sf_value *dest) { - int rv; - sf_value val; - - /* The first byte has already been validated by the caller. */ - assert('@' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - rv = parser_number(sfp, &val); - if (rv != 0) { - return rv; - } - - if (val.type != SF_TYPE_INTEGER) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - *dest = val; - dest->type = SF_TYPE_DATE; - } - - return 0; -} - -static int parser_string(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - uint32_t flags = SF_VALUE_FLAG_NONE; - - /* The first byte has already been validated by the caller. */ - assert('"' == *sfp->pos); - - base = ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - X20_21_CASES: - X23_5B_CASES: - X5D_7E_CASES: - break; - case '\\': - ++sfp->pos; - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case '"': - case '\\': - flags = SF_VALUE_FLAG_ESCAPED_STRING; - - break; - default: - return SF_ERR_PARSE_ERROR; - } - - break; - case '"': - if (dest) { - dest->type = SF_TYPE_STRING; - dest->flags = flags; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; - default: - return SF_ERR_PARSE_ERROR; - } - } - - return SF_ERR_PARSE_ERROR; -} - -static int parser_token(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - - /* The first byte has already been validated by the caller. */ - base = sfp->pos++; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - TOKEN_CASES: - continue; - } - - break; - } - - if (dest) { - dest->type = SF_TYPE_TOKEN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.base = (uint8_t *)base; - dest->vec.len = (size_t)(sfp->pos - base); - } - - return 0; -} - -static int parser_byteseq(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - - /* The first byte has already been validated by the caller. */ - assert(':' == *sfp->pos); - - base = ++sfp->pos; - - for (; !parser_eof(sfp); ++sfp->pos) { - switch (*sfp->pos) { - case '+': - case '/': - DIGIT_CASES: - ALPHA_CASES: - continue; - case '=': - switch ((sfp->pos - base) & 0x3) { - case 0: - case 1: - return SF_ERR_PARSE_ERROR; - case 2: - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - if (*sfp->pos == '=') { - ++sfp->pos; - } - - break; - case 3: - ++sfp->pos; - - break; - } - - if (parser_eof(sfp) || *sfp->pos != ':') { - return SF_ERR_PARSE_ERROR; - } - - goto fin; - case ':': - if (((sfp->pos - base) & 0x3) == 1) { - return SF_ERR_PARSE_ERROR; - } - - goto fin; - default: - return SF_ERR_PARSE_ERROR; - } - } - - return SF_ERR_PARSE_ERROR; - -fin: - if (dest) { - dest->type = SF_TYPE_BYTESEQ; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; -} - -static int parser_boolean(sf_parser *sfp, sf_value *dest) { - int b; - - /* The first byte has already been validated by the caller. */ - assert('?' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case '0': - b = 0; - - break; - case '1': - b = 1; - - break; - default: - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - - if (dest) { - dest->type = SF_TYPE_BOOLEAN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->boolean = b; - } - - return 0; -} - -static int pctdecode(uint8_t *pc, const uint8_t **ppos) { - uint8_t c, b = **ppos; - - switch (b) { - DIGIT_CASES: - c = (uint8_t)((b - '0') << 4); - - break; - LCHEXALPHA_CASES: - c = (uint8_t)((b - 'a' + 10) << 4); - - break; - default: - return -1; - } - - b = *++*ppos; - - switch (b) { - DIGIT_CASES: - c |= (uint8_t)(b - '0'); - - break; - LCHEXALPHA_CASES: - c |= (uint8_t)(b - 'a' + 10); - - break; - default: - return -1; - } - - *pc = c; - ++*ppos; - - return 0; -} - -/* Start of utf8 dfa */ -/* Copyright (c) 2008-2010 Bjoern Hoehrmann - * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. - * - * Copyright (c) 2008-2009 Bjoern Hoehrmann - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#define UTF8_ACCEPT 0 -#define UTF8_REJECT 12 - -/* clang-format off */ -static const uint8_t utf8d[] = { - /* - * The first part of the table maps bytes to character classes that - * to reduce the size of the transition table and create bitmasks. - */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, - - /* - * The second part is a transition table that maps a combination - * of a state of the automaton and a character class to a state. - */ - 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, - 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, - 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, - 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, - 12,36,12,12,12,12,12,12,12,12,12,12, -}; -/* clang-format on */ - -static void utf8_decode(uint32_t *state, uint8_t byte) { - *state = utf8d[256 + *state + utf8d[byte]]; -} - -/* End of utf8 dfa */ - -static int parser_dispstring(sf_parser *sfp, sf_value *dest) { - const uint8_t *base; - uint8_t c; - uint32_t utf8state = UTF8_ACCEPT; - - assert('%' == *sfp->pos); - - ++sfp->pos; - - if (parser_eof(sfp) || *sfp->pos != '"') { - return SF_ERR_PARSE_ERROR; - } - - base = ++sfp->pos; - - for (; !parser_eof(sfp);) { - switch (*sfp->pos) { - X00_1F_CASES: - X7F_FF_CASES: - return SF_ERR_PARSE_ERROR; - case '%': - ++sfp->pos; - - if (sfp->pos + 2 > sfp->end) { - return SF_ERR_PARSE_ERROR; - } - - if (pctdecode(&c, &sfp->pos) != 0) { - return SF_ERR_PARSE_ERROR; - } - - utf8_decode(&utf8state, c); - if (utf8state == UTF8_REJECT) { - return SF_ERR_PARSE_ERROR; - } - - break; - case '"': - if (utf8state != UTF8_ACCEPT) { - return SF_ERR_PARSE_ERROR; - } - - if (dest) { - dest->type = SF_TYPE_DISPSTRING; - dest->flags = SF_VALUE_FLAG_NONE; - dest->vec.len = (size_t)(sfp->pos - base); - dest->vec.base = dest->vec.len == 0 ? NULL : (uint8_t *)base; - } - - ++sfp->pos; - - return 0; - default: - if (utf8state != UTF8_ACCEPT) { - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - } - } - - return SF_ERR_PARSE_ERROR; -} - -static int parser_bare_item(sf_parser *sfp, sf_value *dest) { - switch (*sfp->pos) { - case '"': - return parser_string(sfp, dest); - case '-': - DIGIT_CASES: - return parser_number(sfp, dest); - case '@': - return parser_date(sfp, dest); - case ':': - return parser_byteseq(sfp, dest); - case '?': - return parser_boolean(sfp, dest); - case '*': - ALPHA_CASES: - return parser_token(sfp, dest); - case '%': - return parser_dispstring(sfp, dest); - default: - return SF_ERR_PARSE_ERROR; - } -} - -static int parser_skip_inner_list(sf_parser *sfp); - -int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { - int rv; - - switch (sfp->state & SF_STATE_OP_MASK) { - case SF_STATE_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_BEFORE_PARAMS: - parser_set_op_state(sfp, SF_STATE_PARAMS); - - break; - case SF_STATE_PARAMS: - break; - default: - assert(0); - abort(); - } - - if (parser_eof(sfp) || *sfp->pos != ';') { - parser_set_op_state(sfp, SF_STATE_AFTER); - - return SF_ERR_EOF; - } - - ++sfp->pos; - - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - rv = parser_key(sfp, dest_key); - if (rv != 0) { - return rv; - } - - if (parser_eof(sfp) || *sfp->pos != '=') { - if (dest_value) { - dest_value->type = SF_TYPE_BOOLEAN; - dest_value->flags = SF_VALUE_FLAG_NONE; - dest_value->boolean = 1; - } - - return 0; - } - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return parser_bare_item(sfp, dest_value); -} - -static int parser_skip_params(sf_parser *sfp) { - int rv; - - for (;;) { - rv = sf_parser_param(sfp, NULL, NULL); - switch (rv) { - case 0: - break; - case SF_ERR_EOF: - return 0; - case SF_ERR_PARSE_ERROR: - return rv; - default: - assert(0); - abort(); - } - } -} - -int sf_parser_inner_list(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state & SF_STATE_OP_MASK) { - case SF_STATE_BEFORE: - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case SF_STATE_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* Technically, we are entering SF_STATE_AFTER, but we will set - another state without reading the state. */ - /* parser_set_op_state(sfp, SF_STATE_AFTER); */ - - /* fall through */ - case SF_STATE_AFTER: - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - switch (*sfp->pos) { - case ' ': - parser_discard_sp(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case ')': - break; - default: - return SF_ERR_PARSE_ERROR; - } - - break; - default: - assert(0); - abort(); - } - - if (*sfp->pos == ')') { - ++sfp->pos; - - parser_unset_inner_list_state(sfp); - parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); - - return SF_ERR_EOF; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - parser_set_op_state(sfp, SF_STATE_BEFORE_PARAMS); - - return 0; -} - -static int parser_skip_inner_list(sf_parser *sfp) { - int rv; - - for (;;) { - rv = sf_parser_inner_list(sfp, NULL); - switch (rv) { - case 0: - break; - case SF_ERR_EOF: - return 0; - case SF_ERR_PARSE_ERROR: - return rv; - default: - assert(0); - abort(); - } - } -} - -static int parser_next_key_or_item(sf_parser *sfp) { - parser_discard_ows(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - if (*sfp->pos != ',') { - return SF_ERR_PARSE_ERROR; - } - - ++sfp->pos; - - parser_discard_ows(sfp); - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return 0; -} - -static int parser_dict_value(sf_parser *sfp, sf_value *dest) { - int rv; - - if (parser_eof(sfp) || *(sfp->pos) != '=') { - /* Boolean true */ - if (dest) { - dest->type = SF_TYPE_BOOLEAN; - dest->flags = SF_VALUE_FLAG_NONE; - dest->boolean = 1; - } - - sfp->state = SF_STATE_DICT_BEFORE_PARAMS; - - return 0; - } - - ++sfp->pos; - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_DICT_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_DICT_BEFORE_PARAMS; - - return 0; -} - -int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value) { - int rv; - - switch (sfp->state) { - case SF_STATE_DICT_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_DICT_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_DICT_AFTER: - rv = parser_next_key_or_item(sfp); - if (rv != 0) { - return rv; - } - - break; - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - break; - default: - assert(0); - abort(); - } - - rv = parser_key(sfp, dest_key); - if (rv != 0) { - return rv; - } - - return parser_dict_value(sfp, dest_value); -} - -int sf_parser_list(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state) { - case SF_STATE_LIST_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_LIST_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_LIST_AFTER: - rv = parser_next_key_or_item(sfp); - if (rv != 0) { - return rv; - } - - break; - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_EOF; - } - - break; - default: - assert(0); - abort(); - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_LIST_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_LIST_BEFORE_PARAMS; - - return 0; -} - -int sf_parser_item(sf_parser *sfp, sf_value *dest) { - int rv; - - switch (sfp->state) { - case SF_STATE_INITIAL: - parser_discard_sp(sfp); - - if (parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - break; - case SF_STATE_ITEM_INNER_LIST_BEFORE: - rv = parser_skip_inner_list(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_ITEM_BEFORE_PARAMS: - rv = parser_skip_params(sfp); - if (rv != 0) { - return rv; - } - - /* fall through */ - case SF_STATE_ITEM_AFTER: - parser_discard_sp(sfp); - - if (!parser_eof(sfp)) { - return SF_ERR_PARSE_ERROR; - } - - return SF_ERR_EOF; - default: - assert(0); - abort(); - } - - if (*sfp->pos == '(') { - if (dest) { - dest->type = SF_TYPE_INNER_LIST; - dest->flags = SF_VALUE_FLAG_NONE; - } - - ++sfp->pos; - - sfp->state = SF_STATE_ITEM_INNER_LIST_BEFORE; - - return 0; - } - - rv = parser_bare_item(sfp, dest); - if (rv != 0) { - return rv; - } - - sfp->state = SF_STATE_ITEM_BEFORE_PARAMS; - - return 0; -} - -void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen) { - if (datalen == 0) { - sfp->pos = sfp->end = NULL; - } else { - sfp->pos = data; - sfp->end = data + datalen; - } - - sfp->state = SF_STATE_INITIAL; -} - -void sf_unescape(sf_vec *dest, const sf_vec *src) { - const uint8_t *p, *q; - uint8_t *o; - size_t len, slen; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - len = src->len; - - for (;;) { - q = memchr(p, '\\', len); - if (q == NULL) { - memcpy(o, p, len); - o += len; - - dest->len = (size_t)(o - dest->base); - - return; - } - - slen = (size_t)(q - p); - memcpy(o, p, slen); - o += slen; - - p = q + 1; - *o++ = *p++; - len -= slen + 2; - } -} - -void sf_base64decode(sf_vec *dest, const sf_vec *src) { - static const int index_tbl[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1}; - uint8_t *o; - const uint8_t *p, *end; - uint32_t n; - size_t i, left; - int idx; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - left = src->len & 0x3; - if (left == 0 && src->base[src->len - 1] == '=') { - left = 4; - } - end = src->base + src->len - left; - - for (; p != end;) { - n = 0; - - for (i = 1; i <= 4; ++i, ++p) { - idx = index_tbl[*p]; - - assert(idx != -1); - - n += (uint32_t)(idx << (24 - i * 6)); - } - - *o++ = (uint8_t)(n >> 16); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; - } - - switch (left) { - case 0: - goto fin; - case 1: - assert(0); - abort(); - case 3: - if (src->base[src->len - 1] == '=') { - left = 2; - } - - break; - case 4: - assert('=' == src->base[src->len - 1]); - - if (src->base[src->len - 2] == '=') { - left = 2; - } else { - left = 3; - } - - break; - } - - switch (left) { - case 2: - *o = (uint8_t)(index_tbl[*p++] << 2); - *o++ |= (uint8_t)(index_tbl[*p++] >> 4); - - break; - case 3: - n = (uint32_t)(index_tbl[*p++] << 10); - n += (uint32_t)(index_tbl[*p++] << 4); - n += (uint32_t)(index_tbl[*p++] >> 2); - *o++ = (n >> 8) & 0xffu; - *o++ = n & 0xffu; - - break; - } - -fin: - dest->len = (size_t)(o - dest->base); -} - -void sf_pctdecode(sf_vec *dest, const sf_vec *src) { - const uint8_t *p, *q; - uint8_t *o; - size_t len, slen; - - if (src->len == 0) { - dest->len = 0; - - return; - } - - o = dest->base; - p = src->base; - len = src->len; - - for (;;) { - q = memchr(p, '%', len); - if (q == NULL) { - memcpy(o, p, len); - o += len; - - dest->len = (size_t)(o - dest->base); - - return; - } - - slen = (size_t)(q - p); - memcpy(o, p, slen); - o += slen; - - p = q + 1; - - pctdecode(o++, &p); - - len -= slen + 3; - } -} diff --git a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h index 01cc947d4d61bc..9341221a099438 100644 --- a/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h +++ b/deps/ngtcp2/nghttp3/lib/sfparse/sfparse.h @@ -31,90 +31,90 @@ libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 -#endif +#endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */ #ifdef __cplusplus extern "C" { -#endif +#endif /* defined(__cplusplus) */ #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) */ +#else /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ # include -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */ #include #include /** * @enum * - * :type:`sf_type` defines value type. + * :type:`sfparse_type` defines value type. */ -typedef enum sf_type { +typedef enum sfparse_type { /** - * :enum:`SF_TYPE_BOOLEAN` indicates boolean type. + * :enum:`SFPARSE_TYPE_BOOLEAN` indicates boolean type. */ - SF_TYPE_BOOLEAN, + SFPARSE_TYPE_BOOLEAN, /** - * :enum:`SF_TYPE_INTEGER` indicates integer type. + * :enum:`SFPARSE_TYPE_INTEGER` indicates integer type. */ - SF_TYPE_INTEGER, + SFPARSE_TYPE_INTEGER, /** - * :enum:`SF_TYPE_DECIMAL` indicates decimal type. + * :enum:`SFPARSE_TYPE_DECIMAL` indicates decimal type. */ - SF_TYPE_DECIMAL, + SFPARSE_TYPE_DECIMAL, /** - * :enum:`SF_TYPE_STRING` indicates string type. + * :enum:`SFPARSE_TYPE_STRING` indicates string type. */ - SF_TYPE_STRING, + SFPARSE_TYPE_STRING, /** - * :enum:`SF_TYPE_TOKEN` indicates token type. + * :enum:`SFPARSE_TYPE_TOKEN` indicates token type. */ - SF_TYPE_TOKEN, + SFPARSE_TYPE_TOKEN, /** - * :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type. + * :enum:`SFPARSE_TYPE_BYTESEQ` indicates byte sequence type. */ - SF_TYPE_BYTESEQ, + SFPARSE_TYPE_BYTESEQ, /** - * :enum:`SF_TYPE_INNER_LIST` indicates inner list type. + * :enum:`SFPARSE_TYPE_INNER_LIST` indicates inner list type. */ - SF_TYPE_INNER_LIST, + SFPARSE_TYPE_INNER_LIST, /** - * :enum:`SF_TYPE_DATE` indicates date type. + * :enum:`SFPARSE_TYPE_DATE` indicates date type. */ - SF_TYPE_DATE, + SFPARSE_TYPE_DATE, /** - * :enum:`SF_TYPE_DISPSTRING` indicates display string type. + * :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type. */ - SF_TYPE_DISPSTRING -} sf_type; + SFPARSE_TYPE_DISPSTRING +} sfparse_type; /** * @macro * - * :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has + * :macro:`SFPARSE_ERR_PARSE` indicates fatal parse error has * occurred, and it is not possible to continue the processing. */ -#define SF_ERR_PARSE_ERROR -1 +#define SFPARSE_ERR_PARSE -1 /** * @macro * - * :macro:`SF_ERR_EOF` indicates that there is nothing left to read. - * The context of this error varies depending on the function that - * returns this error code. + * :macro:`SFPARSE_ERR_EOF` indicates that there is nothing left to + * read. The context of this error varies depending on the function + * that returns this error code. */ -#define SF_ERR_EOF -2 +#define SFPARSE_ERR_EOF -2 /** * @struct * - * :type:`sf_vec` stores sequence of bytes. + * :type:`sfparse_vec` stores sequence of bytes. */ -typedef struct sf_vec { +typedef struct sfparse_vec { /** * :member:`base` points to the beginning of the sequence of bytes. */ @@ -123,29 +123,29 @@ typedef struct sf_vec { * :member:`len` is the number of bytes contained in this sequence. */ size_t len; -} sf_vec; +} sfparse_vec; /** * @macro * - * :macro:`SF_VALUE_FLAG_NONE` indicates no flag set. + * :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set. */ -#define SF_VALUE_FLAG_NONE 0x0u +#define SFPARSE_VALUE_FLAG_NONE 0x0u /** * @macro * - * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string + * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string * contains escaped character(s). */ -#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u +#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u /** * @struct * - * :type:`sf_decimal` contains decimal value. + * :type:`sfparse_decimal` contains decimal value. */ -typedef struct sf_decimal { +typedef struct sfparse_decimal { /** * :member:`numer` contains numerator of the decimal value. */ @@ -154,275 +154,289 @@ typedef struct sf_decimal { * :member:`denom` contains denominator of the decimal value. */ int64_t denom; -} sf_decimal; +} sfparse_decimal; /** * @struct * - * :type:`sf_value` stores a Structured Field item. For Inner List, - * only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`. In order - * to read the items contained in an inner list, call - * `sf_parser_inner_list`. + * :type:`sfparse_value` stores a Structured Field item. For Inner + * List, only type is set to + * :enum:`sfparse_type.SFPARSE_TYPE_INNER_LIST`. In order to read the + * items contained in an inner list, call `sfparse_parser_inner_list`. */ -typedef struct sf_value { +typedef struct sfparse_value { /** * :member:`type` is the type of the value contained in this * particular object. */ - sf_type type; + sfparse_type type; /** * :member:`flags` is bitwise OR of one or more of - * :macro:`SF_VALUE_FLAG_* `. + * :macro:`SFPARSE_VALUE_FLAG_* `. */ uint32_t flags; /** * @anonunion_start * - * @sf_value_value + * @sfparse_value_value */ union { /** * :member:`boolean` contains boolean value if :member:`type` == - * :enum:`sf_type.SF_TYPE_BOOLEAN`. 1 indicates true, and 0 - * indicates false. + * :enum:`sfparse_type.SFPARSE_TYPE_BOOLEAN`. 1 indicates true, + * and 0 indicates false. */ int boolean; /** * :member:`integer` contains integer value if :member:`type` is - * either :enum:`sf_type.SF_TYPE_INTEGER` or - * :enum:`sf_type.SF_TYPE_DATE`. + * either :enum:`sfparse_type.SFPARSE_TYPE_INTEGER` or + * :enum:`sfparse_type.SFPARSE_TYPE_DATE`. */ int64_t integer; /** * :member:`decimal` contains decimal value if :member:`type` == - * :enum:`sf_type.SF_TYPE_DECIMAL`. + * :enum:`sfparse_type.SFPARSE_TYPE_DECIMAL`. */ - sf_decimal decimal; + sfparse_decimal decimal; /** * :member:`vec` contains sequence of bytes if :member:`type` is - * either :enum:`sf_type.SF_TYPE_STRING`, - * :enum:`sf_type.SF_TYPE_TOKEN`, :enum:`sf_type.SF_TYPE_BYTESEQ`, - * or :enum:`sf_type.SF_TYPE_DISPSTRING`. + * either :enum:`sfparse_type.SFPARSE_TYPE_STRING`, + * :enum:`sfparse_type.SFPARSE_TYPE_TOKEN`, + * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, or + * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`. * - * For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or - * more escaped characters if :member:`flags` has - * :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set. To unescape the - * string, use `sf_unescape`. + * For :enum:`sfparse_type.SFPARSE_TYPE_STRING`, this field + * contains one or more escaped characters if :member:`flags` has + * :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` set. To unescape + * the string, use `sfparse_unescape`. * - * For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64 - * encoded string. To decode this byte string, use - * `sf_base64decode`. + * For :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, this field + * contains base64 encoded string. To decode this byte string, + * use `sfparse_base64decode`. * - * For :enum:`sf_type.SF_TYPE_DISPSTRING`, this field may contain - * percent-encoded UTF-8 byte sequences. To decode it, use - * `sf_pctdecode`. + * For :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`, this field + * may contain percent-encoded UTF-8 byte sequences. To decode + * it, use `sfparse_pctdecode`. * - * If :member:`vec.len ` == 0, :member:`vec.base - * ` is guaranteed to be NULL. + * If :member:`vec.len ` == 0, :member:`vec.base + * ` is guaranteed to be NULL. */ - sf_vec vec; + sfparse_vec vec; /** * @anonunion_end */ }; -} sf_value; +} sfparse_value; /** * @struct * - * :type:`sf_parser` is the Structured Field Values parser. Use - * `sf_parser_init` to initialize it. + * :type:`sfparse_parser` is the Structured Field Values parser. Use + * `sfparse_parser_init` to initialize it. */ -typedef struct sf_parser { +typedef struct sfparse_parser { /* all fields are private */ const uint8_t *pos; const uint8_t *end; uint32_t state; -} sf_parser; +} sfparse_parser; /** * @function * - * `sf_parser_init` initializes |sfp| with the given buffer pointed by - * |data| of length |datalen|. + * `sfparse_parser_init` initializes |sfp| with the given data encoded + * in Structured Field Values pointed by |data| of length |datalen|. */ -void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen); +void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data, + size_t datalen); /** * @function * - * `sf_parser_param` reads a parameter. If this function returns 0, - * it stores parameter key and value in |dest_key| and |dest_value| + * `sfparse_parser_param` reads a parameter. If this function returns + * 0, it stores parameter key and value in |dest_key| and |dest_value| * respectively, if they are not NULL. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all parameters have - * read, and caller can continue to read rest of the values. If it - * returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all parameters + * have read, and caller can continue to read rest of the values. If + * it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error * while parsing field value. */ -int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); +int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function * - * `sf_parser_dict` reads the next dictionary key and value pair. If - * this function returns 0, it stores the key and value in |dest_key| - * and |dest_value| respectively, if they are not NULL. + * `sfparse_parser_dict` reads the next dictionary key and value pair. + * If this function returns 0, it stores the key and value in + * |dest_key| and |dest_value| respectively, if they are not NULL. * * Caller can optionally read parameters attached to the pair by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * This function does no effort to find duplicated keys. Same key may * be reported more than once. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all key and value - * pairs have been read, and there is nothing left to read. + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all key and + * value pairs have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the dictionary have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value); +int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key, + sfparse_value *dest_value); /** * @function * - * `sf_parser_list` reads the next list item. If this function + * `sfparse_parser_list` reads the next list item. If this function * returns 0, it stores the item in |dest| if it is not NULL. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all values in the - * list have been read, and there is nothing left to read. + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in + * the list have been read, and there is nothing left to read. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the list have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_list(sf_parser *sfp, sf_value *dest); +int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_parser_item` reads a single item. If this function returns 0, - * it stores the item in |dest| if it is not NULL. + * `sfparse_parser_item` reads a single item. If this function + * returns 0, it stores the item in |dest| if it is not NULL. * * This function is only used for the field value that consists of a * single item. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should call this function again to make sure that there is * nothing left to read. If this 2nd function call returns - * :macro:`SF_ERR_EOF`, all data have been processed successfully. + * :macro:`SFPARSE_ERR_EOF`, all data have been processed + * successfully. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * There is nothing left to read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_item(sf_parser *sfp, sf_value *dest); +int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_parser_inner_list` reads the next inner list item. If this - * function returns 0, it stores the item in |dest| if it is not NULL. + * `sfparse_parser_inner_list` reads the next inner list item. If + * this function returns 0, it stores the item in |dest| if it is not + * NULL. * * Caller can optionally read parameters attached to the item by - * calling `sf_parser_param`. + * calling `sfparse_parser_param`. * * Caller should keep calling this function until it returns negative - * error code. If it returns :macro:`SF_ERR_EOF`, all values in this - * inner list have been read, and caller can optionally read + * error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in + * this inner list have been read, and caller can optionally read * parameters attached to this inner list by calling - * `sf_parser_param`. Then caller can continue to read rest of the - * values. + * `sfparse_parser_param`. Then caller can continue to read rest of + * the values. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :macro:`SF_ERR_EOF` + * :macro:`SFPARSE_ERR_EOF` * All values in the inner list have read. - * :macro:`SF_ERR_PARSE_ERROR` + * :macro:`SFPARSE_ERR_PARSE` * It encountered fatal error while parsing field value. */ -int sf_parser_inner_list(sf_parser *sfp, sf_value *dest); +int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest); /** * @function * - * `sf_unescape` copies |src| to |dest| by removing escapes (``\``). - * |src| should be the pointer to :member:`sf_value.vec` of type - * :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`, - * `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or - * `sf_parser_param`, otherwise the behavior is undefined. + * `sfparse_unescape` copies |src| to |dest| by removing escapes + * (``\``). |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_STRING` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the unescaped string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the unescaped string. The + * memory areas pointed by :member:`dest->base ` and + * :member:`src->base ` must not overlap. * * This function sets the length of unescaped string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_unescape(sf_vec *dest, const sf_vec *src); +void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src); /** * @function * - * `sf_base64decode` decodes Base64 encoded string |src| and writes - * the result into |dest|. |src| should be the pointer to - * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ` - * produced by either `sf_parser_dict`, `sf_parser_list`, - * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, - * otherwise the behavior is undefined. + * `sfparse_base64decode` decodes Base64 encoded string |src| and + * writes the result into |dest|. |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the decoded byte string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the decoded byte string. * * This function sets the length of decoded byte string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_base64decode(sf_vec *dest, const sf_vec *src); +void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src); /** * @function * - * `sf_pctdecode` decodes percent-encoded string |src| and writes the - * result into |dest|. |src| should be the pointer to - * :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_DISPSTRING` - * produced by either `sf_parser_dict`, `sf_parser_list`, - * `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`, - * otherwise the behavior is undefined. + * `sfparse_pctdecode` decodes percent-encoded string |src| and writes + * the result into |dest|. |src| should be the pointer to + * :member:`sfparse_value.vec` of type + * :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING` produced by either + * `sfparse_parser_dict`, `sfparse_parser_list`, + * `sfparse_parser_inner_list`, `sfparse_parser_item`, or + * `sfparse_parser_param`, otherwise the behavior is undefined. * - * :member:`dest->base ` must point to the buffer that - * has sufficient space to store the decoded byte string. + * :member:`dest->base ` must point to the buffer + * that has sufficient space to store the decoded byte string. The + * memory areas pointed by :member:`dest->base ` and + * :member:`src->base ` must not overlap. * * This function sets the length of decoded byte string to - * :member:`dest->len `. + * :member:`dest->len `. */ -void sf_pctdecode(sf_vec *dest, const sf_vec *src); +void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src); #ifdef __cplusplus } -#endif +#endif /* defined(__cplusplus) */ -#endif /* SFPARSE_H */ +#endif /* !defined(SFPARSE_H) */ diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index 0593c6c32a7805..e166d94534d002 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -57,6 +57,7 @@ 'nghttp3_sources': [ 'nghttp3/lib/nghttp3_balloc.c', 'nghttp3/lib/nghttp3_buf.c', + 'nghttp3/lib/nghttp3_callbacks.c', 'nghttp3/lib/nghttp3_conn.c', 'nghttp3/lib/nghttp3_conv.c', 'nghttp3/lib/nghttp3_debug.c', @@ -77,14 +78,13 @@ 'nghttp3/lib/nghttp3_range.c', 'nghttp3/lib/nghttp3_rcbuf.c', 'nghttp3/lib/nghttp3_ringbuf.c', + 'nghttp3/lib/nghttp3_settings.c', 'nghttp3/lib/nghttp3_str.c', 'nghttp3/lib/nghttp3_stream.c', 'nghttp3/lib/nghttp3_tnode.c', 'nghttp3/lib/nghttp3_unreachable.c', 'nghttp3/lib/nghttp3_vec.c', 'nghttp3/lib/nghttp3_version.c', - # sfparse is also used by nghttp2 and is included by nghttp2.gyp - # 'nghttp3/lib/sfparse.c' ] }, 'targets': [ From ce920869bfc24c9a88ff906d293862b28110a362 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 27 Jul 2025 14:34:12 -0700 Subject: [PATCH 4/4] quic: update the guard to check openssl version Since we need to be able to use the openssl adapter provided by the ngtcp2 library, and because that adapter does not include any compile guards to ensure that OpenSSL 3.5 is being used and that the APIs are actually available, we need to add a compile time check for the openssl version in order to conditionally include the adapter to avoid build errors when using a shared openssl library that is not OpenSSL 3.5. Signed-off-by: James M Snell --- configure.py | 61 ++++++++++++++++++++++++++++++++++++++++++ deps/ngtcp2/ngtcp2.gyp | 31 ++++++++++++++++++++- src/quic/guard.h | 10 +------ 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/configure.py b/configure.py index 79d2e6709c98f1..c60dc30188af96 100755 --- a/configure.py +++ b/configure.py @@ -1205,6 +1205,65 @@ def get_gas_version(cc): warn(f'Could not recognize `gas`: {gas_ret}') return '0.0' +def get_openssl_version(): + """Parse OpenSSL version from opensslv.h header file. + + Returns the version as a number matching OPENSSL_VERSION_NUMBER format: + 0xMNN00PPSL where M=major, NN=minor, PP=patch, S=status(0xf=release,0x0=pre), L=0 + """ + + try: + # Use the C compiler to extract preprocessor macros from opensslv.h + args = ['-E', '-dM', '-include', 'openssl/opensslv.h', '-'] + if not options.shared_openssl: + args = ['-I', 'deps/openssl/openssl/include'] + args + elif options.shared_openssl_includes: + args = ['-I', options.shared_openssl_includes] + args + + proc = subprocess.Popen( + shlex.split(CC) + args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + with proc: + proc.stdin.write(b'\n') + out = to_utf8(proc.communicate()[0]) + + if proc.returncode != 0: + warn('Failed to extract OpenSSL version from opensslv.h header') + return 0 + + # Parse the macro definitions + macros = {} + for line in out.split('\n'): + if line.startswith('#define OPENSSL_VERSION_'): + parts = line.split() + if len(parts) >= 3: + macro_name = parts[1] + macro_value = parts[2] + macros[macro_name] = macro_value + + # Extract version components + major = int(macros.get('OPENSSL_VERSION_MAJOR', '0')) + minor = int(macros.get('OPENSSL_VERSION_MINOR', '0')) + patch = int(macros.get('OPENSSL_VERSION_PATCH', '0')) + + # Check if it's a pre-release (has non-empty PRE_RELEASE string) + pre_release = macros.get('OPENSSL_VERSION_PRE_RELEASE', '""').strip('"') + status = 0x0 if pre_release else 0xf + # Construct version number: 0xMNN00PPSL + version_number = ((major << 28) | + (minor << 20) | + (patch << 4) | + status) + + return version_number + + except (OSError, ValueError, subprocess.SubprocessError) as e: + warn(f'Failed to determine OpenSSL version from header: {e}') + return 0 + # Note: Apple clang self-reports as clang 4.2.0 and gcc 4.2.1. It passes # the version check more by accident than anything else but a more rigorous # check involves checking the build number against an allowlist. I'm not @@ -1828,6 +1887,8 @@ def without_ssl_error(option): if options.quic: o['defines'] += ['NODE_OPENSSL_HAS_QUIC'] + o['variables']['openssl_version'] = get_openssl_version() + configure_library('openssl', o) def configure_sqlite(o): diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp index e166d94534d002..2901eff484ac55 100644 --- a/deps/ngtcp2/ngtcp2.gyp +++ b/deps/ngtcp2/ngtcp2.gyp @@ -129,6 +129,36 @@ 'HAVE_NETINET_IN_H', ], }], + # TODO: Support OpenSSL 3.5 shared library builds. + # The complexity here is that we need to use the ngtcp2 ossl + # adapter, which does not include any conditional checks to + # see if the version of OpenSSL used has the necessary QUIC + # APIs, so we need to ensure that we conditionally enable use + # of the adapter only when we know that the OpenSSL version we + # are compiling against has the necessary APIs. We can do that + # by checkig the OpenSSL version number but, currently, the + # code that does so checks only the VERSION.dat file that is + # bundled with the openssl dependency. We'll need to update + # that to support the shared library case, where the version + # of the shared library needs to be determined. + # + # TODO: Support Boringssl here also. ngtcp2 provides an adapter + # for Boringssl. If we can detect that boringssl is being used + # here then we can use that adapter and also set the + # QUIC_NGTCP2_USE_BORINGSSL define (the guard in quic/guard.h + # would need to be updated to check for this define). + ['node_shared_openssl=="false" and openssl_version >= 0x3050001f', { + 'sources': [ + '<@(ngtcp2_sources_ossl)', + ], + 'direct_dependent_settings': { + 'defines': [ + # Tells us that we are using the OpenSSL 3.5 adapter + # that is provided by ngtcp2. + 'QUIC_NGTCP2_USE_OPENSSL_3_5', + ], + }, + }] ], 'direct_dependent_settings': { 'defines': [ @@ -143,7 +173,6 @@ }, 'sources': [ '<@(ngtcp2_sources)', - '<@(ngtcp2_sources_ossl)', ] }, { diff --git a/src/quic/guard.h b/src/quic/guard.h index 2e7bd441057bf9..c0635034522b16 100644 --- a/src/quic/guard.h +++ b/src/quic/guard.h @@ -1,13 +1,5 @@ #pragma once -#if HAVE_OPENSSL -#include -// QUIC is only available in Openssl 3.5.x and later. It was not introduced in -// Node.js until 3.5.1... prior to that we will not compile any of the QUIC -// related code. -#if OPENSSL_VERSION_NUMBER < 0x30500010 || OPENSSL_IS_BORINGSSL -#define OPENSSL_NO_QUIC = 1 -#endif -#else +#ifndef QUIC_NGTCP2_USE_OPENSSL_3_5 #define OPENSSL_NO_QUIC = 1 #endif