From 9d95723ed4c2dff3b2237162a94755024c72541e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Mar 2023 15:05:44 -0700 Subject: [PATCH 01/11] quic: add the CID implementation --- node.gyp | 6 ++ src/quic/cid.cc | 139 +++++++++++++++++++++++++++++++++++ src/quic/cid.h | 125 +++++++++++++++++++++++++++++++ test/cctest/test_quic_cid.cc | 93 +++++++++++++++++++++++ 4 files changed, 363 insertions(+) create mode 100644 src/quic/cid.cc create mode 100644 src/quic/cid.h create mode 100644 test/cctest/test_quic_cid.cc diff --git a/node.gyp b/node.gyp index cad1061d878b59..1632f5f27f3c42 100644 --- a/node.gyp +++ b/node.gyp @@ -335,6 +335,10 @@ 'src/node_crypto.cc', 'src/node_crypto.h', ], + 'node_quic_sources': [ + 'src/quic/cid.cc', + 'src/quic/cid.h', + ], 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', 'conditions': [ ['GENERATOR == "ninja"', { @@ -836,6 +840,7 @@ [ 'node_use_openssl=="true"', { 'sources': [ '<@(node_crypto_sources)', + '<@(node_quic_sources)', ], }], [ 'OS in "linux freebsd mac solaris" and ' @@ -1013,6 +1018,7 @@ 'test/cctest/test_traced_value.cc', 'test/cctest/test_util.cc', 'test/cctest/test_dataqueue.cc', + 'test/cctest/test_quic_cid.cc', ], 'conditions': [ diff --git a/src/quic/cid.cc b/src/quic/cid.cc new file mode 100644 index 00000000000000..18232cd9177384 --- /dev/null +++ b/src/quic/cid.cc @@ -0,0 +1,139 @@ +#include "cid.h" +#include +#include +#include +#include + +namespace node { +namespace quic { + +// ============================================================================ +// CID + +CID::CID() : ptr_(&cid_) { cid_.datalen = 0; } + +CID::CID(const ngtcp2_cid& cid) : CID() { + DCHECK_GE(cid.datalen, kMinLength); + DCHECK_LE(cid.datalen, kMaxLength); + ngtcp2_cid_init(&cid_, cid.data, cid.datalen); +} + +CID::CID(const uint8_t* data, size_t len) : CID() { + DCHECK_GE(len, kMinLength); + DCHECK_LE(len, kMaxLength); + ngtcp2_cid_init(&cid_, data, len); +} + +CID::CID(const ngtcp2_cid* cid) : ptr_(cid) { + CHECK_NOT_NULL(cid); + DCHECK_GE(cid->datalen, kMinLength); + DCHECK_LE(cid->datalen, kMaxLength); +} + +CID::CID(const CID& other) : ptr_(&cid_) { + CHECK_NOT_NULL(other.ptr_); + ngtcp2_cid_init(&cid_, other.ptr_->data, other.ptr_->datalen); +} + +bool CID::operator==(const CID& other) const noexcept { + if (this == &other || (length() == 0 && other.length() == 0)) + return true; + if (length() != other.length()) return false; + return memcmp(ptr_->data, other.ptr_->data, ptr_->datalen) == 0; +} + +bool CID::operator!=(const CID& other) const noexcept { + return !(*this == other); +} + +CID::operator const uint8_t*() const { return ptr_->data; } +CID::operator const ngtcp2_cid&() const { return *ptr_; } +CID::operator const ngtcp2_cid*() const { return ptr_; } +CID::operator bool() const { return ptr_->datalen >= kMinLength; } + +size_t CID::length() const { return ptr_->datalen; } + +std::string CID::ToString() const { + char dest[kMaxLength * 2]; + size_t written = + StringBytes::hex_encode(reinterpret_cast(ptr_->data), + ptr_->datalen, + dest, + arraysize(dest)); + return std::string(dest, written); +} + +CID CID::kInvalid {}; + +// ============================================================================ +// CID::Hash + +size_t CID::Hash::operator()(const CID& cid) const { + size_t hash = 0; + for (size_t n = 0; n < cid.length(); n++) { + hash ^= std::hash{}(cid.ptr_->data[n] + 0x9e3779b9 + + (hash << 6) + (hash >> 2)); + } + return hash; +} + +// ============================================================================ +// CID::Factory + +namespace { +class RandomCIDFactory : public CID::Factory { + public: + RandomCIDFactory() = default; + RandomCIDFactory(const RandomCIDFactory&) = delete; + RandomCIDFactory(RandomCIDFactory&&) = delete; + RandomCIDFactory& operator=(const RandomCIDFactory&) = delete; + RandomCIDFactory& operator=(RandomCIDFactory&&) = delete; + + CID Generate(size_t length_hint) const override { + DCHECK_GE(length_hint, CID::kMinLength); + DCHECK_LE(length_hint, CID::kMaxLength); + Mutex::ScopedLock lock(mutex_); + maybe_refresh_pool(length_hint); + auto start = pool_ + pos_; + pos_ += length_hint; + return CID(start, length_hint); + } + + void 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_); + maybe_refresh_pool(length_hint); + auto start = pool_ + pos_; + pos_ += length_hint; + ngtcp2_cid_init(cid, start, length_hint); + } + + private: + void maybe_refresh_pool(size_t length_hint) const { + // We generate a pool of random data kPoolSize in length + // and pull our random CID from that. If we don't have + // enough random random remaining in the pool to generate + // a CID of the requested size, we regenerate the pool + // and reset it to zero. + if (pos_ + length_hint > kPoolSize) { + CHECK(crypto::CSPRNG(pool_, kPoolSize).is_ok()); + pos_ = 0; + } + } + + static constexpr int kPoolSize = 4096; + mutable int pos_ = kPoolSize; + mutable uint8_t pool_[kPoolSize]; + mutable Mutex mutex_; +}; +} // namespace + +const CID::Factory& CID::Factory::random() { + static RandomCIDFactory instance; + return instance; +} + +} // namespace quic +} // namespace node diff --git a/src/quic/cid.h b/src/quic/cid.h new file mode 100644 index 00000000000000..cc74666fc5dc6b --- /dev/null +++ b/src/quic/cid.h @@ -0,0 +1,125 @@ +#pragma once + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include + +namespace node { +namespace quic { + +// CIDS are used to identify endpoints participating in a QUIC session. +// Once created, CID instances are immutable. +// +// CIDs contain between 1 to 20 bytes. Most typically they are selected +// randomly but there is a spec for creating "routable" CIDs that encode +// a specific structure that is meaningful only to the side that creates +// the CID. For most purposes, CIDs should be treated as opaque tokens. +// +// Each peer in a QUIC session generates one or more CIDs that the *other* +// peer will use to identify the session. When a QUIC client initiates a +// brand new session, it will initially generates a CID of its own (its +// source CID) and a random placeholder CID for the server (the original +// destination CID). When the server receives the initial packet, it will +// generate its own source CID and use the clients source CID as the +// server's destination CID. +// +// Client Server +// ------------------------------------------- +// Source CID <====> Destination CID +// Destination CID <====> Source CID +// +// While the connection is being established, it is possible for either +// peer to generate additional CIDs that are also associated with the +// connection. +class CID final: public MemoryRetainer { + public: + static constexpr size_t kMinLength = NGTCP2_MIN_CIDLEN; + static constexpr size_t kMaxLength = NGTCP2_MAX_CIDLEN; + + // Copy the given ngtcp2_cid. + explicit CID(const ngtcp2_cid& cid); + + // Copy the given buffer as a CID. The len must be within + // kMinLength and kMaxLength. + explicit CID(const uint8_t* data, size_t len); + + // Wrap the given ngtcp2_cid. The CID does not take ownership + // of the underlying ngtcp2_cid. + explicit CID(const ngtcp2_cid* cid); + + CID(const CID& other); + CID(CID&& other) = delete; + + struct Hash final { + size_t operator()(const CID& cid) const; + }; + + bool operator==(const CID& other) const noexcept; + bool operator!=(const CID& other) const noexcept; + + operator const uint8_t*() const; + operator const ngtcp2_cid&() const; + operator const ngtcp2_cid*() const; + + // True if the CID length is at least kMinLength; + operator bool() const; + size_t length() const; + + std::string ToString() const; + + SET_NO_MEMORY_INFO(); + SET_MEMORY_INFO_NAME(CID); + SET_SELF_SIZE(CID); + + template + 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 + // implementations MAY use the Connection ID associated with a QUIC session + // as a routing mechanism, with each CID instance securely encoding the + // routing information. By default, our implementation creates CIDs randomly + // but will allow user code to provide their own CID::Factory implementation. + class Factory; + + static CID kInvalid; + + private: + // 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. + CID(); + + ngtcp2_cid cid_; + const ngtcp2_cid* ptr_; + + friend struct Hash; +}; + +class CID::Factory { + public: + virtual ~Factory() = default; + + // 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; + + // Generate a new CID into the given ngtcp2_cid. This variation of + // Generate should be used far less commonly. It is provided largely + // for a couple of internal cases. + virtual void GenerateInto(ngtcp2_cid* cid, + size_t length_hint = CID::kMaxLength) const = 0; + + // The default random CID generator instance. + static const Factory& random(); + + // TODO(@jasnell): This will soon also include additional implementations + // of CID::Factory that implement the QUIC Load Balancers spec. +}; + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc new file mode 100644 index 00000000000000..f916ebb186153e --- /dev/null +++ b/test/cctest/test_quic_cid.cc @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include + +using node::quic::CID; + +TEST(CID, Basic) { + auto& random = CID::Factory::random(); + { + auto cid = random.Generate(); + CHECK_EQ(cid.length(), CID::kMaxLength); + CHECK(cid); + CHECK_EQ(cid, cid); + } + { + auto cid = random.Generate(5); + CHECK_EQ(cid.length(), 5); + CHECK(cid); + } + { + auto cid1 = random.Generate(); + auto cid2 = random.Generate(); + CHECK_NE(cid1, cid2); + } + { + auto cid1 = random.Generate(5); + auto cid2 = random.Generate(); + CHECK_NE(cid1, cid2); + } + { + auto cid1 = random.Generate(); + auto cid2 = random.Generate(5); + CHECK_NE(cid1, cid2); + } + { + auto cid = CID::kInvalid; + // They are copy constructible... + auto cid2 = cid; + CHECK(!cid); + CHECK_EQ(cid.length(), 0); + CHECK_EQ(cid, cid2); + } + { + auto cid1 = random.Generate(); + auto cid2 = random.Generate(); + CID::Map map; + map[cid1] = "hello"; + map[cid2] = "there"; + CHECK_EQ(map[cid1], "hello"); + CHECK_EQ(map[cid2], "there"); + CHECK_NE(map[cid2], "hello"); + CHECK_NE(map[cid1], "there"); + } + { + ngtcp2_cid cid_; + uint8_t data[] = {1, 2, 3, 4, 5}; + ngtcp2_cid_init(&cid_, data, 5); + auto 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; + CHECK_EQ(cid.length(), 5); + CHECK_EQ(cid.ToString(), "0102030405"); + } + { + ngtcp2_cid cid_; + uint8_t data[] = {1, 2, 3, 4, 5}; + ngtcp2_cid_init(&cid_, data, 5); + auto cid = CID(&cid_); + // This variation of the constructor wraps the cid_, so if we + // modify the original data it does change in the CID. + 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(); + } + } + { + ngtcp2_cid cid_; + // Generate a bunch to ensure that the pool is regenerated. + for (int n = 0; n < 1000; n++) { + random.GenerateInto(&cid_, 10); + CHECK_EQ(cid_.datalen, 10); + } + } +} From 9c6c3b0012662ace0728d6a56c87550a6636818b Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Mar 2023 16:21:07 -0700 Subject: [PATCH 02/11] quic: add the PreferredAddress implementation --- node.gyp | 4 +- src/quic/preferredaddress.cc | 153 +++++++++++++++++++++++++++++++++++ src/quic/preferredaddress.h | 70 ++++++++++++++++ 3 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/quic/preferredaddress.cc create mode 100644 src/quic/preferredaddress.h diff --git a/node.gyp b/node.gyp index 1632f5f27f3c42..7b8cc9dd16c3ab 100644 --- a/node.gyp +++ b/node.gyp @@ -337,7 +337,9 @@ ], 'node_quic_sources': [ 'src/quic/cid.cc', + 'src/quic/preferredaddress.cc', 'src/quic/cid.h', + 'src/quic/preferredaddress.h', ], 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', 'conditions': [ @@ -1018,7 +1020,6 @@ 'test/cctest/test_traced_value.cc', 'test/cctest/test_util.cc', 'test/cctest/test_dataqueue.cc', - 'test/cctest/test_quic_cid.cc', ], 'conditions': [ @@ -1029,6 +1030,7 @@ 'sources': [ 'test/cctest/test_crypto_clienthello.cc', 'test/cctest/test_node_crypto.cc', + 'test/cctest/test_quic_cid.cc', ] }], ['v8_enable_inspector==1', { diff --git a/src/quic/preferredaddress.cc b/src/quic/preferredaddress.cc new file mode 100644 index 00000000000000..64e39f0b086984 --- /dev/null +++ b/src/quic/preferredaddress.cc @@ -0,0 +1,153 @@ +#include "preferredaddress.h" +#include +#include +#include +#include +#include +#include +#include + +namespace node { + +using v8::Just; +using v8::Local; +using v8::Maybe; +using v8::Nothing; +using v8::Value; + +namespace quic { + +namespace { +template +std::optional get_address_info( + const ngtcp2_preferred_addr& paddr) { + if constexpr (FAMILY == AF_INET) { + if (!paddr.ipv4_present) return std::nullopt; + PreferredAddress::AddressInfo address; + address.family = FAMILY; + address.port = paddr.ipv4_port; + if (uv_inet_ntop(FAMILY, paddr.ipv4_addr, + address.host, sizeof(address.host)) == 0) { + address.address = address.host; + } + return address; + } else { + if (!paddr.ipv6_present) return std::nullopt; + PreferredAddress::AddressInfo address; + address.family = FAMILY; + address.port = paddr.ipv6_port; + if (uv_inet_ntop(FAMILY, paddr.ipv6_addr, + address.host, sizeof(address.host)) == 0) { + address.address = address.host; + } + return address; + } +} + +template +void copy_to_transport_params( + ngtcp2_transport_params* params, + const sockaddr* addr) { + params->preferred_address_present = true; + if constexpr (FAMILY == AF_INET) { + const sockaddr_in* src = reinterpret_cast(addr); + params->preferred_address.ipv4_port = SocketAddress::GetPort(addr); + memcpy(params->preferred_address.ipv4_addr, + &src->sin_addr, + sizeof(params->preferred_address.ipv4_addr)); + } else { + DCHECK_EQ(FAMILY, AF_INET6); + const sockaddr_in6* src = reinterpret_cast(addr); + params->preferred_address.ipv6_port = SocketAddress::GetPort(addr); + memcpy(params->preferred_address.ipv6_addr, + &src->sin6_addr, + sizeof(params->preferred_address.ipv4_addr)); + } + UNREACHABLE(); +} + +bool resolve(const PreferredAddress::AddressInfo& address, + uv_getaddrinfo_t* req) { + addrinfo hints{}; + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + hints.ai_family = address.family; + hints.ai_socktype = SOCK_DGRAM; + + // ngtcp2 requires the selection of the preferred address + // to be synchronous, which means we have to do a sync resolve + // using uv_getaddrinfo here. + return uv_getaddrinfo(nullptr, + req, + nullptr, + address.host, + // TODO(@jasnell): The to_string here is not really + // the most performant way of converting the uint16_t + // port into a string. Depending on execution count, + // the potential cost here could be mitigated with a + // more efficient conversion. For now, however, this + // works. + std::to_string(address.port).c_str(), + &hints) == 0 && req->addrinfo != nullptr; +} +} // namespace + +Maybe PreferredAddress::GetPolicy( + Environment* env, + Local value) { + CHECK(value->IsUint32()); + uint32_t val = 0; + if (value->Uint32Value(env->context()).To(&val)) { + switch (val) { + case QUIC_PREFERRED_ADDRESS_USE: return Just(Policy::USE); + case QUIC_PREFERRED_ADDRESS_IGNORE: return Just(Policy::IGNORE); + } + } + THROW_ERR_INVALID_ARG_VALUE(env, + "%d is not a valid preferred address policy", val); + return Nothing(); +} + +PreferredAddress::PreferredAddress(ngtcp2_path* dest, + const ngtcp2_preferred_addr* paddr) + : dest_(dest), paddr_(paddr) { + DCHECK_NOT_NULL(paddr); + DCHECK_NOT_NULL(dest); +} + +std::optional +PreferredAddress::ipv4() const { + return get_address_info(*paddr_); +} + +std::optional +PreferredAddress::ipv6() const { + return get_address_info(*paddr_); +} + +void PreferredAddress::Use(const AddressInfo& address) { + uv_getaddrinfo_t req; + auto on_exit = OnScopeLeave([&] { + if (req.addrinfo != nullptr) uv_freeaddrinfo(req.addrinfo); + }); + + if (resolve(address, &req)) { + DCHECK_NOT_NULL(req.addrinfo); + dest_->remote.addrlen = req.addrinfo->ai_addrlen; + memcpy(dest_->remote.addr, req.addrinfo->ai_addr, req.addrinfo->ai_addrlen); + } +} + +void PreferredAddress::Set( + ngtcp2_transport_params* params, + const sockaddr* addr) { + DCHECK_NOT_NULL(params); + DCHECK_NOT_NULL(addr); + switch (addr->sa_family) { + case AF_INET: return copy_to_transport_params(params, addr); + case AF_INET6: return copy_to_transport_params(params, addr); + } + // Any other value is just ignored. +} + +} // namespace quic +} // namespace node diff --git a/src/quic/preferredaddress.h b/src/quic/preferredaddress.h new file mode 100644 index 00000000000000..cc35eef6ac52e0 --- /dev/null +++ b/src/quic/preferredaddress.h @@ -0,0 +1,70 @@ +#pragma once + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include +#include +#include +#include + +namespace node { +namespace quic { + +// PreferredAddress is a helper class used only when a client Session receives +// an advertised preferred address from a server. The helper provides +// information about the server advertised preferred address and allows +// the preferred address to be selected. +class PreferredAddress final { + public: + enum class Policy { + // Ignore the server-advertised preferred address. + IGNORE, + // Use the server-advertised preferred address. + USE, + }; + + // The QUIC_* constants are expected to be exported out to be used on + // the JavaScript side of the API. + static constexpr uint32_t QUIC_PREFERRED_ADDRESS_USE = + static_cast(Policy::USE); + static constexpr uint32_t QUIC_PREFERRED_ADDRESS_IGNORE = + static_cast(Policy::IGNORE); + + static v8::Maybe GetPolicy(Environment* env, + v8::Local value); + + struct AddressInfo final { + char host[NI_MAXHOST]; + int family; + uint16_t port; + std::string_view address; + }; + + explicit PreferredAddress(ngtcp2_path* dest, + const ngtcp2_preferred_addr* paddr); + PreferredAddress(const PreferredAddress&) = delete; + PreferredAddress(PreferredAddress&&) = delete; + PreferredAddress& operator=(const PreferredAddress&) = delete; + PreferredAddress& operator=(PreferredAddress&&) = delete; + + void Use(const AddressInfo& address); + + std::optional ipv4() const; + std::optional ipv6() const; + + // Set the preferred address in the transport params. + // The address family (ipv4 or ipv6) will be automatically + // detected from the given addr. Any other address family + // will be ignored. + static void Set(ngtcp2_transport_params* params, const sockaddr* addr); + + private: + ngtcp2_path* dest_; + const ngtcp2_preferred_addr* paddr_; +}; + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From d092604ed84d6b2a3a02b88253d1ccf4464f974e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Mar 2023 16:31:10 -0700 Subject: [PATCH 03/11] quic: add Path and PathStorage implementations --- node.gyp | 2 ++ src/quic/data.cc | 17 +++++++++++++++++ src/quic/data.h | 23 +++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/quic/data.cc create mode 100644 src/quic/data.h diff --git a/node.gyp b/node.gyp index 7b8cc9dd16c3ab..45304452e74f33 100644 --- a/node.gyp +++ b/node.gyp @@ -337,8 +337,10 @@ ], 'node_quic_sources': [ 'src/quic/cid.cc', + 'src/quic/data.cc', 'src/quic/preferredaddress.cc', 'src/quic/cid.h', + 'src/quic/data.h', 'src/quic/preferredaddress.h', ], 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', diff --git a/src/quic/data.cc b/src/quic/data.cc new file mode 100644 index 00000000000000..b5d6ee2c0b4db0 --- /dev/null +++ b/src/quic/data.cc @@ -0,0 +1,17 @@ +#include "data.h" +#include +#include "ngtcp2/ngtcp2.h" + +namespace node { +namespace quic { + +Path::Path(const SocketAddress& local, const SocketAddress& remote) { + ngtcp2_addr_init(&this->local, local.data(), local.length()); + ngtcp2_addr_init(&this->remote, remote.data(), remote.length()); +} + +PathStorage::PathStorage() { ngtcp2_path_storage_zero(this); } +PathStorage::operator ngtcp2_path() { return path; } + +} // namespace quic +} // namespace node diff --git a/src/quic/data.h b/src/quic/data.h new file mode 100644 index 00000000000000..78aae5253725bd --- /dev/null +++ b/src/quic/data.h @@ -0,0 +1,23 @@ +#pragma once + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include +#include + +namespace node { +namespace quic { + +struct Path final : public ngtcp2_path { + Path(const SocketAddress& local, const SocketAddress& remote); +}; + +struct PathStorage final: public ngtcp2_path_storage { + PathStorage(); + operator ngtcp2_path(); +}; + +} // namespace quic +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS From e762f996e058d2a62a1dcfe86a80dd25812e7025 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Mar 2023 16:53:26 -0700 Subject: [PATCH 04/11] quic: add Store implementation Store is a utility class that abstracts working with BackingStore that will be used in the quic impl --- src/quic/data.cc | 76 +++++++++++++++++++++++++++++++++++++++++++++++- src/quic/data.h | 40 +++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/quic/data.cc b/src/quic/data.cc index b5d6ee2c0b4db0..a334cb7eee1583 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -1,8 +1,15 @@ #include "data.h" +#include +#include #include -#include "ngtcp2/ngtcp2.h" +#include +#include namespace node { + +using v8::Local; +using v8::Value; + namespace quic { Path::Path(const SocketAddress& local, const SocketAddress& remote) { @@ -13,5 +20,72 @@ Path::Path(const SocketAddress& local, const SocketAddress& remote) { PathStorage::PathStorage() { ngtcp2_path_storage_zero(this); } PathStorage::operator ngtcp2_path() { return path; } +// ============================================================================ + +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_(std::move(store)), + length_(length), + offset_(offset) { + CHECK_LE(offset_, store->ByteLength()); + CHECK_LE(length_, store->ByteLength() - offset_); +} + +Store::Store(v8::Local buffer, Option option) + : Store(buffer->GetBackingStore(), buffer->ByteLength()) { + if (option == Option::DETACH) { + USE(buffer->Detach(Local())); + } +} + +Store::Store(v8::Local view, Option option) + : Store(view->Buffer()->GetBackingStore(), + view->ByteLength(), + view->ByteOffset()) { + if (option == Option::DETACH) { + USE(view->Buffer()->Detach(Local())); + } +} + +Store::operator bool() const { return store_ != nullptr; } +size_t Store::length() const { return length_; } + +template +T Store::convert() const { + T buf; + buf.base = store_ != nullptr ? + static_cast(store_->Data()) + offset_ : + nullptr; + buf.len = length_; + return buf; +} + +Store::operator uv_buf_t() const { + return convert(); +} + +Store::operator ngtcp2_vec() const { + return convert(); +} + +Store::operator nghttp3_vec() const { + return convert(); +} + +void Store::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("store", store_); +} + } // namespace quic } // namespace node diff --git a/src/quic/data.h b/src/quic/data.h index 78aae5253725bd..b4704d98866c4b 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -2,8 +2,12 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include +#include #include #include +#include +#include namespace node { namespace quic { @@ -17,6 +21,42 @@ struct PathStorage final: public ngtcp2_path_storage { operator ngtcp2_path(); }; +class Store final : public MemoryRetainer { + public: + Store() = default; + + Store(std::shared_ptr store, + size_t length, + size_t offset = 0); + 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); + + operator uv_buf_t() const; + operator ngtcp2_vec() const; + operator nghttp3_vec() const; + operator bool() const; + size_t length() const; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(Store); + SET_SELF_SIZE(Store); + + private: + template T convert() const; + std::shared_ptr store_; + size_t length_ = 0; + size_t offset_ = 0; +}; + } // namespace quic } // namespace node From a2b787f1ddf77959d744c6581d407db3c22f17e1 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Sun, 26 Mar 2023 17:29:46 -0700 Subject: [PATCH 05/11] quic: add QuicError implementation --- src/quic/data.cc | 181 +++++++++++++++++++++++++++++++++++++++++++++++ src/quic/data.h | 71 +++++++++++++++++++ 2 files changed, 252 insertions(+) diff --git a/src/quic/data.cc b/src/quic/data.cc index a334cb7eee1583..9c87594bcb346b 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -4,10 +4,16 @@ #include #include #include +#include "util.h" namespace node { +using v8::Array; +using v8::BigInt; +using v8::Integer; +using v8::MaybeLocal; using v8::Local; +using v8::Undefined; using v8::Value; namespace quic { @@ -87,5 +93,180 @@ void Store::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("store", store_); } +// ============================================================================ + +namespace { +std::string TypeName(QuicError::Type type) { + switch (type) { + case QuicError::Type::APPLICATION: return "APPLICATION"; + case QuicError::Type::TRANSPORT: return "TRANSPORT"; + case QuicError::Type::VERSION_NEGOTIATION: return "VERSION_NEGOTIATION"; + case QuicError::Type::IDLE_CLOSE: return "IDLE_CLOSE"; + } + UNREACHABLE(); +} +} // namespace + +QuicError::QuicError(const std::string_view reason) + : reason_(reason), + ptr_(&error_) {} + +QuicError::QuicError(const ngtcp2_connection_close_error* ptr) + : reason_(reinterpret_cast(ptr->reason), ptr->reasonlen), + ptr_(ptr) {} + +QuicError::QuicError(const ngtcp2_connection_close_error& error) + : reason_(reinterpret_cast(error.reason), error.reasonlen), + error_(error), + ptr_(&error_) {} + +QuicError::operator bool() const { + if ((code() == NO_ERROR && type() == Type::TRANSPORT) || + ((code() == APP_NO_ERROR && type() == Type::APPLICATION))) { + return false; + } + return true; +} + +const uint8_t* QuicError::reason_c_str() const { + return reinterpret_cast(reason_.c_str()); +} + +bool QuicError::operator!=(const QuicError& other) const { + return !(*this == other); +} + +bool QuicError::operator==(const QuicError& other) const { + if (this == &other) return true; + return type() == other.type() && + code() == other.code() && + frame_type() == other.frame_type(); +} + +QuicError::Type QuicError::type() const { + return static_cast(ptr_->type); +} + +QuicError::error_code QuicError::code() const { + return ptr_->error_code; +} + +uint64_t QuicError::frame_type() const { + return ptr_->frame_type; +} + +const std::string_view QuicError::reason() const { + return reason_; +} + +QuicError::operator const ngtcp2_connection_close_error&() const { + return *ptr_; +} + +QuicError::operator const ngtcp2_connection_close_error*() const { + return ptr_; +} + +MaybeLocal QuicError::ToV8Value(Environment* env) const { + Local argv[] = { + Integer::New(env->isolate(), static_cast(type())), + BigInt::NewFromUnsigned(env->isolate(), code()), + Undefined(env->isolate()), + }; + + if (reason_.length() > 0 && + !node::ToV8Value(env->context(), reason()).ToLocal(&argv[2])) { + return MaybeLocal(); + } + return Array::New(env->isolate(), argv, arraysize(argv)).As(); +} + +std::string QuicError::ToString() const { + std::string str = "QuicError("; + str += TypeName(type()) + ") "; + str += std::to_string(code()); + if (!reason_.empty()) str += ": " + reason_; + return str; +} + +void QuicError::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("reason", reason_.length()); +} + +QuicError QuicError::ForTransport( + error_code code, + const std::string_view reason) { + QuicError error(reason); + ngtcp2_connection_close_error_set_transport_error( + &error.error_, + code, + error.reason_c_str(), + reason.length()); + return error; +} + +QuicError QuicError::ForApplication( + error_code code, + const std::string_view reason) { + QuicError error(reason); + ngtcp2_connection_close_error_set_application_error( + &error.error_, + code, + error.reason_c_str(), + reason.length()); + return error; +} + +QuicError QuicError::ForVersionNegotiation( + const std::string_view reason) { + return ForNgtcp2Error(NGTCP2_ERR_RECV_VERSION_NEGOTIATION, reason); +} + +QuicError QuicError::ForIdleClose( + const std::string_view reason) { + return ForNgtcp2Error(NGTCP2_ERR_IDLE_CLOSE, reason); +} + +QuicError QuicError::ForNgtcp2Error( + int code, + const std::string_view reason) { + QuicError error(reason); + ngtcp2_connection_close_error_set_transport_error_liberr( + &error.error_, + code, + error.reason_c_str(), + reason.length()); + return error; +} + +QuicError QuicError::ForTlsAlert( + int code, + const std::string_view reason) { + QuicError error(reason); + ngtcp2_connection_close_error_set_transport_error_tls_alert( + &error.error_, + code, + error.reason_c_str(), + reason.length()); + return error; +} + +QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { + QuicError error; + ngtcp2_conn_get_connection_close_error(session, &error.error_); + return error; +} + +QuicError QuicError::TRANSPORT_NO_ERROR = + QuicError::ForTransport(QuicError::NO_ERROR); +QuicError QuicError::APPLICATION_NO_ERROR = + QuicError::ForApplication(QuicError::APP_NO_ERROR); +QuicError QuicError::VERSION_NEGOTIATION = + QuicError::ForVersionNegotiation(); +QuicError QuicError::IDLE_CLOSE = + QuicError::ForIdleClose(); +QuicError QuicError::INTERNAL_ERROR = + QuicError::ForNgtcp2Error(NGTCP2_ERR_INTERNAL); + } // namespace quic } // namespace node diff --git a/src/quic/data.h b/src/quic/data.h index b4704d98866c4b..66ba3c9721f34d 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -57,6 +57,77 @@ class Store final : public MemoryRetainer { size_t offset_ = 0; }; +class QuicError final : public MemoryRetainer { + public: + using error_code = uint64_t; + + static constexpr error_code NO_ERROR = NGTCP2_NO_ERROR; + static constexpr error_code APP_NO_ERROR = 65280; + + enum class Type { + TRANSPORT = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + APPLICATION = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, + VERSION_NEGOTIATION = + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION, + IDLE_CLOSE = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE, + }; + + static constexpr error_code QUIC_ERROR_TYPE_TRANSPORT = + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; + static constexpr error_code QUIC_ERROR_TYPE_APPLICATION = + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + + explicit QuicError(const std::string_view reason = ""); + explicit QuicError(const ngtcp2_connection_close_error* ptr); + explicit QuicError(const ngtcp2_connection_close_error& error); + + Type type() const; + error_code code() const; + const std::string_view reason() const; + uint64_t frame_type() const; + + operator const ngtcp2_connection_close_error&() const; + operator const ngtcp2_connection_close_error*() const; + + // Returns false if the QuicError uses a no_error code with type + // transport or application. + operator bool() const; + + bool operator==(const QuicError& other) const; + bool operator!=(const QuicError& other) const; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(QuicError); + SET_SELF_SIZE(QuicError); + + std::string ToString() const; + v8::MaybeLocal ToV8Value(Environment* env) const; + + static QuicError ForTransport(error_code code, + const std::string_view reason = ""); + static QuicError ForApplication(error_code code, + const std::string_view reason = ""); + static QuicError ForVersionNegotiation(const std::string_view reason = ""); + static QuicError ForIdleClose(const std::string_view reason = ""); + static QuicError ForNgtcp2Error(int code, const std::string_view reason = ""); + static QuicError ForTlsAlert(int code, const std::string_view 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; + + private: + const uint8_t* reason_c_str() const; + + std::string reason_; + ngtcp2_connection_close_error error_; + const ngtcp2_connection_close_error* ptr_ = nullptr; +}; + } // namespace quic } // namespace node From 6e1b1b8891faa325a06c3c518aabb0bf474ed9b1 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Mar 2023 12:44:38 -0700 Subject: [PATCH 06/11] quic: fixup lining issues in multiple files --- src/quic/data.cc | 2 +- src/quic/data.h | 8 ++++---- src/quic/preferredaddress.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/quic/data.cc b/src/quic/data.cc index 9c87594bcb346b..4055e88a29c135 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -11,8 +11,8 @@ namespace node { using v8::Array; using v8::BigInt; using v8::Integer; -using v8::MaybeLocal; using v8::Local; +using v8::MaybeLocal; using v8::Undefined; using v8::Value; diff --git a/src/quic/data.h b/src/quic/data.h index 66ba3c9721f34d..ffb01a8546fa6d 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -51,10 +51,10 @@ class Store final : public MemoryRetainer { SET_SELF_SIZE(Store); private: - template T convert() const; - std::shared_ptr store_; - size_t length_ = 0; - size_t offset_ = 0; + template T convert() const; + std::shared_ptr store_; + size_t length_ = 0; + size_t offset_ = 0; }; class QuicError final : public MemoryRetainer { diff --git a/src/quic/preferredaddress.h b/src/quic/preferredaddress.h index cc35eef6ac52e0..82c5370a58d5c4 100644 --- a/src/quic/preferredaddress.h +++ b/src/quic/preferredaddress.h @@ -5,8 +5,8 @@ #include #include #include -#include #include +#include namespace node { namespace quic { From abdf7556033b722e694ba5df6454eb18dded3891 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Mar 2023 12:46:11 -0700 Subject: [PATCH 07/11] quic: apply format-cpp to multiple files --- src/quic/cid.cc | 35 +++++++---- src/quic/cid.h | 2 +- src/quic/data.cc | 115 +++++++++++++++-------------------- src/quic/data.h | 9 +-- src/quic/preferredaddress.cc | 50 +++++++-------- src/quic/preferredaddress.h | 2 +- test/cctest/test_quic_cid.cc | 4 +- 7 files changed, 107 insertions(+), 110 deletions(-) diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 18232cd9177384..06c12ca4c182d9 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -1,8 +1,8 @@ #include "cid.h" +#include #include #include #include -#include namespace node { namespace quic { @@ -10,7 +10,9 @@ namespace quic { // ============================================================================ // CID -CID::CID() : ptr_(&cid_) { cid_.datalen = 0; } +CID::CID() : ptr_(&cid_) { + cid_.datalen = 0; +} CID::CID(const ngtcp2_cid& cid) : CID() { DCHECK_GE(cid.datalen, kMinLength); @@ -36,8 +38,7 @@ CID::CID(const CID& other) : ptr_(&cid_) { } bool CID::operator==(const CID& other) const noexcept { - if (this == &other || (length() == 0 && other.length() == 0)) - return true; + if (this == &other || (length() == 0 && other.length() == 0)) return true; if (length() != other.length()) return false; return memcmp(ptr_->data, other.ptr_->data, ptr_->datalen) == 0; } @@ -46,12 +47,22 @@ bool CID::operator!=(const CID& other) const noexcept { return !(*this == other); } -CID::operator const uint8_t*() const { return ptr_->data; } -CID::operator const ngtcp2_cid&() const { return *ptr_; } -CID::operator const ngtcp2_cid*() const { return ptr_; } -CID::operator bool() const { return ptr_->datalen >= kMinLength; } +CID::operator const uint8_t*() const { + return ptr_->data; +} +CID::operator const ngtcp2_cid&() const { + return *ptr_; +} +CID::operator const ngtcp2_cid*() const { + return ptr_; +} +CID::operator bool() const { + return ptr_->datalen >= kMinLength; +} -size_t CID::length() const { return ptr_->datalen; } +size_t CID::length() const { + return ptr_->datalen; +} std::string CID::ToString() const { char dest[kMaxLength * 2]; @@ -63,7 +74,7 @@ std::string CID::ToString() const { return std::string(dest, written); } -CID CID::kInvalid {}; +CID CID::kInvalid{}; // ============================================================================ // CID::Hash @@ -71,8 +82,8 @@ CID CID::kInvalid {}; size_t CID::Hash::operator()(const CID& cid) const { size_t hash = 0; for (size_t n = 0; n < cid.length(); n++) { - hash ^= std::hash{}(cid.ptr_->data[n] + 0x9e3779b9 + - (hash << 6) + (hash >> 2)); + hash ^= std::hash{}(cid.ptr_->data[n] + 0x9e3779b9 + (hash << 6) + + (hash >> 2)); } return hash; } diff --git a/src/quic/cid.h b/src/quic/cid.h index cc74666fc5dc6b..f50849650c873a 100644 --- a/src/quic/cid.h +++ b/src/quic/cid.h @@ -33,7 +33,7 @@ namespace quic { // While the connection is being established, it is possible for either // peer to generate additional CIDs that are also associated with the // connection. -class CID final: public MemoryRetainer { +class CID final : public MemoryRetainer { public: static constexpr size_t kMinLength = NGTCP2_MIN_CIDLEN; static constexpr size_t kMaxLength = NGTCP2_MAX_CIDLEN; diff --git a/src/quic/data.cc b/src/quic/data.cc index 4055e88a29c135..dc19dedd02d62a 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -1,8 +1,8 @@ #include "data.h" #include #include -#include #include +#include #include #include "util.h" @@ -23,27 +23,27 @@ Path::Path(const SocketAddress& local, const SocketAddress& remote) { ngtcp2_addr_init(&this->remote, remote.data(), remote.length()); } -PathStorage::PathStorage() { ngtcp2_path_storage_zero(this); } -PathStorage::operator ngtcp2_path() { return path; } +PathStorage::PathStorage() { + ngtcp2_path_storage_zero(this); +} +PathStorage::operator ngtcp2_path() { + return path; +} // ============================================================================ Store::Store(std::shared_ptr store, - size_t length, - size_t offset) - : store_(std::move(store)), - length_(length), - offset_(offset) { + 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_(std::move(store)), - length_(length), - offset_(offset) { + 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_); } @@ -64,15 +64,18 @@ Store::Store(v8::Local view, Option option) } } -Store::operator bool() const { return store_ != nullptr; } -size_t Store::length() const { return length_; } +Store::operator bool() const { + return store_ != nullptr; +} +size_t Store::length() const { + return length_; +} template T Store::convert() const { T buf; - buf.base = store_ != nullptr ? - static_cast(store_->Data()) + offset_ : - nullptr; + buf.base = + store_ != nullptr ? static_cast(store_->Data()) + offset_ : nullptr; buf.len = length_; return buf; } @@ -98,18 +101,21 @@ void Store::MemoryInfo(MemoryTracker* tracker) const { namespace { std::string TypeName(QuicError::Type type) { switch (type) { - case QuicError::Type::APPLICATION: return "APPLICATION"; - case QuicError::Type::TRANSPORT: return "TRANSPORT"; - case QuicError::Type::VERSION_NEGOTIATION: return "VERSION_NEGOTIATION"; - case QuicError::Type::IDLE_CLOSE: return "IDLE_CLOSE"; + case QuicError::Type::APPLICATION: + return "APPLICATION"; + case QuicError::Type::TRANSPORT: + return "TRANSPORT"; + case QuicError::Type::VERSION_NEGOTIATION: + return "VERSION_NEGOTIATION"; + case QuicError::Type::IDLE_CLOSE: + return "IDLE_CLOSE"; } UNREACHABLE(); } } // namespace QuicError::QuicError(const std::string_view reason) - : reason_(reason), - ptr_(&error_) {} + : reason_(reason), ptr_(&error_) {} QuicError::QuicError(const ngtcp2_connection_close_error* ptr) : reason_(reinterpret_cast(ptr->reason), ptr->reasonlen), @@ -122,7 +128,7 @@ QuicError::QuicError(const ngtcp2_connection_close_error& error) QuicError::operator bool() const { if ((code() == NO_ERROR && type() == Type::TRANSPORT) || - ((code() == APP_NO_ERROR && type() == Type::APPLICATION))) { + ((code() == APP_NO_ERROR && type() == Type::APPLICATION))) { return false; } return true; @@ -138,8 +144,7 @@ bool QuicError::operator!=(const QuicError& other) const { bool QuicError::operator==(const QuicError& other) const { if (this == &other) return true; - return type() == other.type() && - code() == other.code() && + return type() == other.type() && code() == other.code() && frame_type() == other.frame_type(); } @@ -169,9 +174,9 @@ QuicError::operator const ngtcp2_connection_close_error*() const { MaybeLocal QuicError::ToV8Value(Environment* env) const { Local argv[] = { - Integer::New(env->isolate(), static_cast(type())), - BigInt::NewFromUnsigned(env->isolate(), code()), - Undefined(env->isolate()), + Integer::New(env->isolate(), static_cast(type())), + BigInt::NewFromUnsigned(env->isolate(), code()), + Undefined(env->isolate()), }; if (reason_.length() > 0 && @@ -193,61 +198,41 @@ void QuicError::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("reason", reason_.length()); } -QuicError QuicError::ForTransport( - error_code code, - const std::string_view reason) { +QuicError QuicError::ForTransport(error_code code, + const std::string_view reason) { QuicError error(reason); ngtcp2_connection_close_error_set_transport_error( - &error.error_, - code, - error.reason_c_str(), - reason.length()); + &error.error_, code, error.reason_c_str(), reason.length()); return error; } -QuicError QuicError::ForApplication( - error_code code, - const std::string_view reason) { +QuicError QuicError::ForApplication(error_code code, + const std::string_view reason) { QuicError error(reason); ngtcp2_connection_close_error_set_application_error( - &error.error_, - code, - error.reason_c_str(), - reason.length()); + &error.error_, code, error.reason_c_str(), reason.length()); return error; } -QuicError QuicError::ForVersionNegotiation( - const std::string_view reason) { +QuicError QuicError::ForVersionNegotiation(const std::string_view reason) { return ForNgtcp2Error(NGTCP2_ERR_RECV_VERSION_NEGOTIATION, reason); } -QuicError QuicError::ForIdleClose( - const std::string_view reason) { +QuicError QuicError::ForIdleClose(const std::string_view reason) { return ForNgtcp2Error(NGTCP2_ERR_IDLE_CLOSE, reason); } -QuicError QuicError::ForNgtcp2Error( - int code, - const std::string_view reason) { +QuicError QuicError::ForNgtcp2Error(int code, const std::string_view reason) { QuicError error(reason); ngtcp2_connection_close_error_set_transport_error_liberr( - &error.error_, - code, - error.reason_c_str(), - reason.length()); + &error.error_, code, error.reason_c_str(), reason.length()); return error; } -QuicError QuicError::ForTlsAlert( - int code, - const std::string_view reason) { +QuicError QuicError::ForTlsAlert(int code, const std::string_view reason) { QuicError error(reason); ngtcp2_connection_close_error_set_transport_error_tls_alert( - &error.error_, - code, - error.reason_c_str(), - reason.length()); + &error.error_, code, error.reason_c_str(), reason.length()); return error; } @@ -261,10 +246,8 @@ QuicError QuicError::TRANSPORT_NO_ERROR = QuicError::ForTransport(QuicError::NO_ERROR); QuicError QuicError::APPLICATION_NO_ERROR = QuicError::ForApplication(QuicError::APP_NO_ERROR); -QuicError QuicError::VERSION_NEGOTIATION = - QuicError::ForVersionNegotiation(); -QuicError QuicError::IDLE_CLOSE = - QuicError::ForIdleClose(); +QuicError QuicError::VERSION_NEGOTIATION = QuicError::ForVersionNegotiation(); +QuicError QuicError::IDLE_CLOSE = QuicError::ForIdleClose(); QuicError QuicError::INTERNAL_ERROR = QuicError::ForNgtcp2Error(NGTCP2_ERR_INTERNAL); diff --git a/src/quic/data.h b/src/quic/data.h index ffb01a8546fa6d..019072f446bebc 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -3,10 +3,10 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include +#include +#include #include #include -#include -#include #include namespace node { @@ -16,7 +16,7 @@ struct Path final : public ngtcp2_path { Path(const SocketAddress& local, const SocketAddress& remote); }; -struct PathStorage final: public ngtcp2_path_storage { +struct PathStorage final : public ngtcp2_path_storage { PathStorage(); operator ngtcp2_path(); }; @@ -51,7 +51,8 @@ class Store final : public MemoryRetainer { SET_SELF_SIZE(Store); private: - template T convert() const; + template + T convert() const; std::shared_ptr store_; size_t length_ = 0; size_t offset_ = 0; diff --git a/src/quic/preferredaddress.cc b/src/quic/preferredaddress.cc index 64e39f0b086984..463605110e07e7 100644 --- a/src/quic/preferredaddress.cc +++ b/src/quic/preferredaddress.cc @@ -1,9 +1,9 @@ #include "preferredaddress.h" #include +#include #include #include #include -#include #include #include @@ -26,8 +26,8 @@ std::optional get_address_info( PreferredAddress::AddressInfo address; address.family = FAMILY; address.port = paddr.ipv4_port; - if (uv_inet_ntop(FAMILY, paddr.ipv4_addr, - address.host, sizeof(address.host)) == 0) { + if (uv_inet_ntop( + FAMILY, paddr.ipv4_addr, address.host, sizeof(address.host)) == 0) { address.address = address.host; } return address; @@ -36,8 +36,8 @@ std::optional get_address_info( PreferredAddress::AddressInfo address; address.family = FAMILY; address.port = paddr.ipv6_port; - if (uv_inet_ntop(FAMILY, paddr.ipv6_addr, - address.host, sizeof(address.host)) == 0) { + if (uv_inet_ntop( + FAMILY, paddr.ipv6_addr, address.host, sizeof(address.host)) == 0) { address.address = address.host; } return address; @@ -45,9 +45,8 @@ std::optional get_address_info( } template -void copy_to_transport_params( - ngtcp2_transport_params* params, - const sockaddr* addr) { +void copy_to_transport_params(ngtcp2_transport_params* params, + const sockaddr* addr) { params->preferred_address_present = true; if constexpr (FAMILY == AF_INET) { const sockaddr_in* src = reinterpret_cast(addr); @@ -87,23 +86,25 @@ bool resolve(const PreferredAddress::AddressInfo& address, // more efficient conversion. For now, however, this // works. std::to_string(address.port).c_str(), - &hints) == 0 && req->addrinfo != nullptr; + &hints) == 0 && + req->addrinfo != nullptr; } } // namespace Maybe PreferredAddress::GetPolicy( - Environment* env, - Local value) { + Environment* env, Local value) { CHECK(value->IsUint32()); uint32_t val = 0; if (value->Uint32Value(env->context()).To(&val)) { switch (val) { - case QUIC_PREFERRED_ADDRESS_USE: return Just(Policy::USE); - case QUIC_PREFERRED_ADDRESS_IGNORE: return Just(Policy::IGNORE); + case QUIC_PREFERRED_ADDRESS_USE: + return Just(Policy::USE); + case QUIC_PREFERRED_ADDRESS_IGNORE: + return Just(Policy::IGNORE); } } - THROW_ERR_INVALID_ARG_VALUE(env, - "%d is not a valid preferred address policy", val); + THROW_ERR_INVALID_ARG_VALUE( + env, "%d is not a valid preferred address policy", val); return Nothing(); } @@ -114,13 +115,13 @@ PreferredAddress::PreferredAddress(ngtcp2_path* dest, DCHECK_NOT_NULL(dest); } -std::optional -PreferredAddress::ipv4() const { +std::optional PreferredAddress::ipv4() + const { return get_address_info(*paddr_); } -std::optional -PreferredAddress::ipv6() const { +std::optional PreferredAddress::ipv6() + const { return get_address_info(*paddr_); } @@ -137,14 +138,15 @@ void PreferredAddress::Use(const AddressInfo& address) { } } -void PreferredAddress::Set( - ngtcp2_transport_params* params, - const sockaddr* addr) { +void PreferredAddress::Set(ngtcp2_transport_params* params, + const sockaddr* addr) { DCHECK_NOT_NULL(params); DCHECK_NOT_NULL(addr); switch (addr->sa_family) { - case AF_INET: return copy_to_transport_params(params, addr); - case AF_INET6: return copy_to_transport_params(params, addr); + case AF_INET: + return copy_to_transport_params(params, addr); + case AF_INET6: + return copy_to_transport_params(params, addr); } // Any other value is just ignored. } diff --git a/src/quic/preferredaddress.h b/src/quic/preferredaddress.h index 82c5370a58d5c4..1d1c90f2af1b8d 100644 --- a/src/quic/preferredaddress.h +++ b/src/quic/preferredaddress.h @@ -3,8 +3,8 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include -#include #include +#include #include #include diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc index f916ebb186153e..ae5779ccafc77b 100644 --- a/test/cctest/test_quic_cid.cc +++ b/test/cctest/test_quic_cid.cc @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #include #include From 35556f81a981290dd714293020e0007622480ca7 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Mar 2023 13:38:52 -0700 Subject: [PATCH 08/11] quic: fixup silly compile errors --- src/quic/cid.h | 6 +++--- src/quic/data.h | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/quic/cid.h b/src/quic/cid.h index f50849650c873a..c67d5405507d91 100644 --- a/src/quic/cid.h +++ b/src/quic/cid.h @@ -69,9 +69,9 @@ class CID final : public MemoryRetainer { std::string ToString() const; - SET_NO_MEMORY_INFO(); - SET_MEMORY_INFO_NAME(CID); - SET_SELF_SIZE(CID); + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(CID) + SET_SELF_SIZE(CID) template using Map = std::unordered_map; diff --git a/src/quic/data.h b/src/quic/data.h index 019072f446bebc..8e025e448741e2 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -47,8 +47,8 @@ class Store final : public MemoryRetainer { size_t length() const; void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(Store); - SET_SELF_SIZE(Store); + SET_MEMORY_INFO_NAME(Store) + SET_SELF_SIZE(Store) private: template @@ -98,8 +98,8 @@ class QuicError final : public MemoryRetainer { bool operator!=(const QuicError& other) const; void MemoryInfo(MemoryTracker* tracker) const override; - SET_MEMORY_INFO_NAME(QuicError); - SET_SELF_SIZE(QuicError); + SET_MEMORY_INFO_NAME(QuicError) + SET_SELF_SIZE(QuicError) std::string ToString() const; v8::MaybeLocal ToV8Value(Environment* env) const; From 70695c36766ef3849f4375cf56540a2e5a885650 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Mar 2023 17:53:11 -0400 Subject: [PATCH 09/11] quic: fixup silly windows build errors --- src/quic/data.cc | 8 ++++---- src/quic/data.h | 4 ++-- src/quic/preferredaddress.cc | 4 ++-- src/quic/preferredaddress.h | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/quic/data.cc b/src/quic/data.cc index dc19dedd02d62a..809f84b51e92b5 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -127,8 +127,8 @@ QuicError::QuicError(const ngtcp2_connection_close_error& error) ptr_(&error_) {} QuicError::operator bool() const { - if ((code() == NO_ERROR && type() == Type::TRANSPORT) || - ((code() == APP_NO_ERROR && type() == Type::APPLICATION))) { + if ((code() == QUIC_NO_ERROR && type() == Type::TRANSPORT) || + ((code() == QUIC_APP_NO_ERROR && type() == Type::APPLICATION))) { return false; } return true; @@ -243,9 +243,9 @@ QuicError QuicError::FromConnectionClose(ngtcp2_conn* session) { } QuicError QuicError::TRANSPORT_NO_ERROR = - QuicError::ForTransport(QuicError::NO_ERROR); + QuicError::ForTransport(QuicError::QUIC_NO_ERROR); QuicError QuicError::APPLICATION_NO_ERROR = - QuicError::ForApplication(QuicError::APP_NO_ERROR); + QuicError::ForApplication(QuicError::QUIC_APP_NO_ERROR); QuicError QuicError::VERSION_NEGOTIATION = QuicError::ForVersionNegotiation(); QuicError QuicError::IDLE_CLOSE = QuicError::ForIdleClose(); QuicError QuicError::INTERNAL_ERROR = diff --git a/src/quic/data.h b/src/quic/data.h index 8e025e448741e2..c4106316c7b689 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -62,8 +62,8 @@ class QuicError final : public MemoryRetainer { public: using error_code = uint64_t; - static constexpr error_code NO_ERROR = NGTCP2_NO_ERROR; - static constexpr error_code APP_NO_ERROR = 65280; + static constexpr error_code QUIC_NO_ERROR = NGTCP2_NO_ERROR; + static constexpr error_code QUIC_APP_NO_ERROR = 65280; enum class Type { TRANSPORT = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, diff --git a/src/quic/preferredaddress.cc b/src/quic/preferredaddress.cc index 463605110e07e7..76dd7d1436c0e9 100644 --- a/src/quic/preferredaddress.cc +++ b/src/quic/preferredaddress.cc @@ -98,9 +98,9 @@ Maybe PreferredAddress::GetPolicy( if (value->Uint32Value(env->context()).To(&val)) { switch (val) { case QUIC_PREFERRED_ADDRESS_USE: - return Just(Policy::USE); + return Just(Policy::USE_PREFERRED_ADDRESS); case QUIC_PREFERRED_ADDRESS_IGNORE: - return Just(Policy::IGNORE); + return Just(Policy::IGNORE_PREFERRED_ADDRESS); } } THROW_ERR_INVALID_ARG_VALUE( diff --git a/src/quic/preferredaddress.h b/src/quic/preferredaddress.h index 1d1c90f2af1b8d..5914ac600ca1a4 100644 --- a/src/quic/preferredaddress.h +++ b/src/quic/preferredaddress.h @@ -19,17 +19,17 @@ class PreferredAddress final { public: enum class Policy { // Ignore the server-advertised preferred address. - IGNORE, + IGNORE_PREFERRED_ADDRESS, // Use the server-advertised preferred address. - USE, + USE_PREFERRED_ADDRESS, }; // The QUIC_* constants are expected to be exported out to be used on // the JavaScript side of the API. static constexpr uint32_t QUIC_PREFERRED_ADDRESS_USE = - static_cast(Policy::USE); + static_cast(Policy::USE_PREFERRED_ADDRESS); static constexpr uint32_t QUIC_PREFERRED_ADDRESS_IGNORE = - static_cast(Policy::IGNORE); + static_cast(Policy::IGNORE_PREFERRED_ADDRESS); static v8::Maybe GetPolicy(Environment* env, v8::Local value); From 7d8e349534ca95fbb7645f44a995840f413ed4ff Mon Sep 17 00:00:00 2001 From: James M Snell Date: Mon, 27 Mar 2023 15:13:22 -0700 Subject: [PATCH 10/11] quic: add shared openssl build guard --- src/quic/cid.cc | 2 ++ src/quic/cid.h | 3 ++- src/quic/data.cc | 4 ++++ src/quic/data.h | 2 ++ src/quic/preferredaddress.cc | 4 ++++ src/quic/preferredaddress.h | 2 ++ test/cctest/test_quic_cid.cc | 2 ++ 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 06c12ca4c182d9..103fed860d40e1 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -1,3 +1,4 @@ +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include "cid.h" #include #include @@ -148,3 +149,4 @@ const CID::Factory& CID::Factory::random() { } // namespace quic } // namespace node +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC diff --git a/src/quic/cid.h b/src/quic/cid.h index c67d5405507d91..bfd6eb47c9ff9b 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 @@ -122,4 +122,5 @@ class CID::Factory { } // namespace quic } // namespace node +#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 809f84b51e92b5..fcc2335db7adb9 100644 --- a/src/quic/data.cc +++ b/src/quic/data.cc @@ -1,3 +1,5 @@ +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC + #include "data.h" #include #include @@ -253,3 +255,5 @@ QuicError QuicError::INTERNAL_ERROR = } // namespace quic } // namespace node + +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC diff --git a/src/quic/data.h b/src/quic/data.h index c4106316c7b689..14a613df69196b 100644 --- a/src/quic/data.h +++ b/src/quic/data.h @@ -1,6 +1,7 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include #include @@ -132,4 +133,5 @@ class QuicError final : public MemoryRetainer { } // namespace quic } // namespace node +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/quic/preferredaddress.cc b/src/quic/preferredaddress.cc index 76dd7d1436c0e9..180241cf272aa8 100644 --- a/src/quic/preferredaddress.cc +++ b/src/quic/preferredaddress.cc @@ -1,3 +1,5 @@ +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC + #include "preferredaddress.h" #include #include @@ -153,3 +155,5 @@ void PreferredAddress::Set(ngtcp2_transport_params* params, } // namespace quic } // namespace node + +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC diff --git a/src/quic/preferredaddress.h b/src/quic/preferredaddress.h index 5914ac600ca1a4..6be468fac2cd08 100644 --- a/src/quic/preferredaddress.h +++ b/src/quic/preferredaddress.h @@ -1,6 +1,7 @@ #pragma once #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include #include @@ -67,4 +68,5 @@ class PreferredAddress final { } // namespace quic } // namespace node +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/test/cctest/test_quic_cid.cc b/test/cctest/test_quic_cid.cc index ae5779ccafc77b..44e4e5d7b998e7 100644 --- a/test/cctest/test_quic_cid.cc +++ b/test/cctest/test_quic_cid.cc @@ -1,3 +1,4 @@ +#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include #include #include @@ -91,3 +92,4 @@ TEST(CID, Basic) { } } } +#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC From dde6f5d2b2c3a3dd3716acd29c06dc8bdc1951da Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 28 Mar 2023 07:54:03 -0700 Subject: [PATCH 11/11] fixup! quic: add the CID implementation --- src/quic/cid.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/quic/cid.cc b/src/quic/cid.cc index 103fed860d40e1..019896104fb63b 100644 --- a/src/quic/cid.cc +++ b/src/quic/cid.cc @@ -15,11 +15,7 @@ CID::CID() : ptr_(&cid_) { cid_.datalen = 0; } -CID::CID(const ngtcp2_cid& cid) : CID() { - DCHECK_GE(cid.datalen, kMinLength); - DCHECK_LE(cid.datalen, kMaxLength); - ngtcp2_cid_init(&cid_, cid.data, cid.datalen); -} +CID::CID(const ngtcp2_cid& cid) : CID(cid.data, cid.datalen) {} CID::CID(const uint8_t* data, size_t len) : CID() { DCHECK_GE(len, kMinLength);