• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2021 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "shrpx_quic_connection_handler.h"
26 
27 #include "ssl_compat.h"
28 
29 #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL
30 #  include <wolfssl/options.h>
31 #  include <wolfssl/openssl/rand.h>
32 #else // !NGHTTP2_OPENSSL_IS_WOLFSSL
33 #  include <openssl/rand.h>
34 #endif // !NGHTTP2_OPENSSL_IS_WOLFSSL
35 
36 #include <ngtcp2/ngtcp2.h>
37 #include <ngtcp2/ngtcp2_crypto.h>
38 
39 #include "shrpx_worker.h"
40 #include "shrpx_client_handler.h"
41 #include "shrpx_log.h"
42 #include "shrpx_http3_upstream.h"
43 #include "shrpx_connection_handler.h"
44 
45 namespace shrpx {
46 
47 namespace {
stateless_reset_bucket_regen_timercb(struct ev_loop * loop,ev_timer * w,int revents)48 void stateless_reset_bucket_regen_timercb(struct ev_loop *loop, ev_timer *w,
49                                           int revents) {
50   auto quic_conn_handler = static_cast<QUICConnectionHandler *>(w->data);
51 
52   quic_conn_handler->on_stateless_reset_bucket_regen();
53 }
54 } // namespace
55 
QUICConnectionHandler(Worker * worker)56 QUICConnectionHandler::QUICConnectionHandler(Worker *worker)
57   : worker_{worker}, stateless_reset_bucket_{SHRPX_QUIC_STATELESS_RESET_BURST} {
58   ev_timer_init(&stateless_reset_bucket_regen_timer_,
59                 stateless_reset_bucket_regen_timercb, 0., 1.);
60   stateless_reset_bucket_regen_timer_.data = this;
61 }
62 
~QUICConnectionHandler()63 QUICConnectionHandler::~QUICConnectionHandler() {
64   ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
65 }
66 
handle_packet(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_info & pi,std::span<const uint8_t> data)67 int QUICConnectionHandler::handle_packet(const UpstreamAddr *faddr,
68                                          const Address &remote_addr,
69                                          const Address &local_addr,
70                                          const ngtcp2_pkt_info &pi,
71                                          std::span<const uint8_t> data) {
72   int rv;
73   ngtcp2_version_cid vc;
74 
75   rv = ngtcp2_pkt_decode_version_cid(&vc, data.data(), data.size(),
76                                      SHRPX_QUIC_SCIDLEN);
77   switch (rv) {
78   case 0:
79     break;
80   case NGTCP2_ERR_VERSION_NEGOTIATION:
81     send_version_negotiation(faddr, vc.version, {vc.dcid, vc.dcidlen},
82                              {vc.scid, vc.scidlen}, remote_addr, local_addr);
83 
84     return 0;
85   default:
86     return 0;
87   }
88 
89   auto config = get_config();
90 
91   ngtcp2_cid dcid_key;
92   ngtcp2_cid_init(&dcid_key, vc.dcid, vc.dcidlen);
93 
94   auto conn_handler = worker_->get_connection_handler();
95 
96   ClientHandler *handler;
97 
98   auto &quicconf = config->quic;
99 
100   auto it = connections_.find(dcid_key);
101   if (it == std::end(connections_)) {
102     auto cwit = close_waits_.find(dcid_key);
103     if (cwit != std::end(close_waits_)) {
104       auto cw = (*cwit).second;
105 
106       cw->handle_packet(faddr, remote_addr, local_addr, pi, data);
107 
108       return 0;
109     }
110 
111     if (data[0] & 0x80) {
112       if (generate_quic_hashed_connection_id(dcid_key, remote_addr, local_addr,
113                                              dcid_key) != 0) {
114         return 0;
115       }
116 
117       it = connections_.find(dcid_key);
118       if (it == std::end(connections_)) {
119         auto cwit = close_waits_.find(dcid_key);
120         if (cwit != std::end(close_waits_)) {
121           auto cw = (*cwit).second;
122 
123           cw->handle_packet(faddr, remote_addr, local_addr, pi, data);
124 
125           return 0;
126         }
127       }
128     }
129   }
130 
131   if (it == std::end(connections_)) {
132     ConnectionID decrypted_dcid;
133 
134     auto &qkms = conn_handler->get_quic_keying_materials();
135     const QUICKeyingMaterial *qkm = nullptr;
136 
137     if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) {
138       qkm = select_quic_keying_material(
139         *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK);
140 
141       if (decrypt_quic_connection_id(decrypted_dcid,
142                                      vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
143                                      qkm->cid_decryption_ctx) != 0) {
144         return 0;
145       }
146 
147       if (qkm != &qkms->keying_materials.front() ||
148           decrypted_dcid.worker != worker_->get_worker_id()) {
149         auto quic_lwp =
150           conn_handler->match_quic_lingering_worker_process_worker_id(
151             decrypted_dcid.worker);
152         if (quic_lwp) {
153           if (conn_handler->forward_quic_packet_to_lingering_worker_process(
154                 quic_lwp, remote_addr, local_addr, pi, data) == 0) {
155             return 0;
156           }
157 
158           return 0;
159         }
160       }
161     }
162 
163     // new connection
164 
165     auto &upstreamconf = config->conn.upstream;
166     if (worker_->get_worker_stat()->num_connections >=
167         upstreamconf.worker_connections) {
168       if (LOG_ENABLED(INFO)) {
169         LOG(INFO) << "Too many connections >="
170                   << upstreamconf.worker_connections;
171       }
172 
173       return 0;
174     }
175 
176     ngtcp2_pkt_hd hd;
177     ngtcp2_cid odcid, *podcid = nullptr;
178     std::span<const uint8_t> token;
179     ngtcp2_token_type token_type = NGTCP2_TOKEN_TYPE_UNKNOWN;
180 
181     switch (ngtcp2_accept(&hd, data.data(), data.size())) {
182     case 0: {
183       // If we get Initial and it has the Worker ID of this worker, it
184       // is likely that client is intentionally use the prefix.  Just
185       // drop it.
186       if (vc.dcidlen == SHRPX_QUIC_SCIDLEN) {
187         if (qkm != &qkms->keying_materials.front()) {
188           qkm = &qkms->keying_materials.front();
189 
190           if (decrypt_quic_connection_id(
191                 decrypted_dcid, vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
192                 qkm->cid_decryption_ctx) != 0) {
193             return 0;
194           }
195         }
196 
197         if (decrypted_dcid.worker == worker_->get_worker_id()) {
198           return 0;
199         }
200       }
201 
202       if (worker_->get_graceful_shutdown()) {
203         send_connection_close(faddr, hd.version, hd.dcid, hd.scid, remote_addr,
204                               local_addr, NGTCP2_CONNECTION_REFUSED,
205                               data.size() * 3);
206         return 0;
207       }
208 
209       if (hd.tokenlen == 0) {
210         if (quicconf.upstream.require_token) {
211           send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
212                      {vc.scid, vc.scidlen}, remote_addr, local_addr,
213                      data.size() * 3);
214 
215           return 0;
216         }
217 
218         break;
219       }
220 
221       switch (hd.token[0]) {
222       case NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY: {
223         if (vc.dcidlen != SHRPX_QUIC_SCIDLEN) {
224           // Initial packets with Retry token must have DCID chosen by
225           // server.
226           return 0;
227         }
228 
229         auto qkm = select_quic_keying_material(
230           *qkms.get(), vc.dcid[0] & SHRPX_QUIC_DCID_KM_ID_MASK);
231 
232         if (verify_retry_token(odcid, {hd.token, hd.tokenlen}, hd.version,
233                                hd.dcid, &remote_addr.su.sa, remote_addr.len,
234                                qkm->secret) != 0) {
235           if (LOG_ENABLED(INFO)) {
236             LOG(INFO) << "Failed to validate Retry token from remote="
237                       << util::to_numeric_addr(&remote_addr);
238           }
239 
240           // 2nd Retry packet is not allowed, so send CONNECTION_CLOSE
241           // with INVALID_TOKEN.
242           send_connection_close(faddr, hd.version, hd.dcid, hd.scid,
243                                 remote_addr, local_addr, NGTCP2_INVALID_TOKEN,
244                                 data.size() * 3);
245           return 0;
246         }
247 
248         if (LOG_ENABLED(INFO)) {
249           LOG(INFO) << "Successfully validated Retry token from remote="
250                     << util::to_numeric_addr(&remote_addr);
251         }
252 
253         podcid = &odcid;
254         token = {hd.token, hd.tokenlen};
255         token_type = NGTCP2_TOKEN_TYPE_RETRY;
256 
257         break;
258       }
259       case NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR: {
260         // If a token is a regular token, it must be at least
261         // NGTCP2_MIN_INITIAL_DCIDLEN bytes long.
262         if (vc.dcidlen < NGTCP2_MIN_INITIAL_DCIDLEN) {
263           return 0;
264         }
265 
266         if (hd.tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN + 1) {
267           if (LOG_ENABLED(INFO)) {
268             LOG(INFO) << "Failed to validate token from remote="
269                       << util::to_numeric_addr(&remote_addr);
270           }
271 
272           if (quicconf.upstream.require_token) {
273             send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
274                        {vc.scid, vc.scidlen}, remote_addr, local_addr,
275                        data.size() * 3);
276 
277             return 0;
278           }
279 
280           break;
281         }
282 
283         auto qkm = select_quic_keying_material(
284           *qkms.get(), hd.token[NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN]);
285 
286         if (verify_token({hd.token, hd.tokenlen}, &remote_addr.su.sa,
287                          remote_addr.len, qkm->secret) != 0) {
288           if (LOG_ENABLED(INFO)) {
289             LOG(INFO) << "Failed to validate token from remote="
290                       << util::to_numeric_addr(&remote_addr);
291           }
292 
293           if (quicconf.upstream.require_token) {
294             send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
295                        {vc.scid, vc.scidlen}, remote_addr, local_addr,
296                        data.size() * 3);
297 
298             return 0;
299           }
300 
301           break;
302         }
303 
304         if (LOG_ENABLED(INFO)) {
305           LOG(INFO) << "Successfully validated token from remote="
306                     << util::to_numeric_addr(&remote_addr);
307         }
308 
309         token = {hd.token, hd.tokenlen};
310         token_type = NGTCP2_TOKEN_TYPE_NEW_TOKEN;
311 
312         break;
313       }
314       default:
315         if (quicconf.upstream.require_token) {
316           send_retry(faddr, vc.version, {vc.dcid, vc.dcidlen},
317                      {vc.scid, vc.scidlen}, remote_addr, local_addr,
318                      data.size() * 3);
319 
320           return 0;
321         }
322 
323         break;
324       }
325 
326       break;
327     }
328     default:
329       if (!(data[0] & 0x80) && vc.dcidlen == SHRPX_QUIC_SCIDLEN &&
330           decrypted_dcid.worker != worker_->get_worker_id()) {
331         if (!config->single_thread && conn_handler->forward_quic_packet(
332                                         faddr, remote_addr, local_addr, pi,
333                                         decrypted_dcid.worker, data) == 0) {
334           return 0;
335         }
336 
337         if (data.size() >= SHRPX_QUIC_SCIDLEN + 22) {
338           send_stateless_reset(faddr, data.size(), {vc.dcid, vc.dcidlen},
339                                remote_addr, local_addr);
340         }
341       }
342 
343       return 0;
344     }
345 
346     handler = handle_new_connection(faddr, remote_addr, local_addr, hd, podcid,
347                                     token, token_type);
348     if (handler == nullptr) {
349       return 0;
350     }
351   } else {
352     handler = (*it).second;
353   }
354 
355   if (handler->read_quic(faddr, remote_addr, local_addr, pi, data) != 0) {
356     delete handler;
357     return 0;
358   }
359 
360   handler->signal_write();
361 
362   return 0;
363 }
364 
handle_new_connection(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_hd & hd,const ngtcp2_cid * odcid,std::span<const uint8_t> token,ngtcp2_token_type token_type)365 ClientHandler *QUICConnectionHandler::handle_new_connection(
366   const UpstreamAddr *faddr, const Address &remote_addr,
367   const Address &local_addr, const ngtcp2_pkt_hd &hd, const ngtcp2_cid *odcid,
368   std::span<const uint8_t> token, ngtcp2_token_type token_type) {
369   std::array<char, NI_MAXHOST> host;
370   std::array<char, NI_MAXSERV> service;
371   int rv;
372 
373   rv = getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(),
374                    host.size(), service.data(), service.size(),
375                    NI_NUMERICHOST | NI_NUMERICSERV);
376   if (rv != 0) {
377     LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv);
378 
379     return nullptr;
380   }
381 
382   auto ssl_ctx = worker_->get_quic_sv_ssl_ctx();
383 
384   assert(ssl_ctx);
385 
386   auto ssl = tls::create_ssl(ssl_ctx);
387   if (ssl == nullptr) {
388     return nullptr;
389   }
390 
391 #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL)
392   assert(SSL_is_quic(ssl));
393 #endif // NGHTTP2_GENUINE_OPENSSL || NGHTTP2_OPENSSL_IS_WOLFSSL
394 
395   SSL_set_accept_state(ssl);
396 
397   auto config = get_config();
398   auto &quicconf = config->quic;
399 
400   if (quicconf.upstream.early_data) {
401 #if defined(NGHTTP2_GENUINE_OPENSSL) ||                                        \
402   (defined(NGHTTP2_OPENSSL_IS_WOLFSSL) && defined(WOLFSSL_EARLY_DATA))
403     SSL_set_quic_early_data_enabled(ssl, 1);
404 #elif defined(NGHTTP2_OPENSSL_IS_BORINGSSL)
405     SSL_set_early_data_enabled(ssl, 1);
406 #endif // NGHTTP2_OPENSSL_IS_BORINGSSL
407   }
408 
409   // Disable TLS session ticket if we don't have working ticket
410   // keys.
411   if (!worker_->get_ticket_keys()) {
412     SSL_set_options(ssl, SSL_OP_NO_TICKET);
413   }
414 
415   auto handler = std::make_unique<ClientHandler>(
416     worker_, faddr->fd, ssl, StringRef{host.data()}, StringRef{service.data()},
417     remote_addr.su.sa.sa_family, faddr);
418 
419   auto upstream = std::make_unique<Http3Upstream>(handler.get());
420   if (upstream->init(faddr, remote_addr, local_addr, hd, odcid, token,
421                      token_type) != 0) {
422     return nullptr;
423   }
424 
425   handler->setup_http3_upstream(std::move(upstream));
426 
427   return handler.release();
428 }
429 
430 namespace {
generate_reserved_version(const Address & addr,uint32_t version)431 uint32_t generate_reserved_version(const Address &addr, uint32_t version) {
432   uint32_t h = 0x811C9DC5u;
433   const uint8_t *p = reinterpret_cast<const uint8_t *>(&addr.su.sa);
434   const uint8_t *ep = p + addr.len;
435 
436   for (; p != ep; ++p) {
437     h ^= *p;
438     h *= 0x01000193u;
439   }
440 
441   version = htonl(version);
442   p = (const uint8_t *)&version;
443   ep = p + sizeof(version);
444 
445   for (; p != ep; ++p) {
446     h ^= *p;
447     h *= 0x01000193u;
448   }
449 
450   h &= 0xf0f0f0f0u;
451   h |= 0x0a0a0a0au;
452 
453   return h;
454 }
455 } // namespace
456 
send_retry(const UpstreamAddr * faddr,uint32_t version,std::span<const uint8_t> ini_dcid,std::span<const uint8_t> ini_scid,const Address & remote_addr,const Address & local_addr,size_t max_pktlen)457 int QUICConnectionHandler::send_retry(
458   const UpstreamAddr *faddr, uint32_t version,
459   std::span<const uint8_t> ini_dcid, std::span<const uint8_t> ini_scid,
460   const Address &remote_addr, const Address &local_addr, size_t max_pktlen) {
461   std::array<char, NI_MAXHOST> host;
462   std::array<char, NI_MAXSERV> port;
463 
464   if (getnameinfo(&remote_addr.su.sa, remote_addr.len, host.data(), host.size(),
465                   port.data(), port.size(),
466                   NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
467     return -1;
468   }
469 
470   auto config = get_config();
471   auto &quicconf = config->quic;
472 
473   auto conn_handler = worker_->get_connection_handler();
474   auto &qkms = conn_handler->get_quic_keying_materials();
475   auto &qkm = qkms->keying_materials.front();
476 
477   ngtcp2_cid retry_scid;
478 
479   if (generate_quic_retry_connection_id(retry_scid, quicconf.server_id, qkm.id,
480                                         qkm.cid_encryption_ctx) != 0) {
481     return -1;
482   }
483 
484   ngtcp2_cid idcid, iscid;
485   ngtcp2_cid_init(&idcid, ini_dcid.data(), ini_dcid.size());
486   ngtcp2_cid_init(&iscid, ini_scid.data(), ini_scid.size());
487 
488   std::array<uint8_t, NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN> tokenbuf;
489 
490   auto token =
491     generate_retry_token(tokenbuf, version, &remote_addr.su.sa, remote_addr.len,
492                          retry_scid, idcid, qkm.secret);
493   if (!token) {
494     return -1;
495   }
496 
497   std::vector<uint8_t> buf;
498   buf.resize(std::min(max_pktlen, static_cast<size_t>(256)));
499 
500   auto nwrite = ngtcp2_crypto_write_retry(buf.data(), buf.size(), version,
501                                           &iscid, &retry_scid, &idcid,
502                                           token->data(), token->size());
503   if (nwrite < 0) {
504     LOG(ERROR) << "ngtcp2_crypto_write_retry: " << ngtcp2_strerror(nwrite);
505     return -1;
506   }
507 
508   buf.resize(nwrite);
509 
510   quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
511                    &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{}, buf,
512                    buf.size());
513 
514   if (generate_quic_hashed_connection_id(idcid, remote_addr, local_addr,
515                                          idcid) != 0) {
516     return -1;
517   }
518 
519   auto d =
520     static_cast<ev_tstamp>(NGTCP2_DEFAULT_INITIAL_RTT * 3) / NGTCP2_SECONDS;
521 
522   if (LOG_ENABLED(INFO)) {
523     LOG(INFO) << "Enter close-wait period " << d << "s with " << buf.size()
524               << " bytes sentinel packet";
525   }
526 
527   auto cw = std::make_unique<CloseWait>(worker_, std::vector<ngtcp2_cid>{idcid},
528                                         std::move(buf), d);
529 
530   add_close_wait(cw.release());
531 
532   return 0;
533 }
534 
send_version_negotiation(const UpstreamAddr * faddr,uint32_t version,std::span<const uint8_t> ini_dcid,std::span<const uint8_t> ini_scid,const Address & remote_addr,const Address & local_addr)535 int QUICConnectionHandler::send_version_negotiation(
536   const UpstreamAddr *faddr, uint32_t version,
537   std::span<const uint8_t> ini_dcid, std::span<const uint8_t> ini_scid,
538   const Address &remote_addr, const Address &local_addr) {
539   auto sv = std::to_array({
540     generate_reserved_version(remote_addr, version),
541     NGTCP2_PROTO_VER_V1,
542   });
543 
544   std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
545 
546   uint8_t rand_byte;
547   util::random_bytes(&rand_byte, &rand_byte + 1, worker_->get_randgen());
548 
549   auto nwrite = ngtcp2_pkt_write_version_negotiation(
550     buf.data(), buf.size(), rand_byte, ini_scid.data(), ini_scid.size(),
551     ini_dcid.data(), ini_dcid.size(), sv.data(), sv.size());
552   if (nwrite < 0) {
553     LOG(ERROR) << "ngtcp2_pkt_write_version_negotiation: "
554                << ngtcp2_strerror(nwrite);
555     return -1;
556   }
557 
558   auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
559   return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
560                           &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
561                           pkt, pkt.size());
562 }
563 
send_stateless_reset(const UpstreamAddr * faddr,size_t pktlen,std::span<const uint8_t> dcid,const Address & remote_addr,const Address & local_addr)564 int QUICConnectionHandler::send_stateless_reset(const UpstreamAddr *faddr,
565                                                 size_t pktlen,
566                                                 std::span<const uint8_t> dcid,
567                                                 const Address &remote_addr,
568                                                 const Address &local_addr) {
569   if (stateless_reset_bucket_ == 0) {
570     if (LOG_ENABLED(INFO)) {
571       LOG(INFO) << "Stateless Reset bucket has been depleted";
572     }
573 
574     return 0;
575   }
576 
577   --stateless_reset_bucket_;
578 
579   if (!ev_is_active(&stateless_reset_bucket_regen_timer_)) {
580     ev_timer_again(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
581   }
582 
583   std::array<uint8_t, NGTCP2_STATELESS_RESET_TOKENLEN> token;
584   ngtcp2_cid cid;
585 
586   ngtcp2_cid_init(&cid, dcid.data(), dcid.size());
587 
588   auto conn_handler = worker_->get_connection_handler();
589   auto &qkms = conn_handler->get_quic_keying_materials();
590   auto &qkm = qkms->keying_materials.front();
591 
592   if (auto rv = generate_quic_stateless_reset_token(
593         token.data(), cid, qkm.secret.data(), qkm.secret.size());
594       rv != 0) {
595     return -1;
596   }
597 
598   // SCID + minimum expansion - NGTCP2_STATELESS_RESET_TOKENLEN
599   constexpr size_t max_rand_byteslen =
600     NGTCP2_MAX_CIDLEN + 22 - NGTCP2_STATELESS_RESET_TOKENLEN;
601 
602   size_t rand_byteslen;
603 
604   if (pktlen <= 43) {
605     // As per
606     // https://datatracker.ietf.org/doc/html/rfc9000#section-10.3
607     rand_byteslen = pktlen - NGTCP2_STATELESS_RESET_TOKENLEN - 1;
608   } else {
609     rand_byteslen = max_rand_byteslen;
610   }
611 
612   std::array<uint8_t, max_rand_byteslen> rand_bytes;
613 
614   if (RAND_bytes(rand_bytes.data(), rand_byteslen) != 1) {
615     return -1;
616   }
617 
618   std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
619 
620   auto nwrite = ngtcp2_pkt_write_stateless_reset(
621     buf.data(), buf.size(), token.data(), rand_bytes.data(), rand_byteslen);
622   if (nwrite < 0) {
623     LOG(ERROR) << "ngtcp2_pkt_write_stateless_reset: "
624                << ngtcp2_strerror(nwrite);
625     return -1;
626   }
627 
628   if (LOG_ENABLED(INFO)) {
629     LOG(INFO) << "Send stateless_reset to remote="
630               << util::to_numeric_addr(&remote_addr)
631               << " dcid=" << util::format_hex(dcid);
632   }
633 
634   auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
635   return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
636                           &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
637                           pkt, pkt.size());
638 }
639 
send_connection_close(const UpstreamAddr * faddr,uint32_t version,const ngtcp2_cid & ini_dcid,const ngtcp2_cid & ini_scid,const Address & remote_addr,const Address & local_addr,uint64_t error_code,size_t max_pktlen)640 int QUICConnectionHandler::send_connection_close(
641   const UpstreamAddr *faddr, uint32_t version, const ngtcp2_cid &ini_dcid,
642   const ngtcp2_cid &ini_scid, const Address &remote_addr,
643   const Address &local_addr, uint64_t error_code, size_t max_pktlen) {
644   std::array<uint8_t, NGTCP2_MAX_UDP_PAYLOAD_SIZE> buf;
645 
646   max_pktlen = std::min(max_pktlen, buf.size());
647 
648   auto nwrite = ngtcp2_crypto_write_connection_close(
649     buf.data(), max_pktlen, version, &ini_scid, &ini_dcid, error_code, nullptr,
650     0);
651   if (nwrite < 0) {
652     LOG(ERROR) << "ngtcp2_crypto_write_connection_close failed";
653     return -1;
654   }
655 
656   if (LOG_ENABLED(INFO)) {
657     LOG(INFO) << "Send Initial CONNECTION_CLOSE with error_code=" << log::hex
658               << error_code << log::dec
659               << " to remote=" << util::to_numeric_addr(&remote_addr)
660               << " dcid="
661               << util::format_hex(std::span{ini_scid.data, ini_scid.datalen})
662               << " scid="
663               << util::format_hex(std::span{ini_dcid.data, ini_dcid.datalen});
664   }
665 
666   auto pkt = std::span{std::begin(buf), static_cast<size_t>(nwrite)};
667   return quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
668                           &local_addr.su.sa, local_addr.len, ngtcp2_pkt_info{},
669                           pkt, pkt.size());
670 }
671 
add_connection_id(const ngtcp2_cid & cid,ClientHandler * handler)672 void QUICConnectionHandler::add_connection_id(const ngtcp2_cid &cid,
673                                               ClientHandler *handler) {
674   connections_.emplace(cid, handler);
675 }
676 
remove_connection_id(const ngtcp2_cid & cid)677 void QUICConnectionHandler::remove_connection_id(const ngtcp2_cid &cid) {
678   connections_.erase(cid);
679 }
680 
add_close_wait(CloseWait * cw)681 void QUICConnectionHandler::add_close_wait(CloseWait *cw) {
682   for (auto &cid : cw->scids) {
683     close_waits_.emplace(cid, cw);
684   }
685 }
686 
remove_close_wait(const CloseWait * cw)687 void QUICConnectionHandler::remove_close_wait(const CloseWait *cw) {
688   for (auto &cid : cw->scids) {
689     close_waits_.erase(cid);
690   }
691 }
692 
on_stateless_reset_bucket_regen()693 void QUICConnectionHandler::on_stateless_reset_bucket_regen() {
694   assert(stateless_reset_bucket_ < SHRPX_QUIC_STATELESS_RESET_BURST);
695 
696   if (++stateless_reset_bucket_ == SHRPX_QUIC_STATELESS_RESET_BURST) {
697     ev_timer_stop(worker_->get_loop(), &stateless_reset_bucket_regen_timer_);
698   }
699 }
700 
close_wait_timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)701 static void close_wait_timeoutcb(struct ev_loop *loop, ev_timer *w,
702                                  int revents) {
703   auto cw = static_cast<CloseWait *>(w->data);
704 
705   if (LOG_ENABLED(INFO)) {
706     LOG(INFO) << "close-wait period finished";
707   }
708 
709   auto quic_conn_handler = cw->worker->get_quic_connection_handler();
710   quic_conn_handler->remove_close_wait(cw);
711 
712   delete cw;
713 }
714 
CloseWait(Worker * worker,std::vector<ngtcp2_cid> scids,std::vector<uint8_t> pkt,ev_tstamp period)715 CloseWait::CloseWait(Worker *worker, std::vector<ngtcp2_cid> scids,
716                      std::vector<uint8_t> pkt, ev_tstamp period)
717   : worker{worker},
718     scids{std::move(scids)},
719     pkt{std::move(pkt)},
720     bytes_recv{0},
721     bytes_sent{0},
722     num_pkts_recv{0},
723     next_pkts_recv{1} {
724   ++worker->get_worker_stat()->num_close_waits;
725 
726   ev_timer_init(&timer, close_wait_timeoutcb, period, 0.);
727   timer.data = this;
728 
729   ev_timer_start(worker->get_loop(), &timer);
730 }
731 
~CloseWait()732 CloseWait::~CloseWait() {
733   auto loop = worker->get_loop();
734 
735   ev_timer_stop(loop, &timer);
736 
737   auto worker_stat = worker->get_worker_stat();
738   --worker_stat->num_close_waits;
739 
740   if (worker->get_graceful_shutdown() && worker_stat->num_connections == 0 &&
741       worker_stat->num_close_waits == 0) {
742     ev_break(loop);
743   }
744 }
745 
handle_packet(const UpstreamAddr * faddr,const Address & remote_addr,const Address & local_addr,const ngtcp2_pkt_info & pi,std::span<const uint8_t> data)746 int CloseWait::handle_packet(const UpstreamAddr *faddr,
747                              const Address &remote_addr,
748                              const Address &local_addr,
749                              const ngtcp2_pkt_info &pi,
750                              std::span<const uint8_t> data) {
751   if (pkt.empty()) {
752     return 0;
753   }
754 
755   ++num_pkts_recv;
756   bytes_recv += data.size();
757 
758   if (bytes_sent + pkt.size() > 3 * bytes_recv ||
759       next_pkts_recv > num_pkts_recv) {
760     return 0;
761   }
762 
763   auto rv = quic_send_packet(faddr, &remote_addr.su.sa, remote_addr.len,
764                              &local_addr.su.sa, local_addr.len,
765                              ngtcp2_pkt_info{}, pkt, pkt.size());
766   if (rv != 0) {
767     return -1;
768   }
769 
770   next_pkts_recv *= 2;
771   bytes_sent += pkt.size();
772 
773   return 0;
774 }
775 
776 } // namespace shrpx
777