1#if HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC 2 3#include "packet.h" 4#include <base_object-inl.h> 5#include <crypto/crypto_util.h> 6#include <env-inl.h> 7#include <ngtcp2/ngtcp2.h> 8#include <ngtcp2/ngtcp2_crypto.h> 9#include <node_sockaddr-inl.h> 10#include <req_wrap-inl.h> 11#include <uv.h> 12#include <v8.h> 13#include <string> 14#include "bindingdata.h" 15#include "cid.h" 16#include "tokens.h" 17 18namespace node { 19 20using v8::FunctionTemplate; 21using v8::Local; 22using v8::Object; 23 24namespace quic { 25 26namespace { 27static constexpr size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5; 28static constexpr size_t kMinStatelessResetLen = 41; 29static constexpr size_t kMaxFreeList = 100; 30} // namespace 31 32struct Packet::Data final : public MemoryRetainer { 33 MaybeStackBuffer<uint8_t, kDefaultMaxPacketLength> data_; 34 35 // The diagnostic_label_ is used only as a debugging tool when 36 // logging debug information about the packet. It identifies 37 // the purpose of the packet. 38 const std::string diagnostic_label_; 39 40 void MemoryInfo(MemoryTracker* tracker) const override { 41 tracker->TrackFieldWithSize("data", data_.length()); 42 } 43 SET_MEMORY_INFO_NAME(Data) 44 SET_SELF_SIZE(Data) 45 46 Data(size_t length, std::string_view diagnostic_label) 47 : diagnostic_label_(diagnostic_label) { 48 data_.AllocateSufficientStorage(length); 49 } 50 51 size_t length() const { return data_.length(); } 52 operator uv_buf_t() { 53 return uv_buf_init(reinterpret_cast<char*>(data_.out()), data_.length()); 54 } 55 operator ngtcp2_vec() { return ngtcp2_vec{data_.out(), data_.length()}; } 56 57 std::string ToString() const { 58 return diagnostic_label_ + ", " + std::to_string(length()); 59 } 60}; 61 62const SocketAddress& Packet::destination() const { 63 return destination_; 64} 65 66bool Packet::is_sending() const { 67 return !!handle_; 68} 69 70size_t Packet::length() const { 71 return data_ ? data_->length() : 0; 72} 73 74Packet::operator uv_buf_t() const { 75 return !data_ ? uv_buf_init(nullptr, 0) : *data_; 76} 77 78Packet::operator ngtcp2_vec() const { 79 return !data_ ? ngtcp2_vec{nullptr, 0} : *data_; 80} 81 82void Packet::Truncate(size_t len) { 83 DCHECK(data_); 84 DCHECK_LE(len, data_->length()); 85 data_->data_.SetLength(len); 86} 87 88Local<FunctionTemplate> Packet::GetConstructorTemplate(Environment* env) { 89 auto& state = BindingData::Get(env); 90 Local<FunctionTemplate> tmpl = state.packet_constructor_template(); 91 if (tmpl.IsEmpty()) { 92 tmpl = NewFunctionTemplate(env->isolate(), IllegalConstructor); 93 tmpl->Inherit(ReqWrap<uv_udp_send_t>::GetConstructorTemplate(env)); 94 tmpl->InstanceTemplate()->SetInternalFieldCount( 95 Packet::kInternalFieldCount); 96 tmpl->SetClassName(state.packetwrap_string()); 97 state.set_packet_constructor_template(tmpl); 98 } 99 return tmpl; 100} 101 102BaseObjectPtr<Packet> Packet::Create(Environment* env, 103 Listener* listener, 104 const SocketAddress& destination, 105 size_t length, 106 const char* diagnostic_label) { 107 auto& binding = BindingData::Get(env); 108 if (binding.packet_freelist.empty()) { 109 Local<Object> obj; 110 if (UNLIKELY(!GetConstructorTemplate(env) 111 ->InstanceTemplate() 112 ->NewInstance(env->context()) 113 .ToLocal(&obj))) { 114 return BaseObjectPtr<Packet>(); 115 } 116 117 return MakeBaseObject<Packet>( 118 env, listener, obj, destination, length, diagnostic_label); 119 } 120 121 return FromFreeList(env, 122 std::make_shared<Data>(length, diagnostic_label), 123 listener, 124 destination); 125} 126 127BaseObjectPtr<Packet> Packet::Clone() const { 128 auto& binding = BindingData::Get(env()); 129 if (binding.packet_freelist.empty()) { 130 Local<Object> obj; 131 if (UNLIKELY(!GetConstructorTemplate(env()) 132 ->InstanceTemplate() 133 ->NewInstance(env()->context()) 134 .ToLocal(&obj))) { 135 return BaseObjectPtr<Packet>(); 136 } 137 138 return MakeBaseObject<Packet>(env(), listener_, obj, destination_, data_); 139 } 140 141 return FromFreeList(env(), data_, listener_, destination_); 142} 143 144BaseObjectPtr<Packet> Packet::FromFreeList(Environment* env, 145 std::shared_ptr<Data> data, 146 Listener* listener, 147 const SocketAddress& destination) { 148 auto& binding = BindingData::Get(env); 149 auto obj = binding.packet_freelist.back(); 150 binding.packet_freelist.pop_back(); 151 DCHECK_EQ(env, obj->env()); 152 auto packet = static_cast<Packet*>(obj.get()); 153 packet->data_ = std::move(data); 154 packet->destination_ = destination; 155 packet->listener_ = listener; 156 return BaseObjectPtr<Packet>(packet); 157} 158 159Packet::Packet(Environment* env, 160 Listener* listener, 161 Local<Object> object, 162 const SocketAddress& destination, 163 std::shared_ptr<Data> data) 164 : ReqWrap<uv_udp_send_t>(env, object, AsyncWrap::PROVIDER_QUIC_PACKET), 165 listener_(listener), 166 destination_(destination), 167 data_(std::move(data)) {} 168 169Packet::Packet(Environment* env, 170 Listener* listener, 171 Local<Object> object, 172 const SocketAddress& destination, 173 size_t length, 174 const char* diagnostic_label) 175 : Packet(env, 176 listener, 177 object, 178 destination, 179 std::make_shared<Data>(length, diagnostic_label)) {} 180 181int Packet::Send(uv_udp_t* handle, BaseObjectPtr<BaseObject> ref) { 182 if (is_sending()) return UV_EALREADY; 183 if (data_ == nullptr) return UV_EINVAL; 184 DCHECK(!is_sending()); 185 handle_ = std::move(ref); 186 uv_buf_t buf = *this; 187 return Dispatch( 188 uv_udp_send, 189 handle, 190 &buf, 191 1, 192 destination().data(), 193 uv_udp_send_cb{[](uv_udp_send_t* req, int status) { 194 auto ptr = static_cast<Packet*>(ReqWrap<uv_udp_send_t>::from_req(req)); 195 ptr->Done(status); 196 // Do not try accessing ptr after this. We don't know if it 197 // was freelisted or destroyed. Either way, done means done. 198 }}); 199} 200 201void Packet::Done(int status) { 202 DCHECK_NOT_NULL(listener_); 203 listener_->PacketDone(status); 204 handle_.reset(); 205 data_.reset(); 206 listener_ = nullptr; 207 Reset(); 208 209 // As a performance optimization, we add this packet to a freelist 210 // rather than deleting it but only if the freelist isn't too 211 // big, we don't want to accumulate these things forever. 212 auto& binding = BindingData::Get(env()); 213 if (binding.packet_freelist.size() < kMaxFreeList) { 214 binding.packet_freelist.emplace_back(this); 215 } else { 216 delete this; 217 } 218} 219 220std::string Packet::ToString() const { 221 if (!data_) return "Packet (<empty>)"; 222 return "Packet (" + data_->ToString() + ")"; 223} 224 225void Packet::MemoryInfo(MemoryTracker* tracker) const { 226 tracker->TrackField("destination", destination_); 227 tracker->TrackField("data", data_); 228 tracker->TrackField("handle", handle_); 229} 230 231BaseObjectPtr<Packet> Packet::CreateRetryPacket( 232 Environment* env, 233 Listener* listener, 234 const PathDescriptor& path_descriptor, 235 const TokenSecret& token_secret) { 236 auto& random = CID::Factory::random(); 237 CID cid = random.Generate(); 238 RetryToken token(path_descriptor.version, 239 path_descriptor.remote_address, 240 cid, 241 path_descriptor.dcid, 242 token_secret); 243 if (!token) return BaseObjectPtr<Packet>(); 244 245 const ngtcp2_vec& vec = token; 246 247 size_t pktlen = 248 vec.len + (2 * NGTCP2_MAX_CIDLEN) + path_descriptor.scid.length() + 8; 249 250 auto packet = 251 Create(env, listener, path_descriptor.remote_address, pktlen, "retry"); 252 if (!packet) return BaseObjectPtr<Packet>(); 253 254 ngtcp2_vec dest = *packet; 255 256 ssize_t nwrite = ngtcp2_crypto_write_retry(dest.base, 257 pktlen, 258 path_descriptor.version, 259 path_descriptor.scid, 260 cid, 261 path_descriptor.dcid, 262 vec.base, 263 vec.len); 264 if (nwrite <= 0) return BaseObjectPtr<Packet>(); 265 packet->Truncate(static_cast<size_t>(nwrite)); 266 return packet; 267} 268 269BaseObjectPtr<Packet> Packet::CreateConnectionClosePacket( 270 Environment* env, 271 Listener* listener, 272 const SocketAddress& destination, 273 ngtcp2_conn* conn, 274 const QuicError& error) { 275 auto packet = Packet::Create( 276 env, listener, destination, kDefaultMaxPacketLength, "connection close"); 277 ngtcp2_vec vec = *packet; 278 279 ssize_t nwrite = ngtcp2_conn_write_connection_close( 280 conn, nullptr, nullptr, vec.base, vec.len, error, uv_hrtime()); 281 if (nwrite < 0) return BaseObjectPtr<Packet>(); 282 packet->Truncate(static_cast<size_t>(nwrite)); 283 return packet; 284} 285 286BaseObjectPtr<Packet> Packet::CreateImmediateConnectionClosePacket( 287 Environment* env, 288 Listener* listener, 289 const SocketAddress& destination, 290 const PathDescriptor& path_descriptor, 291 const QuicError& reason) { 292 auto packet = Packet::Create(env, 293 listener, 294 path_descriptor.remote_address, 295 kDefaultMaxPacketLength, 296 "immediate connection close (endpoint)"); 297 ngtcp2_vec vec = *packet; 298 ssize_t nwrite = ngtcp2_crypto_write_connection_close( 299 vec.base, 300 vec.len, 301 path_descriptor.version, 302 path_descriptor.dcid, 303 path_descriptor.scid, 304 reason.code(), 305 // We do not bother sending a reason string here, even if 306 // there is one in the QuicError 307 nullptr, 308 0); 309 if (nwrite <= 0) return BaseObjectPtr<Packet>(); 310 packet->Truncate(static_cast<size_t>(nwrite)); 311 return packet; 312} 313 314BaseObjectPtr<Packet> Packet::CreateStatelessResetPacket( 315 Environment* env, 316 Listener* listener, 317 const PathDescriptor& path_descriptor, 318 const TokenSecret& token_secret, 319 size_t source_len) { 320 // Per the QUIC spec, a stateless reset token must be strictly smaller than 321 // the packet that triggered it. This is one of the mechanisms to prevent 322 // infinite looping exchange of stateless tokens with the peer. An endpoint 323 // should never send a stateless reset token smaller than 41 bytes per the 324 // QUIC spec. The reason is that packets less than 41 bytes may allow an 325 // observer to reliably determine that it's a stateless reset. 326 size_t pktlen = source_len - 1; 327 if (pktlen < kMinStatelessResetLen) return BaseObjectPtr<Packet>(); 328 329 StatelessResetToken token(token_secret, path_descriptor.dcid); 330 uint8_t random[kRandlen]; 331 CHECK(crypto::CSPRNG(random, kRandlen).is_ok()); 332 333 auto packet = Packet::Create(env, 334 listener, 335 path_descriptor.remote_address, 336 kDefaultMaxPacketLength, 337 "stateless reset"); 338 ngtcp2_vec vec = *packet; 339 340 ssize_t nwrite = ngtcp2_pkt_write_stateless_reset( 341 vec.base, pktlen, token, random, kRandlen); 342 if (nwrite <= static_cast<ssize_t>(kMinStatelessResetLen)) { 343 return BaseObjectPtr<Packet>(); 344 } 345 346 packet->Truncate(static_cast<size_t>(nwrite)); 347 return packet; 348} 349 350BaseObjectPtr<Packet> Packet::CreateVersionNegotiationPacket( 351 Environment* env, 352 Listener* listener, 353 const PathDescriptor& path_descriptor) { 354 const auto generateReservedVersion = [&] { 355 socklen_t addrlen = path_descriptor.remote_address.length(); 356 uint32_t h = 0x811C9DC5u; 357 uint32_t ver = htonl(path_descriptor.version); 358 const uint8_t* p = path_descriptor.remote_address.raw(); 359 const uint8_t* ep = p + addrlen; 360 for (; p != ep; ++p) { 361 h ^= *p; 362 h *= 0x01000193u; 363 } 364 p = reinterpret_cast<const uint8_t*>(&ver); 365 ep = p + sizeof(path_descriptor.version); 366 for (; p != ep; ++p) { 367 h ^= *p; 368 h *= 0x01000193u; 369 } 370 h &= 0xf0f0f0f0u; 371 h |= NGTCP2_RESERVED_VERSION_MASK; 372 return h; 373 }; 374 375 uint32_t sv[3] = { 376 generateReservedVersion(), NGTCP2_PROTO_VER_MIN, NGTCP2_PROTO_VER_MAX}; 377 378 size_t pktlen = path_descriptor.dcid.length() + 379 path_descriptor.scid.length() + (sizeof(sv)) + 7; 380 381 auto packet = Packet::Create(env, 382 listener, 383 path_descriptor.remote_address, 384 kDefaultMaxPacketLength, 385 "version negotiation"); 386 ngtcp2_vec vec = *packet; 387 388 ssize_t nwrite = 389 ngtcp2_pkt_write_version_negotiation(vec.base, 390 pktlen, 391 0, 392 path_descriptor.dcid, 393 path_descriptor.dcid.length(), 394 path_descriptor.scid, 395 path_descriptor.scid.length(), 396 sv, 397 arraysize(sv)); 398 if (nwrite <= 0) return BaseObjectPtr<Packet>(); 399 packet->Truncate(static_cast<size_t>(nwrite)); 400 return packet; 401} 402 403} // namespace quic 404} // namespace node 405 406#endif // HAVE_OPENSSL && NODE_OPENSSL_HAS_QUIC 407