%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/src/quic/
Upload File :
Create Path :
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

Zerion Mini Shell 1.0