• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace node {
19 
20 using v8::FunctionTemplate;
21 using v8::Local;
22 using v8::Object;
23 
24 namespace quic {
25 
26 namespace {
27 static constexpr size_t kRandlen = NGTCP2_MIN_STATELESS_RESET_RANDLEN * 5;
28 static constexpr size_t kMinStatelessResetLen = 41;
29 static constexpr size_t kMaxFreeList = 100;
30 }  // namespace
31 
32 struct 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 
MemoryInfonode::quic::Packet::Data40   void MemoryInfo(MemoryTracker* tracker) const override {
41     tracker->TrackFieldWithSize("data", data_.length());
42   }
43   SET_MEMORY_INFO_NAME(Data)
SET_SELF_SIZEnode::quic::Packet::Data44   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 
lengthnode::quic::Packet::Data51   size_t length() const { return data_.length(); }
operator uv_buf_tnode::quic::Packet::Data52   operator uv_buf_t() {
53     return uv_buf_init(reinterpret_cast<char*>(data_.out()), data_.length());
54   }
operator ngtcp2_vecnode::quic::Packet::Data55   operator ngtcp2_vec() { return ngtcp2_vec{data_.out(), data_.length()}; }
56 
ToStringnode::quic::Packet::Data57   std::string ToString() const {
58     return diagnostic_label_ + ", " + std::to_string(length());
59   }
60 };
61 
destination() const62 const SocketAddress& Packet::destination() const {
63   return destination_;
64 }
65 
is_sending() const66 bool Packet::is_sending() const {
67   return !!handle_;
68 }
69 
length() const70 size_t Packet::length() const {
71   return data_ ? data_->length() : 0;
72 }
73 
operator uv_buf_t() const74 Packet::operator uv_buf_t() const {
75   return !data_ ? uv_buf_init(nullptr, 0) : *data_;
76 }
77 
operator ngtcp2_vec() const78 Packet::operator ngtcp2_vec() const {
79   return !data_ ? ngtcp2_vec{nullptr, 0} : *data_;
80 }
81 
Truncate(size_t len)82 void Packet::Truncate(size_t len) {
83   DCHECK(data_);
84   DCHECK_LE(len, data_->length());
85   data_->data_.SetLength(len);
86 }
87 
GetConstructorTemplate(Environment * env)88 Local<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 
Create(Environment * env,Listener * listener,const SocketAddress & destination,size_t length,const char * diagnostic_label)102 BaseObjectPtr<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 
Clone() const127 BaseObjectPtr<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 
FromFreeList(Environment * env,std::shared_ptr<Data> data,Listener * listener,const SocketAddress & destination)144 BaseObjectPtr<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 
Packet(Environment * env,Listener * listener,Local<Object> object,const SocketAddress & destination,std::shared_ptr<Data> data)159 Packet::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 
Packet(Environment * env,Listener * listener,Local<Object> object,const SocketAddress & destination,size_t length,const char * diagnostic_label)169 Packet::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 
Send(uv_udp_t * handle,BaseObjectPtr<BaseObject> ref)181 int 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 
Done(int status)201 void 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 
ToString() const220 std::string Packet::ToString() const {
221   if (!data_) return "Packet (<empty>)";
222   return "Packet (" + data_->ToString() + ")";
223 }
224 
MemoryInfo(MemoryTracker * tracker) const225 void Packet::MemoryInfo(MemoryTracker* tracker) const {
226   tracker->TrackField("destination", destination_);
227   tracker->TrackField("data", data_);
228   tracker->TrackField("handle", handle_);
229 }
230 
CreateRetryPacket(Environment * env,Listener * listener,const PathDescriptor & path_descriptor,const TokenSecret & token_secret)231 BaseObjectPtr<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 
CreateConnectionClosePacket(Environment * env,Listener * listener,const SocketAddress & destination,ngtcp2_conn * conn,const QuicError & error)269 BaseObjectPtr<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 
CreateImmediateConnectionClosePacket(Environment * env,Listener * listener,const SocketAddress & destination,const PathDescriptor & path_descriptor,const QuicError & reason)286 BaseObjectPtr<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 
CreateStatelessResetPacket(Environment * env,Listener * listener,const PathDescriptor & path_descriptor,const TokenSecret & token_secret,size_t source_len)314 BaseObjectPtr<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 
CreateVersionNegotiationPacket(Environment * env,Listener * listener,const PathDescriptor & path_descriptor)350 BaseObjectPtr<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