%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/packet.cc |
#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC #include "packet.h" #include <base_object-inl.h> #include <crypto/crypto_util.h> #include <env-inl.h> #include <ngtcp2/ngtcp2.h> #include <ngtcp2/ngtcp2_crypto.h> #include <node_sockaddr-inl.h> #include <req_wrap-inl.h> #include <uv.h> #include <v8.h> #include <string> #include "bindingdata.h" #include "cid.h" #include "defs.h" #include "tokens.h" namespace node { using v8::FunctionTemplate; using v8::Local; using v8::Object; namespace quic { namespace { static constexpr size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5; static constexpr size_t kMinStatelessResetLen = 41; static constexpr size_t kMaxFreeList = 100; } // namespace std::string PathDescriptor::ToString() const { DebugIndentScope indent; auto prefix = indent.Prefix(); std::string res = "{"; res += prefix + "version: " + std::to_string(version); res += prefix + "dcid: " + dcid.ToString(); res += prefix + "scid: " + scid.ToString(); res += prefix + "local address: " + local_address.ToString(); res += prefix + "remote address: " + remote_address.ToString(); res += indent.Close(); return res; } struct Packet::Data final : public MemoryRetainer { MaybeStackBuffer<uint8_t, kDefaultMaxPacketLength> data_; // The diagnostic_label_ is used only as a debugging tool when // logging debug information about the packet. It identifies // the purpose of the packet. const std::string diagnostic_label_; void MemoryInfo(MemoryTracker* tracker) const override { tracker->TrackFieldWithSize("data", data_.length()); } SET_MEMORY_INFO_NAME(Data) SET_SELF_SIZE(Data) Data(size_t length, std::string_view diagnostic_label) : diagnostic_label_(diagnostic_label) { data_.AllocateSufficientStorage(length); } size_t length() const { return data_.length(); } operator uv_buf_t() { return uv_buf_init(reinterpret_cast<char*>(data_.out()), data_.length()); } operator ngtcp2_vec() { return ngtcp2_vec{data_.out(), data_.length()}; } std::string ToString() const { return diagnostic_label_ + ", " + std::to_string(length()); } }; const SocketAddress& Packet::destination() const { return destination_; } size_t Packet::length() const { return data_ ? data_->length() : 0; } Packet::operator uv_buf_t() const { return !data_ ? uv_buf_init(nullptr, 0) : *data_; } Packet::operator ngtcp2_vec() const { return !data_ ? ngtcp2_vec{nullptr, 0} : *data_; } void Packet::Truncate(size_t len) { DCHECK(data_); DCHECK_LE(len, data_->length()); data_->data_.SetLength(len); } Local<FunctionTemplate> Packet::GetConstructorTemplate(Environment* env) { auto& state = BindingData::Get(env); Local<FunctionTemplate> tmpl = state.packet_constructor_template(); if (tmpl.IsEmpty()) { tmpl = NewFunctionTemplate(env->isolate(), IllegalConstructor); tmpl->Inherit(ReqWrap<uv_udp_send_t>::GetConstructorTemplate(env)); tmpl->InstanceTemplate()->SetInternalFieldCount( Packet::kInternalFieldCount); tmpl->SetClassName(state.packetwrap_string()); state.set_packet_constructor_template(tmpl); } return tmpl; } Packet* Packet::Create(Environment* env, Listener* listener, const SocketAddress& destination, size_t length, const char* diagnostic_label) { if (BindingData::Get(env).packet_freelist.empty()) { Local<Object> obj; if (UNLIKELY(!GetConstructorTemplate(env) ->InstanceTemplate() ->NewInstance(env->context()) .ToLocal(&obj))) { return nullptr; } return new Packet( env, listener, obj, destination, length, diagnostic_label); } return FromFreeList(env, std::make_shared<Data>(length, diagnostic_label), listener, destination); } Packet* Packet::Clone() const { auto& binding = BindingData::Get(env()); if (binding.packet_freelist.empty()) { Local<Object> obj; if (UNLIKELY(!GetConstructorTemplate(env()) ->InstanceTemplate() ->NewInstance(env()->context()) .ToLocal(&obj))) { return nullptr; } return new Packet(env(), listener_, obj, destination_, data_); } return FromFreeList(env(), data_, listener_, destination_); } Packet* Packet::FromFreeList(Environment* env, std::shared_ptr<Data> data, Listener* listener, const SocketAddress& destination) { auto& binding = BindingData::Get(env); if (binding.packet_freelist.empty()) return nullptr; Packet* packet = binding.packet_freelist.back(); binding.packet_freelist.pop_back(); CHECK_NOT_NULL(packet); CHECK_EQ(env, packet->env()); Debug(packet, "Reusing packet from freelist"); packet->data_ = data; packet->destination_ = destination; packet->listener_ = listener; return packet; } Packet::Packet(Environment* env, Listener* listener, Local<Object> object, const SocketAddress& destination, std::shared_ptr<Data> data) : ReqWrap<uv_udp_send_t>(env, object, AsyncWrap::PROVIDER_QUIC_PACKET), listener_(listener), destination_(destination), data_(std::move(data)) { ClearWeak(); Debug(this, "Created a new packet"); } Packet::Packet(Environment* env, Listener* listener, Local<Object> object, const SocketAddress& destination, size_t length, const char* diagnostic_label) : Packet(env, listener, object, destination, std::make_shared<Data>(length, diagnostic_label)) {} void Packet::Done(int status) { Debug(this, "Packet is done with status %d", status); if (listener_ != nullptr) { listener_->PacketDone(status); } // As a performance optimization, we add this packet to a freelist // rather than deleting it but only if the freelist isn't too // big, we don't want to accumulate these things forever. auto& binding = BindingData::Get(env()); if (binding.packet_freelist.size() < kMaxFreeList) { Debug(this, "Returning packet to freelist"); listener_ = nullptr; data_.reset(); Reset(); binding.packet_freelist.push_back(this); } else { delete this; } } std::string Packet::ToString() const { if (!data_) return "Packet (<empty>)"; return "Packet (" + data_->ToString() + ")"; } void Packet::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("destination", destination_); tracker->TrackField("data", data_); } Packet* Packet::CreateRetryPacket(Environment* env, Listener* listener, const PathDescriptor& path_descriptor, const TokenSecret& token_secret) { auto& random = CID::Factory::random(); CID cid = random.Generate(); RetryToken token(path_descriptor.version, path_descriptor.remote_address, cid, path_descriptor.dcid, token_secret); if (!token) return nullptr; const ngtcp2_vec& vec = token; size_t pktlen = vec.len + (2 * NGTCP2_MAX_CIDLEN) + path_descriptor.scid.length() + 8; auto packet = Create(env, listener, path_descriptor.remote_address, pktlen, "retry"); if (packet == nullptr) return nullptr; ngtcp2_vec dest = *packet; ssize_t nwrite = ngtcp2_crypto_write_retry(dest.base, pktlen, path_descriptor.version, path_descriptor.scid, cid, path_descriptor.dcid, vec.base, vec.len); if (nwrite <= 0) { packet->Done(UV_ECANCELED); return nullptr; } packet->Truncate(static_cast<size_t>(nwrite)); return packet; } Packet* Packet::CreateConnectionClosePacket(Environment* env, Listener* listener, const SocketAddress& destination, ngtcp2_conn* conn, const QuicError& error) { auto packet = Create( env, listener, destination, kDefaultMaxPacketLength, "connection close"); if (packet == nullptr) return nullptr; ngtcp2_vec vec = *packet; ssize_t nwrite = ngtcp2_conn_write_connection_close( conn, nullptr, nullptr, vec.base, vec.len, error, uv_hrtime()); if (nwrite < 0) { packet->Done(UV_ECANCELED); return nullptr; } packet->Truncate(static_cast<size_t>(nwrite)); return packet; } Packet* Packet::CreateImmediateConnectionClosePacket( Environment* env, Listener* listener, const PathDescriptor& path_descriptor, const QuicError& reason) { auto packet = Create(env, listener, path_descriptor.remote_address, kDefaultMaxPacketLength, "immediate connection close (endpoint)"); if (packet == nullptr) return nullptr; ngtcp2_vec vec = *packet; ssize_t nwrite = ngtcp2_crypto_write_connection_close( vec.base, vec.len, path_descriptor.version, path_descriptor.dcid, path_descriptor.scid, reason.code(), // We do not bother sending a reason string here, even if // there is one in the QuicError nullptr, 0); if (nwrite <= 0) { packet->Done(UV_ECANCELED); return nullptr; } packet->Truncate(static_cast<size_t>(nwrite)); return packet; } Packet* Packet::CreateStatelessResetPacket( Environment* env, Listener* listener, const PathDescriptor& path_descriptor, const TokenSecret& token_secret, size_t source_len) { // Per the QUIC spec, a stateless reset token must be strictly smaller than // the packet that triggered it. This is one of the mechanisms to prevent // infinite looping exchange of stateless tokens with the peer. An endpoint // should never send a stateless reset token smaller than 41 bytes per the // QUIC spec. The reason is that packets less than 41 bytes may allow an // observer to reliably determine that it's a stateless reset. size_t pktlen = source_len - 1; if (pktlen < kMinStatelessResetLen) return nullptr; StatelessResetToken token(token_secret, path_descriptor.dcid); uint8_t random[kRandlen]; CHECK(crypto::CSPRNG(random, kRandlen).is_ok()); auto packet = Create(env, listener, path_descriptor.remote_address, kDefaultMaxPacketLength, "stateless reset"); if (packet == nullptr) return nullptr; ngtcp2_vec vec = *packet; ssize_t nwrite = ngtcp2_pkt_write_stateless_reset( vec.base, pktlen, token, random, kRandlen); if (nwrite <= static_cast<ssize_t>(kMinStatelessResetLen)) { packet->Done(UV_ECANCELED); return nullptr; } packet->Truncate(static_cast<size_t>(nwrite)); return packet; } Packet* Packet::CreateVersionNegotiationPacket( Environment* env, Listener* listener, const PathDescriptor& path_descriptor) { const auto generateReservedVersion = [&] { socklen_t addrlen = path_descriptor.remote_address.length(); uint32_t h = 0x811C9DC5u; uint32_t ver = htonl(path_descriptor.version); const uint8_t* p = path_descriptor.remote_address.raw(); const uint8_t* ep = p + addrlen; for (; p != ep; ++p) { h ^= *p; h *= 0x01000193u; } p = reinterpret_cast<const uint8_t*>(&ver); ep = p + sizeof(path_descriptor.version); for (; p != ep; ++p) { h ^= *p; h *= 0x01000193u; } h &= 0xf0f0f0f0u; h |= NGTCP2_RESERVED_VERSION_MASK; return h; }; uint32_t sv[3] = { generateReservedVersion(), NGTCP2_PROTO_VER_MIN, NGTCP2_PROTO_VER_MAX}; size_t pktlen = path_descriptor.dcid.length() + path_descriptor.scid.length() + (sizeof(sv)) + 7; auto packet = Create(env, listener, path_descriptor.remote_address, kDefaultMaxPacketLength, "version negotiation"); if (packet == nullptr) return nullptr; ngtcp2_vec vec = *packet; ssize_t nwrite = ngtcp2_pkt_write_version_negotiation(vec.base, pktlen, 0, path_descriptor.dcid, path_descriptor.dcid.length(), path_descriptor.scid, path_descriptor.scid.length(), sv, arraysize(sv)); if (nwrite <= 0) { packet->Done(UV_ECANCELED); return nullptr; } packet->Truncate(static_cast<size_t>(nwrite)); return packet; } } // namespace quic } // namespace node #endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC