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