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