| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent da2210e commit 09a4bb1
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -335,6 +335,14 @@ | |||
| 335 | 335 | 'src/node_crypto.cc', | |
| 336 | 336 | 'src/node_crypto.h', | |
| 337 | 337 | ], | |
| 338 | + 'node_quic_sources': [ | ||
| 339 | + 'src/quic/cid.cc', | ||
| 340 | + 'src/quic/data.cc', | ||
| 341 | + 'src/quic/preferredaddress.cc', | ||
| 342 | + 'src/quic/cid.h', | ||
| 343 | + 'src/quic/data.h', | ||
| 344 | + 'src/quic/preferredaddress.h', | ||
| 345 | + ], | ||
| 338 | 346 | 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', | |
| 339 | 347 | 'conditions': [ | |
| 340 | 348 | ['GENERATOR == "ninja"', { | |
@@ -836,6 +844,7 @@ | |||
| 836 | 844 | [ 'node_use_openssl=="true"', { | |
| 837 | 845 | 'sources': [ | |
| 838 | 846 | '<@(node_crypto_sources)', | |
| 847 | + '<@(node_quic_sources)', | ||
| 839 | 848 | ], | |
| 840 | 849 | }], | |
| 841 | 850 | [ 'OS in "linux freebsd mac solaris" and ' | |
@@ -1023,6 +1032,7 @@ | |||
| 1023 | 1032 | 'sources': [ | |
| 1024 | 1033 | 'test/cctest/test_crypto_clienthello.cc', | |
| 1025 | 1034 | 'test/cctest/test_node_crypto.cc', | |
| 1035 | + 'test/cctest/test_quic_cid.cc', | ||
| 1026 | 1036 | ] | |
| 1027 | 1037 | }], | |
| 1028 | 1038 | ['v8_enable_inspector==1', { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,148 @@ | |||
| 1 | + #if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC | ||
| 2 | + #include "cid.h" | ||
| 3 | + #include <crypto/crypto_util.h> | ||
| 4 | + #include <memory_tracker-inl.h> | ||
| 5 | + #include <node_mutex.h> | ||
| 6 | + #include <string_bytes.h> | ||
| 7 | + | ||
| 8 | + namespace node { | ||
| 9 | + namespace quic { | ||
| 10 | + | ||
| 11 | + // ============================================================================ | ||
| 12 | + // CID | ||
| 13 | + | ||
| 14 | + CID::CID() : ptr_(&cid_) { | ||
| 15 | + cid_.datalen = 0; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + CID::CID(const ngtcp2_cid& cid) : CID(cid.data, cid.datalen) {} | ||
| 19 | + | ||
| 20 | + CID::CID(const uint8_t* data, size_t len) : CID() { | ||
| 21 | + DCHECK_GE(len, kMinLength); | ||
| 22 | + DCHECK_LE(len, kMaxLength); | ||
| 23 | + ngtcp2_cid_init(&cid_, data, len); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + CID::CID(const ngtcp2_cid* cid) : ptr_(cid) { | ||
| 27 | + CHECK_NOT_NULL(cid); | ||
| 28 | + DCHECK_GE(cid->datalen, kMinLength); | ||
| 29 | + DCHECK_LE(cid->datalen, kMaxLength); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + CID::CID(const CID& other) : ptr_(&cid_) { | ||
| 33 | + CHECK_NOT_NULL(other.ptr_); | ||
| 34 | + ngtcp2_cid_init(&cid_, other.ptr_->data, other.ptr_->datalen); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + bool CID::operator==(const CID& other) const noexcept { | ||
| 38 | + if (this == &other || (length() == 0 && other.length() == 0)) return true; | ||
| 39 | + if (length() != other.length()) return false; | ||
| 40 | + return memcmp(ptr_->data, other.ptr_->data, ptr_->datalen) == 0; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + bool CID::operator!=(const CID& other) const noexcept { | ||
| 44 | + return !(*this == other); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + CID::operator const uint8_t*() const { | ||
| 48 | + return ptr_->data; | ||
| 49 | + } | ||
| 50 | + CID::operator const ngtcp2_cid&() const { | ||
| 51 | + return *ptr_; | ||
| 52 | + } | ||
| 53 | + CID::operator const ngtcp2_cid*() const { | ||
| 54 | + return ptr_; | ||
| 55 | + } | ||
| 56 | + CID::operator bool() const { | ||
| 57 | + return ptr_->datalen >= kMinLength; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + size_t CID::length() const { | ||
| 61 | + return ptr_->datalen; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + std::string CID::ToString() const { | ||
| 65 | + char dest[kMaxLength * 2]; | ||
| 66 | + size_t written = | ||
| 67 | + StringBytes::hex_encode(reinterpret_cast<const char*>(ptr_->data), | ||
| 68 | + ptr_->datalen, | ||
| 69 | + dest, | ||
| 70 | + arraysize(dest)); | ||
| 71 | + return std::string(dest, written); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + CID CID::kInvalid{}; | ||
| 75 | + | ||
| 76 | + // ============================================================================ | ||
| 77 | + // CID::Hash | ||
| 78 | + | ||
| 79 | + size_t CID::Hash::operator()(const CID& cid) const { | ||
| 80 | + size_t hash = 0; | ||
| 81 | + for (size_t n = 0; n < cid.length(); n++) { | ||
| 82 | + hash ^= std::hash<uint8_t>{}(cid.ptr_->data[n] + 0x9e3779b9 + (hash << 6) + | ||
| 83 | + (hash >> 2)); | ||
| 84 | + } | ||
| 85 | + return hash; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + // ============================================================================ | ||
| 89 | + // CID::Factory | ||
| 90 | + | ||
| 91 | + namespace { | ||
| 92 | + class RandomCIDFactory : public CID::Factory { | ||
| 93 | + public: | ||
| 94 | + RandomCIDFactory() = default; | ||
| 95 | + RandomCIDFactory(const RandomCIDFactory&) = delete; | ||
| 96 | + RandomCIDFactory(RandomCIDFactory&&) = delete; | ||
| 97 | + RandomCIDFactory& operator=(const RandomCIDFactory&) = delete; | ||
| 98 | + RandomCIDFactory& operator=(RandomCIDFactory&&) = delete; | ||
| 99 | + | ||
| 100 | + CID Generate(size_t length_hint) const override { | ||
| 101 | + DCHECK_GE(length_hint, CID::kMinLength); | ||
| 102 | + DCHECK_LE(length_hint, CID::kMaxLength); | ||
| 103 | + Mutex::ScopedLock lock(mutex_); | ||
| 104 | + maybe_refresh_pool(length_hint); | ||
| 105 | + auto start = pool_ + pos_; | ||
| 106 | + pos_ += length_hint; | ||
| 107 | + return CID(start, length_hint); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + void GenerateInto(ngtcp2_cid* cid, | ||
| 111 | + size_t length_hint = CID::kMaxLength) const override { | ||
| 112 | + DCHECK_GE(length_hint, CID::kMinLength); | ||
| 113 | + DCHECK_LE(length_hint, CID::kMaxLength); | ||
| 114 | + Mutex::ScopedLock lock(mutex_); | ||
| 115 | + maybe_refresh_pool(length_hint); | ||
| 116 | + auto start = pool_ + pos_; | ||
| 117 | + pos_ += length_hint; | ||
| 118 | + ngtcp2_cid_init(cid, start, length_hint); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + private: | ||
| 122 | + void maybe_refresh_pool(size_t length_hint) const { | ||
| 123 | + // We generate a pool of random data kPoolSize in length | ||
| 124 | + // and pull our random CID from that. If we don't have | ||
| 125 | + // enough random random remaining in the pool to generate | ||
| 126 | + // a CID of the requested size, we regenerate the pool | ||
| 127 | + // and reset it to zero. | ||
| 128 | + if (pos_ + length_hint > kPoolSize) { | ||
| 129 | + CHECK(crypto::CSPRNG(pool_, kPoolSize).is_ok()); | ||
| 130 | + pos_ = 0; | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + static constexpr int kPoolSize = 4096; | ||
| 135 | + mutable int pos_ = kPoolSize; | ||
| 136 | + mutable uint8_t pool_[kPoolSize]; | ||
| 137 | + mutable Mutex mutex_; | ||
| 138 | + }; | ||
| 139 | + } // namespace | ||
| 140 | + | ||
| 141 | + const CID::Factory& CID::Factory::random() { | ||
| 142 | + static RandomCIDFactory instance; | ||
| 143 | + return instance; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + } // namespace quic | ||
| 147 | + } // namespace node | ||
| 148 | + #endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,126 @@ | |||
| 1 | + #pragma once | ||
| 2 | + | ||
| 3 | + #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
| 4 | + #if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC | ||
| 5 | + #include <memory_tracker.h> | ||
| 6 | + #include <ngtcp2/ngtcp2.h> | ||
| 7 | + #include <string> | ||
| 8 | + | ||
| 9 | + namespace node { | ||
| 10 | + namespace quic { | ||
| 11 | + | ||
| 12 | + // CIDS are used to identify endpoints participating in a QUIC session. | ||
| 13 | + // Once created, CID instances are immutable. | ||
| 14 | + // | ||
| 15 | + // CIDs contain between 1 to 20 bytes. Most typically they are selected | ||
| 16 | + // randomly but there is a spec for creating "routable" CIDs that encode | ||
| 17 | + // a specific structure that is meaningful only to the side that creates | ||
| 18 | + // the CID. For most purposes, CIDs should be treated as opaque tokens. | ||
| 19 | + // | ||
| 20 | + // Each peer in a QUIC session generates one or more CIDs that the *other* | ||
| 21 | + // peer will use to identify the session. When a QUIC client initiates a | ||
| 22 | + // brand new session, it will initially generates a CID of its own (its | ||
| 23 | + // source CID) and a random placeholder CID for the server (the original | ||
| 24 | + // destination CID). When the server receives the initial packet, it will | ||
| 25 | + // generate its own source CID and use the clients source CID as the | ||
| 26 | + // server's destination CID. | ||
| 27 | + // | ||
| 28 | + // Client Server | ||
| 29 | + // ------------------------------------------- | ||
| 30 | + // Source CID <====> Destination CID | ||
| 31 | + // Destination CID <====> Source CID | ||
| 32 | + // | ||
| 33 | + // While the connection is being established, it is possible for either | ||
| 34 | + // peer to generate additional CIDs that are also associated with the | ||
| 35 | + // connection. | ||
| 36 | + class CID final : public MemoryRetainer { | ||
| 37 | + public: | ||
| 38 | + static constexpr size_t kMinLength = NGTCP2_MIN_CIDLEN; | ||
| 39 | + static constexpr size_t kMaxLength = NGTCP2_MAX_CIDLEN; | ||
| 40 | + | ||
| 41 | + // Copy the given ngtcp2_cid. | ||
| 42 | + explicit CID(const ngtcp2_cid& cid); | ||
| 43 | + | ||
| 44 | + // Copy the given buffer as a CID. The len must be within | ||
| 45 | + // kMinLength and kMaxLength. | ||
| 46 | + explicit CID(const uint8_t* data, size_t len); | ||
| 47 | + | ||
| 48 | + // Wrap the given ngtcp2_cid. The CID does not take ownership | ||
| 49 | + // of the underlying ngtcp2_cid. | ||
| 50 | + explicit CID(const ngtcp2_cid* cid); | ||
| 51 | + | ||
| 52 | + CID(const CID& other); | ||
| 53 | + CID(CID&& other) = delete; | ||
| 54 | + | ||
| 55 | + struct Hash final { | ||
| 56 | + size_t operator()(const CID& cid) const; | ||
| 57 | + }; | ||
| 58 | + | ||
| 59 | + bool operator==(const CID& other) const noexcept; | ||
| 60 | + bool operator!=(const CID& other) const noexcept; | ||
| 61 | + | ||
| 62 | + operator const uint8_t*() const; | ||
| 63 | + operator const ngtcp2_cid&() const; | ||
| 64 | + operator const ngtcp2_cid*() const; | ||
| 65 | + | ||
| 66 | + // True if the CID length is at least kMinLength; | ||
| 67 | + operator bool() const; | ||
| 68 | + size_t length() const; | ||
| 69 | + | ||
| 70 | + std::string ToString() const; | ||
| 71 | + | ||
| 72 | + SET_NO_MEMORY_INFO() | ||
| 73 | + SET_MEMORY_INFO_NAME(CID) | ||
| 74 | + SET_SELF_SIZE(CID) | ||
| 75 | + | ||
| 76 | + template <typename T> | ||
| 77 | + using Map = std::unordered_map<CID, T, CID::Hash>; | ||
| 78 | + | ||
| 79 | + // A CID::Factory, as the name suggests, is used to create new CIDs. | ||
| 80 | + // Per https://datatracker.ietf.org/doc/draft-ietf-quic-load-balancers/, QUIC | ||
| 81 | + // implementations MAY use the Connection ID associated with a QUIC session | ||
| 82 | + // as a routing mechanism, with each CID instance securely encoding the | ||
| 83 | + // routing information. By default, our implementation creates CIDs randomly | ||
| 84 | + // but will allow user code to provide their own CID::Factory implementation. | ||
| 85 | + class Factory; | ||
| 86 | + | ||
| 87 | + static CID kInvalid; | ||
| 88 | + | ||
| 89 | + private: | ||
| 90 | + // The default constructor creates an empty, zero-length CID. | ||
| 91 | + // Zero-length CIDs are not usable. We use them as a placeholder | ||
| 92 | + // for a missing or empty CID value. | ||
| 93 | + CID(); | ||
| 94 | + | ||
| 95 | + ngtcp2_cid cid_; | ||
| 96 | + const ngtcp2_cid* ptr_; | ||
| 97 | + | ||
| 98 | + friend struct Hash; | ||
| 99 | + }; | ||
| 100 | + | ||
| 101 | + class CID::Factory { | ||
| 102 | + public: | ||
| 103 | + virtual ~Factory() = default; | ||
| 104 | + | ||
| 105 | + // Generate a new CID. The length_hint must be between CID::kMinLength | ||
| 106 | + // and CID::kMaxLength. The implementation can choose to ignore the length. | ||
| 107 | + virtual CID Generate(size_t length_hint = CID::kMaxLength) const = 0; | ||
| 108 | + | ||
| 109 | + // Generate a new CID into the given ngtcp2_cid. This variation of | ||
| 110 | + // Generate should be used far less commonly. It is provided largely | ||
| 111 | + // for a couple of internal cases. | ||
| 112 | + virtual void GenerateInto(ngtcp2_cid* cid, | ||
| 113 | + size_t length_hint = CID::kMaxLength) const = 0; | ||
| 114 | + | ||
| 115 | + // The default random CID generator instance. | ||
| 116 | + static const Factory& random(); | ||
| 117 | + | ||
| 118 | + // TODO(@jasnell): This will soon also include additional implementations | ||
| 119 | + // of CID::Factory that implement the QUIC Load Balancers spec. | ||
| 120 | + }; | ||
| 121 | + | ||
| 122 | + } // namespace quic | ||
| 123 | + } // namespace node | ||
| 124 | + | ||
| 125 | + #endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC | ||
| 126 | + #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments