• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 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_config.h"
26 
27 #ifdef HAVE_PWD_H
28 #  include <pwd.h>
29 #endif // HAVE_PWD_H
30 #ifdef HAVE_NETDB_H
31 #  include <netdb.h>
32 #endif // HAVE_NETDB_H
33 #ifdef HAVE_SYSLOG_H
34 #  include <syslog.h>
35 #endif // HAVE_SYSLOG_H
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #ifdef HAVE_FCNTL_H
39 #  include <fcntl.h>
40 #endif // HAVE_FCNTL_H
41 #ifdef HAVE_UNISTD_H
42 #  include <unistd.h>
43 #endif // HAVE_UNISTD_H
44 #include <dirent.h>
45 
46 #include <cstring>
47 #include <cerrno>
48 #include <limits>
49 #include <fstream>
50 #include <unordered_map>
51 
52 #ifdef NGHTTP2_OPENSSL_IS_WOLFSSL
53 #  include <wolfssl/openssl/evp.h>
54 #else // !NGHTTP2_OPENSSL_IS_WOLFSSL
55 #  include <openssl/evp.h>
56 #endif // !NGHTTP2_OPENSSL_IS_WOLFSSL
57 
58 #include <nghttp2/nghttp2.h>
59 
60 #include "url-parser/url_parser.h"
61 
62 #include "shrpx_log.h"
63 #include "shrpx_tls.h"
64 #include "shrpx_http.h"
65 #ifdef HAVE_MRUBY
66 #  include "shrpx_mruby.h"
67 #endif // HAVE_MRUBY
68 #include "util.h"
69 #include "base64.h"
70 #include "ssl_compat.h"
71 #include "xsi_strerror.h"
72 
73 #ifndef AI_NUMERICSERV
74 #  define AI_NUMERICSERV 0
75 #endif
76 
77 namespace shrpx {
78 
79 namespace {
80 Config *config;
81 } // namespace
82 
83 constexpr auto SHRPX_UNIX_PATH_PREFIX = "unix:"_sr;
84 
get_config()85 const Config *get_config() { return config; }
86 
mod_config()87 Config *mod_config() { return config; }
88 
replace_config(std::unique_ptr<Config> another)89 std::unique_ptr<Config> replace_config(std::unique_ptr<Config> another) {
90   auto p = config;
91   config = another.release();
92   return std::unique_ptr<Config>(p);
93 }
94 
create_config()95 void create_config() { config = new Config(); }
96 
~Config()97 Config::~Config() {
98   auto &upstreamconf = http2.upstream;
99 
100   nghttp2_option_del(upstreamconf.option);
101   nghttp2_option_del(upstreamconf.alt_mode_option);
102   nghttp2_session_callbacks_del(upstreamconf.callbacks);
103 
104   auto &downstreamconf = http2.downstream;
105 
106   nghttp2_option_del(downstreamconf.option);
107   nghttp2_session_callbacks_del(downstreamconf.callbacks);
108 
109   auto &dumpconf = http2.upstream.debug.dump;
110 
111   if (dumpconf.request_header) {
112     fclose(dumpconf.request_header);
113   }
114 
115   if (dumpconf.response_header) {
116     fclose(dumpconf.response_header);
117   }
118 }
119 
~TicketKeys()120 TicketKeys::~TicketKeys() {
121   /* Erase keys from memory */
122   for (auto &key : keys) {
123     memset(&key, 0, sizeof(key));
124   }
125 }
126 
127 namespace {
split_host_port(char * host,size_t hostlen,uint16_t * port_ptr,const StringRef & hostport,const StringRef & opt)128 int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
129                     const StringRef &hostport, const StringRef &opt) {
130   // host and port in |hostport| is separated by single ','.
131   auto sep = std::find(std::begin(hostport), std::end(hostport), ',');
132   if (sep == std::end(hostport)) {
133     LOG(ERROR) << opt << ": Invalid host, port: " << hostport;
134     return -1;
135   }
136   size_t len = sep - std::begin(hostport);
137   if (hostlen < len + 1) {
138     LOG(ERROR) << opt << ": Hostname too long: " << hostport;
139     return -1;
140   }
141   std::copy(std::begin(hostport), sep, host);
142   host[len] = '\0';
143 
144   auto portstr = StringRef{sep + 1, std::end(hostport)};
145   auto d = util::parse_uint(portstr);
146   if (d && 1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
147     *port_ptr = *d;
148     return 0;
149   }
150 
151   LOG(ERROR) << opt << ": Port is invalid: " << portstr;
152   return -1;
153 }
154 } // namespace
155 
156 namespace {
is_secure(const StringRef & filename)157 bool is_secure(const StringRef &filename) {
158   struct stat buf;
159   int rv = stat(filename.data(), &buf);
160   if (rv == 0) {
161     if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
162         !(buf.st_mode & S_IRWXO)) {
163       return true;
164     }
165   }
166 
167   return false;
168 }
169 } // namespace
170 
171 std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<StringRef> & files,const EVP_CIPHER * cipher,const EVP_MD * hmac)172 read_tls_ticket_key_file(const std::vector<StringRef> &files,
173                          const EVP_CIPHER *cipher, const EVP_MD *hmac) {
174   auto ticket_keys = std::make_unique<TicketKeys>();
175   auto &keys = ticket_keys->keys;
176   keys.resize(files.size());
177   auto enc_keylen = EVP_CIPHER_key_length(cipher);
178   auto hmac_keylen = EVP_MD_size(hmac);
179   if (cipher == EVP_aes_128_cbc()) {
180     // backward compatibility, as a legacy of using same file format
181     // with nginx and apache.
182     hmac_keylen = 16;
183   }
184   auto expectedlen = keys[0].data.name.size() + enc_keylen + hmac_keylen;
185   char buf[256];
186   assert(sizeof(buf) >= expectedlen);
187 
188   size_t i = 0;
189   for (auto &file : files) {
190     struct stat fst {};
191 
192     if (stat(file.data(), &fst) == -1) {
193       auto error = errno;
194       LOG(ERROR) << "tls-ticket-key-file: could not stat file " << file
195                  << ", errno=" << error;
196       return nullptr;
197     }
198 
199     if (static_cast<size_t>(fst.st_size) != expectedlen) {
200       LOG(ERROR) << "tls-ticket-key-file: the expected file size is "
201                  << expectedlen << ", the actual file size is " << fst.st_size;
202       return nullptr;
203     }
204 
205     std::ifstream f(file.data());
206     if (!f) {
207       LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
208       return nullptr;
209     }
210 
211     f.read(buf, expectedlen);
212     if (static_cast<size_t>(f.gcount()) != expectedlen) {
213       LOG(ERROR) << "tls-ticket-key-file: want to read " << expectedlen
214                  << " bytes but only read " << f.gcount() << " bytes from "
215                  << file;
216       return nullptr;
217     }
218 
219     auto &key = keys[i++];
220     key.cipher = cipher;
221     key.hmac = hmac;
222     key.hmac_keylen = hmac_keylen;
223 
224     if (LOG_ENABLED(INFO)) {
225       LOG(INFO) << "enc_keylen=" << enc_keylen
226                 << ", hmac_keylen=" << key.hmac_keylen;
227     }
228 
229     auto p = buf;
230     std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
231     p += key.data.name.size();
232     std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
233     p += enc_keylen;
234     std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
235 
236     if (LOG_ENABLED(INFO)) {
237       LOG(INFO) << "session ticket key: " << util::format_hex(key.data.name);
238     }
239   }
240   return ticket_keys;
241 }
242 
243 #ifdef ENABLE_HTTP3
244 std::shared_ptr<QUICKeyingMaterials>
read_quic_secret_file(const StringRef & path)245 read_quic_secret_file(const StringRef &path) {
246   constexpr size_t expectedlen =
247     SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
248 
249   auto qkms = std::make_shared<QUICKeyingMaterials>();
250   auto &kms = qkms->keying_materials;
251 
252   std::ifstream f(path.data());
253   if (!f) {
254     LOG(ERROR) << "frontend-quic-secret-file: could not open file " << path;
255     return nullptr;
256   }
257 
258   std::array<char, 4096> buf;
259 
260   while (f.getline(buf.data(), buf.size())) {
261     auto len = strlen(buf.data());
262     if (len == 0 || buf[0] == '#') {
263       continue;
264     }
265 
266     auto s = StringRef{std::begin(buf), std::begin(buf) + len};
267     if (s.size() != expectedlen * 2 || !util::is_hex_string(s)) {
268       LOG(ERROR) << "frontend-quic-secret-file: each line must be a "
269                  << expectedlen * 2 << " bytes hex encoded string";
270       return nullptr;
271     }
272 
273     kms.emplace_back();
274     auto &qkm = kms.back();
275 
276     auto p = std::begin(s);
277 
278     util::decode_hex(std::begin(qkm.reserved),
279                      StringRef{p, p + qkm.reserved.size()});
280     p += qkm.reserved.size() * 2;
281     util::decode_hex(std::begin(qkm.secret),
282                      StringRef{p, p + qkm.secret.size()});
283     p += qkm.secret.size() * 2;
284     util::decode_hex(std::begin(qkm.salt), StringRef{p, p + qkm.salt.size()});
285     p += qkm.salt.size() * 2;
286 
287     assert(static_cast<size_t>(p - std::begin(s)) == expectedlen * 2);
288 
289     qkm.id = qkm.reserved[0] & SHRPX_QUIC_DCID_KM_ID_MASK;
290 
291     if (kms.size() == 8) {
292       break;
293     }
294   }
295 
296   if (f.bad() || (!f.eof() && f.fail())) {
297     LOG(ERROR)
298       << "frontend-quic-secret-file: error occurred while reading file "
299       << path;
300     return nullptr;
301   }
302 
303   if (kms.empty()) {
304     LOG(WARN)
305       << "frontend-quic-secret-file: no keying materials are present in file "
306       << path;
307     return nullptr;
308   }
309 
310   return qkms;
311 }
312 #endif // ENABLE_HTTP3
313 
open_file_for_write(const char * filename)314 FILE *open_file_for_write(const char *filename) {
315   std::array<char, STRERROR_BUFSIZE> errbuf;
316 
317 #ifdef O_CLOEXEC
318   auto fd =
319     open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
320 #else
321   auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
322 
323   // We get race condition if execve is called at the same time.
324   if (fd != -1) {
325     util::make_socket_closeonexec(fd);
326   }
327 #endif
328   if (fd == -1) {
329     auto error = errno;
330     LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
331                << xsi_strerror(error, errbuf.data(), errbuf.size());
332     return nullptr;
333   }
334   auto f = fdopen(fd, "wb");
335   if (f == nullptr) {
336     auto error = errno;
337     LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
338                << xsi_strerror(error, errbuf.data(), errbuf.size());
339     return nullptr;
340   }
341 
342   return f;
343 }
344 
345 namespace {
346 // Read passwd from |filename|
read_passwd_from_file(const StringRef & opt,const StringRef & filename)347 std::string read_passwd_from_file(const StringRef &opt,
348                                   const StringRef &filename) {
349   std::string line;
350 
351   if (!is_secure(filename)) {
352     LOG(ERROR) << opt << ": Private key passwd file " << filename
353                << " has insecure mode.";
354     return line;
355   }
356 
357   std::ifstream in(filename.data(), std::ios::binary);
358   if (!in) {
359     LOG(ERROR) << opt << ": Could not open key passwd file " << filename;
360     return line;
361   }
362 
363   std::getline(in, line);
364   return line;
365 }
366 } // namespace
367 
parse_header(BlockAllocator & balloc,const StringRef & optarg)368 HeaderRefs::value_type parse_header(BlockAllocator &balloc,
369                                     const StringRef &optarg) {
370   auto colon = std::find(std::begin(optarg), std::end(optarg), ':');
371 
372   if (colon == std::end(optarg) || colon == std::begin(optarg)) {
373     return {};
374   }
375 
376   auto value = colon + 1;
377   for (; *value == '\t' || *value == ' '; ++value)
378     ;
379 
380   auto name_iov =
381     make_byte_ref(balloc, std::distance(std::begin(optarg), colon) + 1);
382   auto p = std::copy(std::begin(optarg), colon, std::begin(name_iov));
383   util::inp_strlower(std::begin(name_iov), p);
384   *p = '\0';
385 
386   auto nv =
387     HeaderRef(StringRef{std::span{std::begin(name_iov), p}},
388               make_string_ref(balloc, StringRef{value, std::end(optarg)}));
389 
390   if (!nghttp2_check_header_name(nv.name.byte(), nv.name.size()) ||
391       !nghttp2_check_header_value_rfc9113(nv.value.byte(), nv.value.size())) {
392     return {};
393   }
394 
395   return nv;
396 }
397 
398 template <typename T>
parse_uint(T * dest,const StringRef & opt,const StringRef & optarg)399 int parse_uint(T *dest, const StringRef &opt, const StringRef &optarg) {
400   auto val = util::parse_uint(optarg);
401   if (!val) {
402     LOG(ERROR) << opt << ": bad value.  Specify an integer >= 0.";
403     return -1;
404   }
405 
406   *dest = *val;
407 
408   return 0;
409 }
410 
411 namespace {
412 template <typename T>
parse_uint_with_unit(T * dest,const StringRef & opt,const StringRef & optarg)413 int parse_uint_with_unit(T *dest, const StringRef &opt,
414                          const StringRef &optarg) {
415   auto n = util::parse_uint_with_unit(optarg);
416   if (!n) {
417     LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
418     return -1;
419   }
420 
421   if (static_cast<uint64_t>(std::numeric_limits<T>::max()) <
422       static_cast<uint64_t>(*n)) {
423     LOG(ERROR) << opt
424                << ": too large.  The value should be less than or equal to "
425                << std::numeric_limits<T>::max();
426     return -1;
427   }
428 
429   *dest = *n;
430 
431   return 0;
432 }
433 } // namespace
434 
435 namespace {
parse_altsvc(AltSvc & altsvc,const StringRef & opt,const StringRef & optarg)436 int parse_altsvc(AltSvc &altsvc, const StringRef &opt,
437                  const StringRef &optarg) {
438   // PROTOID, PORT, HOST, ORIGIN, PARAMS.
439   auto tokens = util::split_str(optarg, ',', 5);
440 
441   if (tokens.size() < 2) {
442     // Requires at least protocol_id and port
443     LOG(ERROR) << opt << ": too few parameters: " << optarg;
444     return -1;
445   }
446 
447   int port;
448 
449   if (parse_uint(&port, opt, tokens[1]) != 0) {
450     return -1;
451   }
452 
453   if (port < 1 ||
454       port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
455     LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
456     return -1;
457   }
458 
459   altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
460 
461   altsvc.port = port;
462   altsvc.service = make_string_ref(config->balloc, tokens[1]);
463 
464   if (tokens.size() > 2) {
465     if (!tokens[2].empty()) {
466       altsvc.host = make_string_ref(config->balloc, tokens[2]);
467     }
468 
469     if (tokens.size() > 3) {
470       if (!tokens[3].empty()) {
471         altsvc.origin = make_string_ref(config->balloc, tokens[3]);
472       }
473 
474       if (tokens.size() > 4) {
475         if (!tokens[4].empty()) {
476           altsvc.params = make_string_ref(config->balloc, tokens[4]);
477         }
478       }
479     }
480   }
481 
482   return 0;
483 }
484 } // namespace
485 
486 namespace {
487 // generated by gennghttpxfun.py
log_var_lookup_token(const StringRef & name)488 LogFragmentType log_var_lookup_token(const StringRef &name) {
489   switch (name.size()) {
490   case 3:
491     switch (name[2]) {
492     case 'd':
493       if (util::strieq("pi"_sr, name, 2)) {
494         return LogFragmentType::PID;
495       }
496       break;
497     }
498     break;
499   case 4:
500     switch (name[3]) {
501     case 'h':
502       if (util::strieq("pat"_sr, name, 3)) {
503         return LogFragmentType::PATH;
504       }
505       break;
506     case 'n':
507       if (util::strieq("alp"_sr, name, 3)) {
508         return LogFragmentType::ALPN;
509       }
510       break;
511     }
512     break;
513   case 6:
514     switch (name[5]) {
515     case 'd':
516       if (util::strieq("metho"_sr, name, 5)) {
517         return LogFragmentType::METHOD;
518       }
519       break;
520     case 's':
521       if (util::strieq("statu"_sr, name, 5)) {
522         return LogFragmentType::STATUS;
523       }
524       break;
525     }
526     break;
527   case 7:
528     switch (name[6]) {
529     case 'i':
530       if (util::strieq("tls_sn"_sr, name, 6)) {
531         return LogFragmentType::TLS_SNI;
532       }
533       break;
534     case 't':
535       if (util::strieq("reques"_sr, name, 6)) {
536         return LogFragmentType::REQUEST;
537       }
538       break;
539     }
540     break;
541   case 10:
542     switch (name[9]) {
543     case 'l':
544       if (util::strieq("time_loca"_sr, name, 9)) {
545         return LogFragmentType::TIME_LOCAL;
546       }
547       break;
548     case 'r':
549       if (util::strieq("ssl_ciphe"_sr, name, 9)) {
550         return LogFragmentType::SSL_CIPHER;
551       }
552       if (util::strieq("tls_ciphe"_sr, name, 9)) {
553         return LogFragmentType::TLS_CIPHER;
554       }
555       break;
556     }
557     break;
558   case 11:
559     switch (name[10]) {
560     case 'r':
561       if (util::strieq("remote_add"_sr, name, 10)) {
562         return LogFragmentType::REMOTE_ADDR;
563       }
564       break;
565     case 't':
566       if (util::strieq("remote_por"_sr, name, 10)) {
567         return LogFragmentType::REMOTE_PORT;
568       }
569       if (util::strieq("server_por"_sr, name, 10)) {
570         return LogFragmentType::SERVER_PORT;
571       }
572       break;
573     }
574     break;
575   case 12:
576     switch (name[11]) {
577     case '1':
578       if (util::strieq("time_iso860"_sr, name, 11)) {
579         return LogFragmentType::TIME_ISO8601;
580       }
581       break;
582     case 'e':
583       if (util::strieq("request_tim"_sr, name, 11)) {
584         return LogFragmentType::REQUEST_TIME;
585       }
586       break;
587     case 'l':
588       if (util::strieq("ssl_protoco"_sr, name, 11)) {
589         return LogFragmentType::SSL_PROTOCOL;
590       }
591       if (util::strieq("tls_protoco"_sr, name, 11)) {
592         return LogFragmentType::TLS_PROTOCOL;
593       }
594       break;
595     case 't':
596       if (util::strieq("backend_hos"_sr, name, 11)) {
597         return LogFragmentType::BACKEND_HOST;
598       }
599       if (util::strieq("backend_por"_sr, name, 11)) {
600         return LogFragmentType::BACKEND_PORT;
601       }
602       break;
603     }
604     break;
605   case 14:
606     switch (name[13]) {
607     case 'd':
608       if (util::strieq("ssl_session_i"_sr, name, 13)) {
609         return LogFragmentType::SSL_SESSION_ID;
610       }
611       if (util::strieq("tls_session_i"_sr, name, 13)) {
612         return LogFragmentType::TLS_SESSION_ID;
613       }
614       break;
615     }
616     break;
617   case 15:
618     switch (name[14]) {
619     case 't':
620       if (util::strieq("body_bytes_sen"_sr, name, 14)) {
621         return LogFragmentType::BODY_BYTES_SENT;
622       }
623       break;
624     }
625     break;
626   case 16:
627     switch (name[15]) {
628     case 'n':
629       if (util::strieq("protocol_versio"_sr, name, 15)) {
630         return LogFragmentType::PROTOCOL_VERSION;
631       }
632       break;
633     }
634     break;
635   case 17:
636     switch (name[16]) {
637     case 'l':
638       if (util::strieq("tls_client_seria"_sr, name, 16)) {
639         return LogFragmentType::TLS_CLIENT_SERIAL;
640       }
641       break;
642     }
643     break;
644   case 18:
645     switch (name[17]) {
646     case 'd':
647       if (util::strieq("ssl_session_reuse"_sr, name, 17)) {
648         return LogFragmentType::SSL_SESSION_REUSED;
649       }
650       if (util::strieq("tls_session_reuse"_sr, name, 17)) {
651         return LogFragmentType::TLS_SESSION_REUSED;
652       }
653       break;
654     case 'y':
655       if (util::strieq("path_without_quer"_sr, name, 17)) {
656         return LogFragmentType::PATH_WITHOUT_QUERY;
657       }
658       break;
659     }
660     break;
661   case 22:
662     switch (name[21]) {
663     case 'e':
664       if (util::strieq("tls_client_issuer_nam"_sr, name, 21)) {
665         return LogFragmentType::TLS_CLIENT_ISSUER_NAME;
666       }
667       break;
668     }
669     break;
670   case 23:
671     switch (name[22]) {
672     case 'e':
673       if (util::strieq("tls_client_subject_nam"_sr, name, 22)) {
674         return LogFragmentType::TLS_CLIENT_SUBJECT_NAME;
675       }
676       break;
677     }
678     break;
679   case 27:
680     switch (name[26]) {
681     case '1':
682       if (util::strieq("tls_client_fingerprint_sha"_sr, name, 26)) {
683         return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1;
684       }
685       break;
686     }
687     break;
688   case 29:
689     switch (name[28]) {
690     case '6':
691       if (util::strieq("tls_client_fingerprint_sha25"_sr, name, 28)) {
692         return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256;
693       }
694       break;
695     }
696     break;
697   }
698   return LogFragmentType::NONE;
699 }
700 } // namespace
701 
702 namespace {
var_token(char c)703 bool var_token(char c) {
704   return util::is_alpha(c) || util::is_digit(c) || c == '_';
705 }
706 } // namespace
707 
parse_log_format(BlockAllocator & balloc,const StringRef & optarg)708 std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
709                                           const StringRef &optarg) {
710   auto literal_start = std::begin(optarg);
711   auto p = literal_start;
712   auto eop = std::end(optarg);
713 
714   auto res = std::vector<LogFragment>();
715 
716   for (; p != eop;) {
717     if (*p != '$') {
718       ++p;
719       continue;
720     }
721 
722     auto var_start = p;
723 
724     ++p;
725 
726     StringRef var_name;
727     if (p != eop && *p == '{') {
728       auto var_name_start = ++p;
729       for (; p != eop && var_token(*p); ++p)
730         ;
731 
732       if (p == eop || *p != '}') {
733         LOG(WARN) << "Missing '}' after " << StringRef{var_start, p};
734         continue;
735       }
736 
737       var_name = StringRef{var_name_start, p};
738       ++p;
739     } else {
740       auto var_name_start = p;
741       for (; p != eop && var_token(*p); ++p)
742         ;
743 
744       var_name = StringRef{var_name_start, p};
745     }
746 
747     auto value = std::begin(var_name);
748 
749     auto type = log_var_lookup_token(var_name);
750 
751     if (type == LogFragmentType::NONE) {
752       if (util::istarts_with(var_name, "http_"_sr)) {
753         if ("host"_sr == var_name.substr(str_size("http_"))) {
754           // Special handling of host header field.  We will use
755           // :authority header field if host header is missing.  This
756           // is a typical case in HTTP/2.
757           type = LogFragmentType::AUTHORITY;
758         } else {
759           type = LogFragmentType::HTTP;
760           value += str_size("http_");
761         }
762       } else {
763         LOG(WARN) << "Unrecognized log format variable: " << var_name;
764         continue;
765       }
766     }
767 
768     if (literal_start < var_start) {
769       res.emplace_back(
770         LogFragmentType::LITERAL,
771         make_string_ref(balloc, StringRef{literal_start, var_start}));
772     }
773 
774     literal_start = p;
775 
776     if (value == std::begin(var_name)) {
777       res.emplace_back(type);
778       continue;
779     }
780 
781     {
782       auto iov =
783         make_byte_ref(balloc, std::distance(value, std::end(var_name)) + 1);
784       auto p = std::copy(value, std::end(var_name), std::begin(iov));
785       std::transform(std::begin(iov), p, std::begin(iov),
786                      [](auto c) { return c == '_' ? '-' : c; });
787       *p = '\0';
788       res.emplace_back(type, StringRef{std::span{std::begin(iov), p}});
789     }
790   }
791 
792   if (literal_start != eop) {
793     res.emplace_back(LogFragmentType::LITERAL,
794                      make_string_ref(balloc, StringRef{literal_start, eop}));
795   }
796 
797   return res;
798 }
799 
800 namespace {
parse_address_family(int * dest,const StringRef & opt,const StringRef & optarg)801 int parse_address_family(int *dest, const StringRef &opt,
802                          const StringRef &optarg) {
803   if (util::strieq("auto"_sr, optarg)) {
804     *dest = AF_UNSPEC;
805     return 0;
806   }
807   if (util::strieq("IPv4"_sr, optarg)) {
808     *dest = AF_INET;
809     return 0;
810   }
811   if (util::strieq("IPv6"_sr, optarg)) {
812     *dest = AF_INET6;
813     return 0;
814   }
815 
816   LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
817   return -1;
818 }
819 } // namespace
820 
821 namespace {
parse_duration(ev_tstamp * dest,const StringRef & opt,const StringRef & optarg)822 int parse_duration(ev_tstamp *dest, const StringRef &opt,
823                    const StringRef &optarg) {
824   auto t = util::parse_duration_with_unit(optarg);
825   if (!t) {
826     LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
827     return -1;
828   }
829 
830   *dest = *t;
831 
832   return 0;
833 }
834 } // namespace
835 
836 namespace {
parse_tls_proto_version(int & dest,const StringRef & opt,const StringRef & optarg)837 int parse_tls_proto_version(int &dest, const StringRef &opt,
838                             const StringRef &optarg) {
839   auto v = tls::proto_version_from_string(optarg);
840   if (v == -1) {
841     LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
842     return -1;
843   }
844 
845   dest = v;
846 
847   return 0;
848 }
849 } // namespace
850 
851 struct MemcachedConnectionParams {
852   bool tls;
853 };
854 
855 namespace {
856 // Parses memcached connection configuration parameter |src_params|,
857 // and stores parsed results into |out|.  This function returns 0 if
858 // it succeeds, or -1.
parse_memcached_connection_params(MemcachedConnectionParams & out,const StringRef & src_params,const StringRef & opt)859 int parse_memcached_connection_params(MemcachedConnectionParams &out,
860                                       const StringRef &src_params,
861                                       const StringRef &opt) {
862   auto last = std::end(src_params);
863   for (auto first = std::begin(src_params); first != last;) {
864     auto end = std::find(first, last, ';');
865     auto param = StringRef{first, end};
866 
867     if (util::strieq("tls"_sr, param)) {
868       out.tls = true;
869     } else if (util::strieq("no-tls"_sr, param)) {
870       out.tls = false;
871     } else if (!param.empty()) {
872       LOG(ERROR) << opt << ": " << param << ": unknown keyword";
873       return -1;
874     }
875 
876     if (end == last) {
877       break;
878     }
879 
880     first = end + 1;
881   }
882 
883   return 0;
884 }
885 } // namespace
886 
887 struct UpstreamParams {
888   UpstreamAltMode alt_mode;
889   bool tls;
890   bool sni_fwd;
891   bool proxyproto;
892   bool quic;
893 };
894 
895 namespace {
896 // Parses upstream configuration parameter |src_params|, and stores
897 // parsed results into |out|.  This function returns 0 if it succeeds,
898 // or -1.
parse_upstream_params(UpstreamParams & out,const StringRef & src_params)899 int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
900   auto last = std::end(src_params);
901   for (auto first = std::begin(src_params); first != last;) {
902     auto end = std::find(first, last, ';');
903     auto param = StringRef{first, end};
904 
905     if (util::strieq("tls"_sr, param)) {
906       out.tls = true;
907     } else if (util::strieq("sni-fwd"_sr, param)) {
908       out.sni_fwd = true;
909     } else if (util::strieq("no-tls"_sr, param)) {
910       out.tls = false;
911     } else if (util::strieq("api"_sr, param)) {
912       if (out.alt_mode != UpstreamAltMode::NONE &&
913           out.alt_mode != UpstreamAltMode::API) {
914         LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
915         return -1;
916       }
917       out.alt_mode = UpstreamAltMode::API;
918     } else if (util::strieq("healthmon"_sr, param)) {
919       if (out.alt_mode != UpstreamAltMode::NONE &&
920           out.alt_mode != UpstreamAltMode::HEALTHMON) {
921         LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
922         return -1;
923       }
924       out.alt_mode = UpstreamAltMode::HEALTHMON;
925     } else if (util::strieq("proxyproto"_sr, param)) {
926       out.proxyproto = true;
927     } else if (util::strieq("quic"_sr, param)) {
928 #ifdef ENABLE_HTTP3
929       out.quic = true;
930 #else  // !ENABLE_HTTP3
931       LOG(ERROR) << "quic: QUIC is disabled at compile time";
932       return -1;
933 #endif // !ENABLE_HTTP3
934     } else if (!param.empty()) {
935       LOG(ERROR) << "frontend: " << param << ": unknown keyword";
936       return -1;
937     }
938 
939     if (end == last) {
940       break;
941     }
942 
943     first = end + 1;
944   }
945 
946   return 0;
947 }
948 } // namespace
949 
950 struct DownstreamParams {
951   StringRef sni;
952   StringRef mruby;
953   StringRef group;
954   AffinityConfig affinity;
955   ev_tstamp read_timeout;
956   ev_tstamp write_timeout;
957   size_t fall;
958   size_t rise;
959   uint32_t weight;
960   uint32_t group_weight;
961   Proto proto;
962   bool tls;
963   bool dns;
964   bool redirect_if_not_tls;
965   bool upgrade_scheme;
966   bool dnf;
967 };
968 
969 namespace {
970 // Parses |value| of parameter named |name| as duration.  This
971 // function returns 0 if it succeeds and the parsed value is assigned
972 // to |dest|, or -1.
parse_downstream_param_duration(ev_tstamp & dest,const StringRef & name,const StringRef & value)973 int parse_downstream_param_duration(ev_tstamp &dest, const StringRef &name,
974                                     const StringRef &value) {
975   auto t = util::parse_duration_with_unit(value);
976   if (!t) {
977     LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
978     return -1;
979   }
980   dest = *t;
981   return 0;
982 }
983 } // namespace
984 
985 namespace {
986 // Parses downstream configuration parameter |src_params|, and stores
987 // parsed results into |out|.  This function returns 0 if it succeeds,
988 // or -1.
parse_downstream_params(DownstreamParams & out,const StringRef & src_params)989 int parse_downstream_params(DownstreamParams &out,
990                             const StringRef &src_params) {
991   auto last = std::end(src_params);
992   for (auto first = std::begin(src_params); first != last;) {
993     auto end = std::find(first, last, ';');
994     auto param = StringRef{first, end};
995 
996     if (util::istarts_with(param, "proto="_sr)) {
997       auto protostr = StringRef{first + str_size("proto="), end};
998       if (protostr.empty()) {
999         LOG(ERROR) << "backend: proto: protocol is empty";
1000         return -1;
1001       }
1002 
1003       if ("h2"_sr == protostr) {
1004         out.proto = Proto::HTTP2;
1005       } else if ("http/1.1"_sr == protostr) {
1006         out.proto = Proto::HTTP1;
1007       } else {
1008         LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
1009         return -1;
1010       }
1011     } else if (util::istarts_with(param, "fall="_sr)) {
1012       auto valstr = StringRef{first + str_size("fall="), end};
1013       if (valstr.empty()) {
1014         LOG(ERROR) << "backend: fall: non-negative integer is expected";
1015         return -1;
1016       }
1017 
1018       auto n = util::parse_uint(valstr);
1019       if (!n) {
1020         LOG(ERROR) << "backend: fall: non-negative integer is expected";
1021         return -1;
1022       }
1023 
1024       out.fall = *n;
1025     } else if (util::istarts_with(param, "rise="_sr)) {
1026       auto valstr = StringRef{first + str_size("rise="), end};
1027       if (valstr.empty()) {
1028         LOG(ERROR) << "backend: rise: non-negative integer is expected";
1029         return -1;
1030       }
1031 
1032       auto n = util::parse_uint(valstr);
1033       if (!n) {
1034         LOG(ERROR) << "backend: rise: non-negative integer is expected";
1035         return -1;
1036       }
1037 
1038       out.rise = *n;
1039     } else if (util::strieq("tls"_sr, param)) {
1040       out.tls = true;
1041     } else if (util::strieq("no-tls"_sr, param)) {
1042       out.tls = false;
1043     } else if (util::istarts_with(param, "sni="_sr)) {
1044       out.sni = StringRef{first + str_size("sni="), end};
1045     } else if (util::istarts_with(param, "affinity="_sr)) {
1046       auto valstr = StringRef{first + str_size("affinity="), end};
1047       if (util::strieq("none"_sr, valstr)) {
1048         out.affinity.type = SessionAffinity::NONE;
1049       } else if (util::strieq("ip"_sr, valstr)) {
1050         out.affinity.type = SessionAffinity::IP;
1051       } else if (util::strieq("cookie"_sr, valstr)) {
1052         out.affinity.type = SessionAffinity::COOKIE;
1053       } else {
1054         LOG(ERROR)
1055           << "backend: affinity: value must be one of none, ip, and cookie";
1056         return -1;
1057       }
1058     } else if (util::istarts_with(param, "affinity-cookie-name="_sr)) {
1059       auto val = StringRef{first + str_size("affinity-cookie-name="), end};
1060       if (val.empty()) {
1061         LOG(ERROR)
1062           << "backend: affinity-cookie-name: non empty string is expected";
1063         return -1;
1064       }
1065       out.affinity.cookie.name = val;
1066     } else if (util::istarts_with(param, "affinity-cookie-path="_sr)) {
1067       out.affinity.cookie.path =
1068         StringRef{first + str_size("affinity-cookie-path="), end};
1069     } else if (util::istarts_with(param, "affinity-cookie-secure="_sr)) {
1070       auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
1071       if (util::strieq("auto"_sr, valstr)) {
1072         out.affinity.cookie.secure = SessionAffinityCookieSecure::AUTO;
1073       } else if (util::strieq("yes"_sr, valstr)) {
1074         out.affinity.cookie.secure = SessionAffinityCookieSecure::YES;
1075       } else if (util::strieq("no"_sr, valstr)) {
1076         out.affinity.cookie.secure = SessionAffinityCookieSecure::NO;
1077       } else {
1078         LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
1079                       "auto, yes, and no";
1080         return -1;
1081       }
1082     } else if (util::istarts_with(param, "affinity-cookie-stickiness="_sr)) {
1083       auto valstr =
1084         StringRef{first + str_size("affinity-cookie-stickiness="), end};
1085       if (util::strieq("loose"_sr, valstr)) {
1086         out.affinity.cookie.stickiness = SessionAffinityCookieStickiness::LOOSE;
1087       } else if (util::strieq("strict"_sr, valstr)) {
1088         out.affinity.cookie.stickiness =
1089           SessionAffinityCookieStickiness::STRICT;
1090       } else {
1091         LOG(ERROR) << "backend: affinity-cookie-stickiness: value must be "
1092                       "either loose or strict";
1093         return -1;
1094       }
1095     } else if (util::strieq("dns"_sr, param)) {
1096       out.dns = true;
1097     } else if (util::strieq("redirect-if-not-tls"_sr, param)) {
1098       out.redirect_if_not_tls = true;
1099     } else if (util::strieq("upgrade-scheme"_sr, param)) {
1100       out.upgrade_scheme = true;
1101     } else if (util::istarts_with(param, "mruby="_sr)) {
1102       auto valstr = StringRef{first + str_size("mruby="), end};
1103       out.mruby = valstr;
1104     } else if (util::istarts_with(param, "read-timeout="_sr)) {
1105       if (parse_downstream_param_duration(
1106             out.read_timeout, "read-timeout"_sr,
1107             StringRef{first + str_size("read-timeout="), end}) == -1) {
1108         return -1;
1109       }
1110     } else if (util::istarts_with(param, "write-timeout="_sr)) {
1111       if (parse_downstream_param_duration(
1112             out.write_timeout, "write-timeout"_sr,
1113             StringRef{first + str_size("write-timeout="), end}) == -1) {
1114         return -1;
1115       }
1116     } else if (util::istarts_with(param, "weight="_sr)) {
1117       auto valstr = StringRef{first + str_size("weight="), end};
1118       if (valstr.empty()) {
1119         LOG(ERROR)
1120           << "backend: weight: non-negative integer [1, 256] is expected";
1121         return -1;
1122       }
1123 
1124       auto n = util::parse_uint(valstr);
1125       if (!n || (n < 1 || n > 256)) {
1126         LOG(ERROR)
1127           << "backend: weight: non-negative integer [1, 256] is expected";
1128         return -1;
1129       }
1130       out.weight = *n;
1131     } else if (util::istarts_with(param, "group="_sr)) {
1132       auto valstr = StringRef{first + str_size("group="), end};
1133       if (valstr.empty()) {
1134         LOG(ERROR) << "backend: group: empty string is not allowed";
1135         return -1;
1136       }
1137       out.group = valstr;
1138     } else if (util::istarts_with(param, "group-weight="_sr)) {
1139       auto valstr = StringRef{first + str_size("group-weight="), end};
1140       if (valstr.empty()) {
1141         LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
1142                       "expected";
1143         return -1;
1144       }
1145 
1146       auto n = util::parse_uint(valstr);
1147       if (!n || (n < 1 || n > 256)) {
1148         LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
1149                       "expected";
1150         return -1;
1151       }
1152       out.group_weight = *n;
1153     } else if (util::strieq("dnf"_sr, param)) {
1154       out.dnf = true;
1155     } else if (!param.empty()) {
1156       LOG(ERROR) << "backend: " << param << ": unknown keyword";
1157       return -1;
1158     }
1159 
1160     if (end == last) {
1161       break;
1162     }
1163 
1164     first = end + 1;
1165   }
1166 
1167   return 0;
1168 }
1169 } // namespace
1170 
1171 namespace {
1172 // Parses host-path mapping patterns in |src_pattern|, and stores
1173 // mappings in config.  We will store each host-path pattern found in
1174 // |src| with |addr|.  |addr| will be copied accordingly.  Also we
1175 // make a group based on the pattern.  The "/" pattern is considered
1176 // as catch-all.  We also parse protocol specified in |src_proto|.
1177 //
1178 // This function returns 0 if it succeeds, or -1.
parse_mapping(Config * config,DownstreamAddrConfig & addr,std::map<StringRef,size_t> & pattern_addr_indexer,const StringRef & src_pattern,const StringRef & src_params)1179 int parse_mapping(Config *config, DownstreamAddrConfig &addr,
1180                   std::map<StringRef, size_t> &pattern_addr_indexer,
1181                   const StringRef &src_pattern, const StringRef &src_params) {
1182   // This returns at least 1 element (it could be empty string).  We
1183   // will append '/' to all patterns, so it becomes catch-all pattern.
1184   auto mapping = util::split_str(src_pattern, ':');
1185   assert(!mapping.empty());
1186   auto &downstreamconf = *config->conn.downstream;
1187   auto &addr_groups = downstreamconf.addr_groups;
1188 
1189   DownstreamParams params{};
1190   params.proto = Proto::HTTP1;
1191   params.weight = 1;
1192 
1193   if (parse_downstream_params(params, src_params) != 0) {
1194     return -1;
1195   }
1196 
1197   if (addr.host_unix && params.dns) {
1198     LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
1199     return -1;
1200   }
1201 
1202   if (params.affinity.type == SessionAffinity::COOKIE &&
1203       params.affinity.cookie.name.empty()) {
1204     LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
1205                   "affinity=cookie is specified";
1206     return -1;
1207   }
1208 
1209   addr.fall = params.fall;
1210   addr.rise = params.rise;
1211   addr.weight = params.weight;
1212   addr.group = make_string_ref(downstreamconf.balloc, params.group);
1213   addr.group_weight = params.group_weight;
1214   addr.proto = params.proto;
1215   addr.tls = params.tls;
1216   addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
1217   addr.dns = params.dns;
1218   addr.upgrade_scheme = params.upgrade_scheme;
1219   addr.dnf = params.dnf;
1220 
1221   auto &routerconf = downstreamconf.router;
1222   auto &router = routerconf.router;
1223   auto &rw_router = routerconf.rev_wildcard_router;
1224   auto &wildcard_patterns = routerconf.wildcard_patterns;
1225 
1226   for (const auto &raw_pattern : mapping) {
1227     StringRef pattern;
1228     auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
1229     if (slash == std::end(raw_pattern)) {
1230       // This effectively makes empty pattern to "/".  2 for '/' and
1231       // terminal NULL character.
1232       auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2);
1233       auto p = std::copy(std::begin(raw_pattern), std::end(raw_pattern),
1234                          std::begin(iov));
1235       util::inp_strlower(std::begin(iov), p);
1236       *p++ = '/';
1237       *p = '\0';
1238       pattern = StringRef{std::span{std::begin(iov), p}};
1239     } else {
1240       auto path = http2::normalize_path_colon(
1241         downstreamconf.balloc, StringRef{slash, std::end(raw_pattern)},
1242         StringRef{});
1243       auto iov = make_byte_ref(downstreamconf.balloc,
1244                                std::distance(std::begin(raw_pattern), slash) +
1245                                  path.size() + 1);
1246       auto p = std::copy(std::begin(raw_pattern), slash, std::begin(iov));
1247       util::inp_strlower(std::begin(iov), p);
1248       p = std::copy(std::begin(path), std::end(path), p);
1249       *p = '\0';
1250       pattern = StringRef{std::span{std::begin(iov), p}};
1251     }
1252     auto it = pattern_addr_indexer.find(pattern);
1253     if (it != std::end(pattern_addr_indexer)) {
1254       auto &g = addr_groups[(*it).second];
1255       // Last value wins if we have multiple different affinity
1256       // value under one group.
1257       if (params.affinity.type != SessionAffinity::NONE) {
1258         if (g.affinity.type == SessionAffinity::NONE) {
1259           g.affinity.type = params.affinity.type;
1260           if (params.affinity.type == SessionAffinity::COOKIE) {
1261             g.affinity.cookie.name = make_string_ref(
1262               downstreamconf.balloc, params.affinity.cookie.name);
1263             if (!params.affinity.cookie.path.empty()) {
1264               g.affinity.cookie.path = make_string_ref(
1265                 downstreamconf.balloc, params.affinity.cookie.path);
1266             }
1267             g.affinity.cookie.secure = params.affinity.cookie.secure;
1268             g.affinity.cookie.stickiness = params.affinity.cookie.stickiness;
1269           }
1270         } else if (g.affinity.type != params.affinity.type ||
1271                    g.affinity.cookie.name != params.affinity.cookie.name ||
1272                    g.affinity.cookie.path != params.affinity.cookie.path ||
1273                    g.affinity.cookie.secure != params.affinity.cookie.secure ||
1274                    g.affinity.cookie.stickiness !=
1275                      params.affinity.cookie.stickiness) {
1276           LOG(ERROR) << "backend: affinity: multiple different affinity "
1277                         "configurations found in a single group";
1278           return -1;
1279         }
1280       }
1281       // If at least one backend requires frontend TLS connection,
1282       // enable it for all backends sharing the same pattern.
1283       if (params.redirect_if_not_tls) {
1284         g.redirect_if_not_tls = true;
1285       }
1286       // All backends in the same group must have the same mruby path.
1287       // If some backends do not specify mruby file, and there is at
1288       // least one backend with mruby file, it is used for all
1289       // backends in the group.
1290       if (!params.mruby.empty()) {
1291         if (g.mruby_file.empty()) {
1292           g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
1293         } else if (g.mruby_file != params.mruby) {
1294           LOG(ERROR) << "backend: mruby: multiple different mruby file found "
1295                         "in a single group";
1296           return -1;
1297         }
1298       }
1299       // All backends in the same group must have the same read/write
1300       // timeout.  If some backends do not specify read/write timeout,
1301       // and there is at least one backend with read/write timeout, it
1302       // is used for all backends in the group.
1303       if (params.read_timeout > 1e-9) {
1304         if (g.timeout.read < 1e-9) {
1305           g.timeout.read = params.read_timeout;
1306         } else if (fabs(g.timeout.read - params.read_timeout) > 1e-9) {
1307           LOG(ERROR)
1308             << "backend: read-timeout: multiple different read-timeout "
1309                "found in a single group";
1310           return -1;
1311         }
1312       }
1313       if (params.write_timeout > 1e-9) {
1314         if (g.timeout.write < 1e-9) {
1315           g.timeout.write = params.write_timeout;
1316         } else if (fabs(g.timeout.write - params.write_timeout) > 1e-9) {
1317           LOG(ERROR) << "backend: write-timeout: multiple different "
1318                         "write-timeout found in a single group";
1319           return -1;
1320         }
1321       }
1322       // All backends in the same group must have the same dnf
1323       // setting.  If some backends do not specify dnf, and there is
1324       // at least one backend with dnf, it is used for all backends in
1325       // the group.  In general, multiple backends are not necessary
1326       // for dnf because there is no need for load balancing.
1327       if (params.dnf) {
1328         g.dnf = true;
1329       }
1330 
1331       g.addrs.push_back(addr);
1332       continue;
1333     }
1334 
1335     auto idx = addr_groups.size();
1336     pattern_addr_indexer.emplace(pattern, idx);
1337     addr_groups.emplace_back(pattern);
1338     auto &g = addr_groups.back();
1339     g.addrs.push_back(addr);
1340     g.affinity.type = params.affinity.type;
1341     if (params.affinity.type == SessionAffinity::COOKIE) {
1342       g.affinity.cookie.name =
1343         make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
1344       if (!params.affinity.cookie.path.empty()) {
1345         g.affinity.cookie.path =
1346           make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
1347       }
1348       g.affinity.cookie.secure = params.affinity.cookie.secure;
1349       g.affinity.cookie.stickiness = params.affinity.cookie.stickiness;
1350     }
1351     g.redirect_if_not_tls = params.redirect_if_not_tls;
1352     g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
1353     g.timeout.read = params.read_timeout;
1354     g.timeout.write = params.write_timeout;
1355     g.dnf = params.dnf;
1356 
1357     if (pattern[0] == '*') {
1358       // wildcard pattern
1359       auto path_first =
1360         std::find(std::begin(g.pattern), std::end(g.pattern), '/');
1361 
1362       auto host = StringRef{std::begin(g.pattern) + 1, path_first};
1363       auto path = StringRef{path_first, std::end(g.pattern)};
1364 
1365       auto path_is_wildcard = false;
1366       if (path[path.size() - 1] == '*') {
1367         path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
1368         path_is_wildcard = true;
1369       }
1370 
1371       auto it = std::find_if(
1372         std::begin(wildcard_patterns), std::end(wildcard_patterns),
1373         [&host](const WildcardPattern &wp) { return wp.host == host; });
1374 
1375       if (it == std::end(wildcard_patterns)) {
1376         wildcard_patterns.emplace_back(host);
1377 
1378         auto &router = wildcard_patterns.back().router;
1379         router.add_route(path, idx, path_is_wildcard);
1380 
1381         auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
1382         auto p =
1383           std::reverse_copy(std::begin(host), std::end(host), std::begin(iov));
1384         *p = '\0';
1385         auto rev_host = StringRef{std::span{std::begin(iov), p}};
1386 
1387         rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
1388       } else {
1389         (*it).router.add_route(path, idx, path_is_wildcard);
1390       }
1391 
1392       continue;
1393     }
1394 
1395     auto path_is_wildcard = false;
1396     if (pattern[pattern.size() - 1] == '*') {
1397       pattern = StringRef{std::begin(pattern),
1398                           std::begin(pattern) + pattern.size() - 1};
1399       path_is_wildcard = true;
1400     }
1401 
1402     router.add_route(pattern, idx, path_is_wildcard);
1403   }
1404   return 0;
1405 }
1406 } // namespace
1407 
1408 namespace {
parse_forwarded_node_type(const StringRef & optarg)1409 ForwardedNode parse_forwarded_node_type(const StringRef &optarg) {
1410   if (util::strieq("obfuscated"_sr, optarg)) {
1411     return ForwardedNode::OBFUSCATED;
1412   }
1413 
1414   if (util::strieq("ip"_sr, optarg)) {
1415     return ForwardedNode::IP;
1416   }
1417 
1418   if (optarg.size() < 2 || optarg[0] != '_') {
1419     return static_cast<ForwardedNode>(-1);
1420   }
1421 
1422   if (std::find_if_not(std::begin(optarg), std::end(optarg), [](char c) {
1423         return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' ||
1424                c == '-';
1425       }) != std::end(optarg)) {
1426     return static_cast<ForwardedNode>(-1);
1427   }
1428 
1429   return ForwardedNode::OBFUSCATED;
1430 }
1431 } // namespace
1432 
1433 namespace {
parse_error_page(std::vector<ErrorPage> & error_pages,const StringRef & opt,const StringRef & optarg)1434 int parse_error_page(std::vector<ErrorPage> &error_pages, const StringRef &opt,
1435                      const StringRef &optarg) {
1436   std::array<char, STRERROR_BUFSIZE> errbuf;
1437 
1438   auto eq = std::find(std::begin(optarg), std::end(optarg), '=');
1439   if (eq == std::end(optarg) || eq + 1 == std::end(optarg)) {
1440     LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
1441     return -1;
1442   }
1443 
1444   auto codestr = StringRef{std::begin(optarg), eq};
1445   unsigned int code;
1446 
1447   if (codestr == "*"_sr) {
1448     code = 0;
1449   } else {
1450     auto n = util::parse_uint(codestr);
1451 
1452     if (!n || n < 400 || n > 599) {
1453       LOG(ERROR) << opt << ": bad code: '" << codestr << "'";
1454       return -1;
1455     }
1456 
1457     code = static_cast<unsigned int>(*n);
1458   }
1459 
1460   auto path = StringRef{eq + 1, std::end(optarg)};
1461 
1462   std::vector<uint8_t> content;
1463   auto fd = open(path.data(), O_RDONLY);
1464   if (fd == -1) {
1465     auto error = errno;
1466     LOG(ERROR) << opt << ": " << optarg << ": "
1467                << xsi_strerror(error, errbuf.data(), errbuf.size());
1468     return -1;
1469   }
1470 
1471   auto fd_closer = defer(close, fd);
1472 
1473   std::array<uint8_t, 4096> buf;
1474   for (;;) {
1475     auto n = read(fd, buf.data(), buf.size());
1476     if (n == -1) {
1477       auto error = errno;
1478       LOG(ERROR) << opt << ": " << optarg << ": "
1479                  << xsi_strerror(error, errbuf.data(), errbuf.size());
1480       return -1;
1481     }
1482     if (n == 0) {
1483       break;
1484     }
1485     content.insert(std::end(content), std::begin(buf), std::begin(buf) + n);
1486   }
1487 
1488   error_pages.push_back(ErrorPage{std::move(content), code});
1489 
1490   return 0;
1491 }
1492 } // namespace
1493 
1494 namespace {
1495 // Maximum size of SCT extension payload length.
1496 constexpr size_t MAX_SCT_EXT_LEN = 16_k;
1497 } // namespace
1498 
1499 struct SubcertParams {
1500   StringRef sct_dir;
1501 };
1502 
1503 namespace {
1504 // Parses subcert parameter |src_params|, and stores parsed results
1505 // into |out|.  This function returns 0 if it succeeds, or -1.
parse_subcert_params(SubcertParams & out,const StringRef & src_params)1506 int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
1507   auto last = std::end(src_params);
1508   for (auto first = std::begin(src_params); first != last;) {
1509     auto end = std::find(first, last, ';');
1510     auto param = StringRef{first, end};
1511 
1512     if (util::istarts_with(param, "sct-dir="_sr)) {
1513 #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_BORINGSSL)
1514       auto sct_dir =
1515         StringRef{std::begin(param) + str_size("sct-dir="), std::end(param)};
1516       if (sct_dir.empty()) {
1517         LOG(ERROR) << "subcert: " << param << ": empty sct-dir";
1518         return -1;
1519       }
1520       out.sct_dir = sct_dir;
1521 #else  // !NGHTTP2_GENUINE_OPENSSL && !NGHTTP2_OPENSSL_IS_BORINGSSL
1522       LOG(WARN) << "subcert: sct-dir is ignored because underlying TLS library "
1523                    "does not support SCT";
1524 #endif // !NGHTTP2_GENUINE_OPENSSL && !NGHTTP2_OPENSSL_IS_BORINGSSL
1525     } else if (!param.empty()) {
1526       LOG(ERROR) << "subcert: " << param << ": unknown keyword";
1527       return -1;
1528     }
1529 
1530     if (end == last) {
1531       break;
1532     }
1533 
1534     first = end + 1;
1535   }
1536 
1537   return 0;
1538 }
1539 } // namespace
1540 
1541 namespace {
1542 // Reads *.sct files from directory denoted by |dir_path|.  |dir_path|
1543 // must be NULL-terminated string.
read_tls_sct_from_dir(std::vector<uint8_t> & dst,const StringRef & opt,const StringRef & dir_path)1544 int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
1545                           const StringRef &dir_path) {
1546   std::array<char, STRERROR_BUFSIZE> errbuf;
1547 
1548   auto dir = opendir(dir_path.data());
1549   if (dir == nullptr) {
1550     auto error = errno;
1551     LOG(ERROR) << opt << ": " << dir_path << ": "
1552                << xsi_strerror(error, errbuf.data(), errbuf.size());
1553     return -1;
1554   }
1555 
1556   auto closer = defer(closedir, dir);
1557 
1558   // 2 bytes total length field
1559   auto len_idx = std::distance(std::begin(dst), std::end(dst));
1560   dst.insert(std::end(dst), 2, 0);
1561 
1562   for (;;) {
1563     errno = 0;
1564     auto ent = readdir(dir);
1565     if (ent == nullptr) {
1566       if (errno != 0) {
1567         auto error = errno;
1568         LOG(ERROR) << opt << ": failed to read directory " << dir_path << ": "
1569                    << xsi_strerror(error, errbuf.data(), errbuf.size());
1570         return -1;
1571       }
1572       break;
1573     }
1574 
1575     auto name = StringRef{ent->d_name};
1576 
1577     if (name[0] == '.' || !util::iends_with(name, ".sct"_sr)) {
1578       continue;
1579     }
1580 
1581     std::string path;
1582     path.resize(dir_path.size() + 1 + name.size());
1583     {
1584       auto p = std::begin(path);
1585       p = std::copy(std::begin(dir_path), std::end(dir_path), p);
1586       *p++ = '/';
1587       std::copy(std::begin(name), std::end(name), p);
1588     }
1589 
1590     auto fd = open(path.c_str(), O_RDONLY);
1591     if (fd == -1) {
1592       auto error = errno;
1593       LOG(ERROR) << opt << ": failed to read SCT from " << path << ": "
1594                  << xsi_strerror(error, errbuf.data(), errbuf.size());
1595       return -1;
1596     }
1597 
1598     auto closer = defer(close, fd);
1599 
1600     // 2 bytes length field for this SCT.
1601     auto len_idx = std::distance(std::begin(dst), std::end(dst));
1602     dst.insert(std::end(dst), 2, 0);
1603 
1604     // *.sct file tends to be small; around 110+ bytes.
1605     std::array<char, 256> buf;
1606     for (;;) {
1607       ssize_t nread;
1608       while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
1609         ;
1610 
1611       if (nread == -1) {
1612         auto error = errno;
1613         LOG(ERROR) << opt << ": failed to read SCT data from " << path << ": "
1614                    << xsi_strerror(error, errbuf.data(), errbuf.size());
1615         return -1;
1616       }
1617 
1618       if (nread == 0) {
1619         break;
1620       }
1621 
1622       dst.insert(std::end(dst), std::begin(buf), std::begin(buf) + nread);
1623 
1624       if (dst.size() > MAX_SCT_EXT_LEN) {
1625         LOG(ERROR) << opt << ": the concatenated SCT data from " << dir_path
1626                    << " is too large.  Max " << MAX_SCT_EXT_LEN;
1627         return -1;
1628       }
1629     }
1630 
1631     auto len = dst.size() - len_idx - 2;
1632 
1633     if (len == 0) {
1634       dst.resize(dst.size() - 2);
1635       continue;
1636     }
1637 
1638     dst[len_idx] = len >> 8;
1639     dst[len_idx + 1] = len;
1640   }
1641 
1642   auto len = dst.size() - len_idx - 2;
1643 
1644   if (len == 0) {
1645     dst.resize(dst.size() - 2);
1646     return 0;
1647   }
1648 
1649   dst[len_idx] = len >> 8;
1650   dst[len_idx + 1] = len;
1651 
1652   return 0;
1653 }
1654 } // namespace
1655 
1656 #ifndef OPENSSL_NO_PSK
1657 namespace {
1658 // Reads PSK secrets from path, and parses each line.  The result is
1659 // directly stored into config->tls.psk_secrets.  This function
1660 // returns 0 if it succeeds, or -1.
parse_psk_secrets(Config * config,const StringRef & path)1661 int parse_psk_secrets(Config *config, const StringRef &path) {
1662   auto &tlsconf = config->tls;
1663 
1664   std::ifstream f(path.data(), std::ios::binary);
1665   if (!f) {
1666     LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
1667     return -1;
1668   }
1669 
1670   size_t lineno = 0;
1671   std::string line;
1672   while (std::getline(f, line)) {
1673     ++lineno;
1674     if (line.empty() || line[0] == '#') {
1675       continue;
1676     }
1677 
1678     auto sep_it = std::find(std::begin(line), std::end(line), ':');
1679     if (sep_it == std::end(line)) {
1680       LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1681                  << ": could not fine separator at line " << lineno;
1682       return -1;
1683     }
1684 
1685     if (sep_it == std::begin(line)) {
1686       LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
1687                  << lineno;
1688       return -1;
1689     }
1690 
1691     if (sep_it + 1 == std::end(line)) {
1692       LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
1693                  << lineno;
1694       return -1;
1695     }
1696 
1697     if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
1698       LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1699                  << ": secret must be hex string at line " << lineno;
1700       return -1;
1701     }
1702 
1703     auto identity =
1704       make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
1705 
1706     auto secret =
1707       util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
1708 
1709     auto rv = tlsconf.psk_secrets.emplace(identity, secret);
1710     if (!rv.second) {
1711       LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1712                  << ": identity has already been registered at line " << lineno;
1713       return -1;
1714     }
1715   }
1716 
1717   return 0;
1718 }
1719 } // namespace
1720 #endif // !OPENSSL_NO_PSK
1721 
1722 #ifndef OPENSSL_NO_PSK
1723 namespace {
1724 // Reads PSK secrets from path, and parses each line.  The result is
1725 // directly stored into config->tls.client.psk.  This function returns
1726 // 0 if it succeeds, or -1.
parse_client_psk_secrets(Config * config,const StringRef & path)1727 int parse_client_psk_secrets(Config *config, const StringRef &path) {
1728   auto &tlsconf = config->tls;
1729 
1730   std::ifstream f(path.data(), std::ios::binary);
1731   if (!f) {
1732     LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
1733                << path;
1734     return -1;
1735   }
1736 
1737   size_t lineno = 0;
1738   std::string line;
1739   while (std::getline(f, line)) {
1740     ++lineno;
1741     if (line.empty() || line[0] == '#') {
1742       continue;
1743     }
1744 
1745     auto sep_it = std::find(std::begin(line), std::end(line), ':');
1746     if (sep_it == std::end(line)) {
1747       LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
1748                  << ": could not find separator at line " << lineno;
1749       return -1;
1750     }
1751 
1752     if (sep_it == std::begin(line)) {
1753       LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
1754                  << lineno;
1755       return -1;
1756     }
1757 
1758     if (sep_it + 1 == std::end(line)) {
1759       LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
1760                  << lineno;
1761       return -1;
1762     }
1763 
1764     if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
1765       LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
1766                  << ": secret must be hex string at line " << lineno;
1767       return -1;
1768     }
1769 
1770     tlsconf.client.psk.identity =
1771       make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
1772 
1773     tlsconf.client.psk.secret = StringRef{
1774       util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)})};
1775 
1776     return 0;
1777   }
1778 
1779   return 0;
1780 }
1781 } // namespace
1782 #endif // !OPENSSL_NO_PSK
1783 
1784 // generated by gennghttpxfun.py
option_lookup_token(const StringRef & name)1785 int option_lookup_token(const StringRef &name) {
1786   switch (name.size()) {
1787   case 4:
1788     switch (name[3]) {
1789     case 'f':
1790       if (util::strieq("con"_sr, name, 3)) {
1791         return SHRPX_OPTID_CONF;
1792       }
1793       break;
1794     case 'r':
1795       if (util::strieq("use"_sr, name, 3)) {
1796         return SHRPX_OPTID_USER;
1797       }
1798       break;
1799     }
1800     break;
1801   case 6:
1802     switch (name[5]) {
1803     case 'a':
1804       if (util::strieq("no-vi"_sr, name, 5)) {
1805         return SHRPX_OPTID_NO_VIA;
1806       }
1807       break;
1808     case 'c':
1809       if (util::strieq("altsv"_sr, name, 5)) {
1810         return SHRPX_OPTID_ALTSVC;
1811       }
1812       break;
1813     case 'n':
1814       if (util::strieq("daemo"_sr, name, 5)) {
1815         return SHRPX_OPTID_DAEMON;
1816       }
1817       break;
1818     case 't':
1819       if (util::strieq("cacer"_sr, name, 5)) {
1820         return SHRPX_OPTID_CACERT;
1821       }
1822       if (util::strieq("clien"_sr, name, 5)) {
1823         return SHRPX_OPTID_CLIENT;
1824       }
1825       break;
1826     }
1827     break;
1828   case 7:
1829     switch (name[6]) {
1830     case 'd':
1831       if (util::strieq("backen"_sr, name, 6)) {
1832         return SHRPX_OPTID_BACKEND;
1833       }
1834       break;
1835     case 'e':
1836       if (util::strieq("includ"_sr, name, 6)) {
1837         return SHRPX_OPTID_INCLUDE;
1838       }
1839       break;
1840     case 'g':
1841       if (util::strieq("backlo"_sr, name, 6)) {
1842         return SHRPX_OPTID_BACKLOG;
1843       }
1844       if (util::strieq("paddin"_sr, name, 6)) {
1845         return SHRPX_OPTID_PADDING;
1846       }
1847       break;
1848     case 'p':
1849       if (util::strieq("no-ocs"_sr, name, 6)) {
1850         return SHRPX_OPTID_NO_OCSP;
1851       }
1852       break;
1853     case 's':
1854       if (util::strieq("cipher"_sr, name, 6)) {
1855         return SHRPX_OPTID_CIPHERS;
1856       }
1857       if (util::strieq("worker"_sr, name, 6)) {
1858         return SHRPX_OPTID_WORKERS;
1859       }
1860       break;
1861     case 't':
1862       if (util::strieq("subcer"_sr, name, 6)) {
1863         return SHRPX_OPTID_SUBCERT;
1864       }
1865       break;
1866     }
1867     break;
1868   case 8:
1869     switch (name[7]) {
1870     case 'd':
1871       if (util::strieq("fronten"_sr, name, 7)) {
1872         return SHRPX_OPTID_FRONTEND;
1873       }
1874       break;
1875     case 'e':
1876       if (util::strieq("insecur"_sr, name, 7)) {
1877         return SHRPX_OPTID_INSECURE;
1878       }
1879       if (util::strieq("pid-fil"_sr, name, 7)) {
1880         return SHRPX_OPTID_PID_FILE;
1881       }
1882       break;
1883     case 'n':
1884       if (util::strieq("fastope"_sr, name, 7)) {
1885         return SHRPX_OPTID_FASTOPEN;
1886       }
1887       break;
1888     case 's':
1889       if (util::strieq("tls-ktl"_sr, name, 7)) {
1890         return SHRPX_OPTID_TLS_KTLS;
1891       }
1892       break;
1893     case 't':
1894       if (util::strieq("npn-lis"_sr, name, 7)) {
1895         return SHRPX_OPTID_NPN_LIST;
1896       }
1897       break;
1898     }
1899     break;
1900   case 9:
1901     switch (name[8]) {
1902     case 'e':
1903       if (util::strieq("no-kqueu"_sr, name, 8)) {
1904         return SHRPX_OPTID_NO_KQUEUE;
1905       }
1906       if (util::strieq("read-rat"_sr, name, 8)) {
1907         return SHRPX_OPTID_READ_RATE;
1908       }
1909       break;
1910     case 'l':
1911       if (util::strieq("log-leve"_sr, name, 8)) {
1912         return SHRPX_OPTID_LOG_LEVEL;
1913       }
1914       break;
1915     case 't':
1916       if (util::strieq("alpn-lis"_sr, name, 8)) {
1917         return SHRPX_OPTID_ALPN_LIST;
1918       }
1919       break;
1920     }
1921     break;
1922   case 10:
1923     switch (name[9]) {
1924     case 'e':
1925       if (util::strieq("error-pag"_sr, name, 9)) {
1926         return SHRPX_OPTID_ERROR_PAGE;
1927       }
1928       if (util::strieq("mruby-fil"_sr, name, 9)) {
1929         return SHRPX_OPTID_MRUBY_FILE;
1930       }
1931       if (util::strieq("write-rat"_sr, name, 9)) {
1932         return SHRPX_OPTID_WRITE_RATE;
1933       }
1934       break;
1935     case 't':
1936       if (util::strieq("read-burs"_sr, name, 9)) {
1937         return SHRPX_OPTID_READ_BURST;
1938       }
1939       break;
1940     }
1941     break;
1942   case 11:
1943     switch (name[10]) {
1944     case 'e':
1945       if (util::strieq("server-nam"_sr, name, 10)) {
1946         return SHRPX_OPTID_SERVER_NAME;
1947       }
1948       break;
1949     case 'f':
1950       if (util::strieq("no-quic-bp"_sr, name, 10)) {
1951         return SHRPX_OPTID_NO_QUIC_BPF;
1952       }
1953       break;
1954     case 'r':
1955       if (util::strieq("tls-sct-di"_sr, name, 10)) {
1956         return SHRPX_OPTID_TLS_SCT_DIR;
1957       }
1958       break;
1959     case 's':
1960       if (util::strieq("backend-tl"_sr, name, 10)) {
1961         return SHRPX_OPTID_BACKEND_TLS;
1962       }
1963       if (util::strieq("ecdh-curve"_sr, name, 10)) {
1964         return SHRPX_OPTID_ECDH_CURVES;
1965       }
1966       if (util::strieq("psk-secret"_sr, name, 10)) {
1967         return SHRPX_OPTID_PSK_SECRETS;
1968       }
1969       break;
1970     case 't':
1971       if (util::strieq("write-burs"_sr, name, 10)) {
1972         return SHRPX_OPTID_WRITE_BURST;
1973       }
1974       break;
1975     case 'y':
1976       if (util::strieq("dns-max-tr"_sr, name, 10)) {
1977         return SHRPX_OPTID_DNS_MAX_TRY;
1978       }
1979       if (util::strieq("http2-prox"_sr, name, 10)) {
1980         return SHRPX_OPTID_HTTP2_PROXY;
1981       }
1982       break;
1983     }
1984     break;
1985   case 12:
1986     switch (name[11]) {
1987     case '4':
1988       if (util::strieq("backend-ipv"_sr, name, 11)) {
1989         return SHRPX_OPTID_BACKEND_IPV4;
1990       }
1991       break;
1992     case '6':
1993       if (util::strieq("backend-ipv"_sr, name, 11)) {
1994         return SHRPX_OPTID_BACKEND_IPV6;
1995       }
1996       break;
1997     case 'c':
1998       if (util::strieq("http2-altsv"_sr, name, 11)) {
1999         return SHRPX_OPTID_HTTP2_ALTSVC;
2000       }
2001       break;
2002     case 'e':
2003       if (util::strieq("host-rewrit"_sr, name, 11)) {
2004         return SHRPX_OPTID_HOST_REWRITE;
2005       }
2006       if (util::strieq("http2-bridg"_sr, name, 11)) {
2007         return SHRPX_OPTID_HTTP2_BRIDGE;
2008       }
2009       break;
2010     case 'p':
2011       if (util::strieq("ocsp-startu"_sr, name, 11)) {
2012         return SHRPX_OPTID_OCSP_STARTUP;
2013       }
2014       break;
2015     case 'y':
2016       if (util::strieq("client-prox"_sr, name, 11)) {
2017         return SHRPX_OPTID_CLIENT_PROXY;
2018       }
2019       if (util::strieq("forwarded-b"_sr, name, 11)) {
2020         return SHRPX_OPTID_FORWARDED_BY;
2021       }
2022       break;
2023     }
2024     break;
2025   case 13:
2026     switch (name[12]) {
2027     case 'd':
2028       if (util::strieq("add-forwarde"_sr, name, 12)) {
2029         return SHRPX_OPTID_ADD_FORWARDED;
2030       }
2031       if (util::strieq("single-threa"_sr, name, 12)) {
2032         return SHRPX_OPTID_SINGLE_THREAD;
2033       }
2034       break;
2035     case 'e':
2036       if (util::strieq("dh-param-fil"_sr, name, 12)) {
2037         return SHRPX_OPTID_DH_PARAM_FILE;
2038       }
2039       if (util::strieq("errorlog-fil"_sr, name, 12)) {
2040         return SHRPX_OPTID_ERRORLOG_FILE;
2041       }
2042       if (util::strieq("rlimit-nofil"_sr, name, 12)) {
2043         return SHRPX_OPTID_RLIMIT_NOFILE;
2044       }
2045       break;
2046     case 'r':
2047       if (util::strieq("forwarded-fo"_sr, name, 12)) {
2048         return SHRPX_OPTID_FORWARDED_FOR;
2049       }
2050       break;
2051     case 's':
2052       if (util::strieq("tls13-cipher"_sr, name, 12)) {
2053         return SHRPX_OPTID_TLS13_CIPHERS;
2054       }
2055       break;
2056     case 't':
2057       if (util::strieq("verify-clien"_sr, name, 12)) {
2058         return SHRPX_OPTID_VERIFY_CLIENT;
2059       }
2060       break;
2061     }
2062     break;
2063   case 14:
2064     switch (name[13]) {
2065     case 'd':
2066       if (util::strieq("quic-server-i"_sr, name, 13)) {
2067         return SHRPX_OPTID_QUIC_SERVER_ID;
2068       }
2069       break;
2070     case 'e':
2071       if (util::strieq("accesslog-fil"_sr, name, 13)) {
2072         return SHRPX_OPTID_ACCESSLOG_FILE;
2073       }
2074       break;
2075     case 'h':
2076       if (util::strieq("no-server-pus"_sr, name, 13)) {
2077         return SHRPX_OPTID_NO_SERVER_PUSH;
2078       }
2079       break;
2080     case 'k':
2081       if (util::strieq("rlimit-memloc"_sr, name, 13)) {
2082         return SHRPX_OPTID_RLIMIT_MEMLOCK;
2083       }
2084       break;
2085     case 'p':
2086       if (util::strieq("no-verify-ocs"_sr, name, 13)) {
2087         return SHRPX_OPTID_NO_VERIFY_OCSP;
2088       }
2089       break;
2090     case 's':
2091       if (util::strieq("backend-no-tl"_sr, name, 13)) {
2092         return SHRPX_OPTID_BACKEND_NO_TLS;
2093       }
2094       if (util::strieq("client-cipher"_sr, name, 13)) {
2095         return SHRPX_OPTID_CLIENT_CIPHERS;
2096       }
2097       if (util::strieq("single-proces"_sr, name, 13)) {
2098         return SHRPX_OPTID_SINGLE_PROCESS;
2099       }
2100       break;
2101     case 't':
2102       if (util::strieq("tls-proto-lis"_sr, name, 13)) {
2103         return SHRPX_OPTID_TLS_PROTO_LIST;
2104       }
2105       break;
2106     }
2107     break;
2108   case 15:
2109     switch (name[14]) {
2110     case 'e':
2111       if (util::strieq("no-host-rewrit"_sr, name, 14)) {
2112         return SHRPX_OPTID_NO_HOST_REWRITE;
2113       }
2114       break;
2115     case 'g':
2116       if (util::strieq("errorlog-syslo"_sr, name, 14)) {
2117         return SHRPX_OPTID_ERRORLOG_SYSLOG;
2118       }
2119       break;
2120     case 's':
2121       if (util::strieq("frontend-no-tl"_sr, name, 14)) {
2122         return SHRPX_OPTID_FRONTEND_NO_TLS;
2123       }
2124       break;
2125     case 'y':
2126       if (util::strieq("syslog-facilit"_sr, name, 14)) {
2127         return SHRPX_OPTID_SYSLOG_FACILITY;
2128       }
2129       break;
2130     }
2131     break;
2132   case 16:
2133     switch (name[15]) {
2134     case 'e':
2135       if (util::strieq("certificate-fil"_sr, name, 15)) {
2136         return SHRPX_OPTID_CERTIFICATE_FILE;
2137       }
2138       if (util::strieq("client-cert-fil"_sr, name, 15)) {
2139         return SHRPX_OPTID_CLIENT_CERT_FILE;
2140       }
2141       if (util::strieq("private-key-fil"_sr, name, 15)) {
2142         return SHRPX_OPTID_PRIVATE_KEY_FILE;
2143       }
2144       if (util::strieq("worker-read-rat"_sr, name, 15)) {
2145         return SHRPX_OPTID_WORKER_READ_RATE;
2146       }
2147       break;
2148     case 'g':
2149       if (util::strieq("accesslog-syslo"_sr, name, 15)) {
2150         return SHRPX_OPTID_ACCESSLOG_SYSLOG;
2151       }
2152       break;
2153     case 't':
2154       if (util::strieq("accesslog-forma"_sr, name, 15)) {
2155         return SHRPX_OPTID_ACCESSLOG_FORMAT;
2156       }
2157       break;
2158     }
2159     break;
2160   case 17:
2161     switch (name[16]) {
2162     case 'e':
2163       if (util::strieq("no-server-rewrit"_sr, name, 16)) {
2164         return SHRPX_OPTID_NO_SERVER_REWRITE;
2165       }
2166       if (util::strieq("worker-write-rat"_sr, name, 16)) {
2167         return SHRPX_OPTID_WORKER_WRITE_RATE;
2168       }
2169       break;
2170     case 's':
2171       if (util::strieq("backend-http1-tl"_sr, name, 16)) {
2172         return SHRPX_OPTID_BACKEND_HTTP1_TLS;
2173       }
2174       if (util::strieq("max-header-field"_sr, name, 16)) {
2175         return SHRPX_OPTID_MAX_HEADER_FIELDS;
2176       }
2177       break;
2178     case 't':
2179       if (util::strieq("dns-cache-timeou"_sr, name, 16)) {
2180         return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
2181       }
2182       if (util::strieq("worker-read-burs"_sr, name, 16)) {
2183         return SHRPX_OPTID_WORKER_READ_BURST;
2184       }
2185       break;
2186     }
2187     break;
2188   case 18:
2189     switch (name[17]) {
2190     case 'a':
2191       if (util::strieq("tls-max-early-dat"_sr, name, 17)) {
2192         return SHRPX_OPTID_TLS_MAX_EARLY_DATA;
2193       }
2194       break;
2195     case 'r':
2196       if (util::strieq("add-request-heade"_sr, name, 17)) {
2197         return SHRPX_OPTID_ADD_REQUEST_HEADER;
2198       }
2199       break;
2200     case 's':
2201       if (util::strieq("client-psk-secret"_sr, name, 17)) {
2202         return SHRPX_OPTID_CLIENT_PSK_SECRETS;
2203       }
2204       break;
2205     case 't':
2206       if (util::strieq("dns-lookup-timeou"_sr, name, 17)) {
2207         return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
2208       }
2209       if (util::strieq("worker-write-burs"_sr, name, 17)) {
2210         return SHRPX_OPTID_WORKER_WRITE_BURST;
2211       }
2212       break;
2213     }
2214     break;
2215   case 19:
2216     switch (name[18]) {
2217     case 'e':
2218       if (util::strieq("no-location-rewrit"_sr, name, 18)) {
2219         return SHRPX_OPTID_NO_LOCATION_REWRITE;
2220       }
2221       if (util::strieq("require-http-schem"_sr, name, 18)) {
2222         return SHRPX_OPTID_REQUIRE_HTTP_SCHEME;
2223       }
2224       if (util::strieq("tls-ticket-key-fil"_sr, name, 18)) {
2225         return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
2226       }
2227       break;
2228     case 'f':
2229       if (util::strieq("backend-max-backof"_sr, name, 18)) {
2230         return SHRPX_OPTID_BACKEND_MAX_BACKOFF;
2231       }
2232       break;
2233     case 'r':
2234       if (util::strieq("add-response-heade"_sr, name, 18)) {
2235         return SHRPX_OPTID_ADD_RESPONSE_HEADER;
2236       }
2237       if (util::strieq("add-x-forwarded-fo"_sr, name, 18)) {
2238         return SHRPX_OPTID_ADD_X_FORWARDED_FOR;
2239       }
2240       if (util::strieq("header-field-buffe"_sr, name, 18)) {
2241         return SHRPX_OPTID_HEADER_FIELD_BUFFER;
2242       }
2243       break;
2244     case 't':
2245       if (util::strieq("redirect-https-por"_sr, name, 18)) {
2246         return SHRPX_OPTID_REDIRECT_HTTPS_PORT;
2247       }
2248       if (util::strieq("stream-read-timeou"_sr, name, 18)) {
2249         return SHRPX_OPTID_STREAM_READ_TIMEOUT;
2250       }
2251       break;
2252     }
2253     break;
2254   case 20:
2255     switch (name[19]) {
2256     case 'g':
2257       if (util::strieq("frontend-frame-debu"_sr, name, 19)) {
2258         return SHRPX_OPTID_FRONTEND_FRAME_DEBUG;
2259       }
2260       break;
2261     case 'l':
2262       if (util::strieq("ocsp-update-interva"_sr, name, 19)) {
2263         return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
2264       }
2265       break;
2266     case 's':
2267       if (util::strieq("max-worker-processe"_sr, name, 19)) {
2268         return SHRPX_OPTID_MAX_WORKER_PROCESSES;
2269       }
2270       if (util::strieq("tls13-client-cipher"_sr, name, 19)) {
2271         return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
2272       }
2273       break;
2274     case 't':
2275       if (util::strieq("backend-read-timeou"_sr, name, 19)) {
2276         return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
2277       }
2278       if (util::strieq("stream-write-timeou"_sr, name, 19)) {
2279         return SHRPX_OPTID_STREAM_WRITE_TIMEOUT;
2280       }
2281       if (util::strieq("verify-client-cacer"_sr, name, 19)) {
2282         return SHRPX_OPTID_VERIFY_CLIENT_CACERT;
2283       }
2284       break;
2285     case 'y':
2286       if (util::strieq("api-max-request-bod"_sr, name, 19)) {
2287         return SHRPX_OPTID_API_MAX_REQUEST_BODY;
2288       }
2289       break;
2290     }
2291     break;
2292   case 21:
2293     switch (name[20]) {
2294     case 'd':
2295       if (util::strieq("backend-tls-sni-fiel"_sr, name, 20)) {
2296         return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
2297       }
2298       break;
2299     case 'e':
2300       if (util::strieq("quic-bpf-program-fil"_sr, name, 20)) {
2301         return SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE;
2302       }
2303       break;
2304     case 'l':
2305       if (util::strieq("accept-proxy-protoco"_sr, name, 20)) {
2306         return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
2307       }
2308       break;
2309     case 'n':
2310       if (util::strieq("tls-max-proto-versio"_sr, name, 20)) {
2311         return SHRPX_OPTID_TLS_MAX_PROTO_VERSION;
2312       }
2313       if (util::strieq("tls-min-proto-versio"_sr, name, 20)) {
2314         return SHRPX_OPTID_TLS_MIN_PROTO_VERSION;
2315       }
2316       break;
2317     case 'r':
2318       if (util::strieq("tls-ticket-key-ciphe"_sr, name, 20)) {
2319         return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
2320       }
2321       break;
2322     case 's':
2323       if (util::strieq("frontend-max-request"_sr, name, 20)) {
2324         return SHRPX_OPTID_FRONTEND_MAX_REQUESTS;
2325       }
2326       break;
2327     case 't':
2328       if (util::strieq("backend-write-timeou"_sr, name, 20)) {
2329         return SHRPX_OPTID_BACKEND_WRITE_TIMEOUT;
2330       }
2331       if (util::strieq("frontend-read-timeou"_sr, name, 20)) {
2332         return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
2333       }
2334       break;
2335     case 'y':
2336       if (util::strieq("accesslog-write-earl"_sr, name, 20)) {
2337         return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
2338       }
2339       break;
2340     }
2341     break;
2342   case 22:
2343     switch (name[21]) {
2344     case 'i':
2345       if (util::strieq("backend-http-proxy-ur"_sr, name, 21)) {
2346         return SHRPX_OPTID_BACKEND_HTTP_PROXY_URI;
2347       }
2348       break;
2349     case 'r':
2350       if (util::strieq("backend-request-buffe"_sr, name, 21)) {
2351         return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
2352       }
2353       if (util::strieq("frontend-quic-qlog-di"_sr, name, 21)) {
2354         return SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR;
2355       }
2356       break;
2357     case 't':
2358       if (util::strieq("frontend-write-timeou"_sr, name, 21)) {
2359         return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
2360       }
2361       break;
2362     case 'y':
2363       if (util::strieq("backend-address-famil"_sr, name, 21)) {
2364         return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
2365       }
2366       break;
2367     }
2368     break;
2369   case 23:
2370     switch (name[22]) {
2371     case 'e':
2372       if (util::strieq("client-private-key-fil"_sr, name, 22)) {
2373         return SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE;
2374       }
2375       if (util::strieq("private-key-passwd-fil"_sr, name, 22)) {
2376         return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
2377       }
2378       break;
2379     case 'g':
2380       if (util::strieq("frontend-quic-debug-lo"_sr, name, 22)) {
2381         return SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG;
2382       }
2383       break;
2384     case 'r':
2385       if (util::strieq("backend-response-buffe"_sr, name, 22)) {
2386         return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
2387       }
2388       break;
2389     case 't':
2390       if (util::strieq("backend-connect-timeou"_sr, name, 22)) {
2391         return SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT;
2392       }
2393       if (util::strieq("frontend-header-timeou"_sr, name, 22)) {
2394         return SHRPX_OPTID_FRONTEND_HEADER_TIMEOUT;
2395       }
2396       break;
2397     }
2398     break;
2399   case 24:
2400     switch (name[23]) {
2401     case 'a':
2402       if (util::strieq("frontend-quic-early-dat"_sr, name, 23)) {
2403         return SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA;
2404       }
2405       break;
2406     case 'd':
2407       if (util::strieq("strip-incoming-forwarde"_sr, name, 23)) {
2408         return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
2409       }
2410       if (util::strieq("tls-ticket-key-memcache"_sr, name, 23)) {
2411         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED;
2412       }
2413       break;
2414     case 'e':
2415       if (util::strieq("fetch-ocsp-response-fil"_sr, name, 23)) {
2416         return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
2417       }
2418       break;
2419     case 'o':
2420       if (util::strieq("no-add-x-forwarded-prot"_sr, name, 23)) {
2421         return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
2422       }
2423       break;
2424     case 't':
2425       if (util::strieq("listener-disable-timeou"_sr, name, 23)) {
2426         return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
2427       }
2428       if (util::strieq("tls-dyn-rec-idle-timeou"_sr, name, 23)) {
2429         return SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT;
2430       }
2431       break;
2432     }
2433     break;
2434   case 25:
2435     switch (name[24]) {
2436     case 'e':
2437       if (util::strieq("backend-http2-window-siz"_sr, name, 24)) {
2438         return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
2439       }
2440       if (util::strieq("frontend-quic-secret-fil"_sr, name, 24)) {
2441         return SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE;
2442       }
2443       break;
2444     case 'g':
2445       if (util::strieq("http2-no-cookie-crumblin"_sr, name, 24)) {
2446         return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING;
2447       }
2448       break;
2449     case 's':
2450       if (util::strieq("backend-http2-window-bit"_sr, name, 24)) {
2451         return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS;
2452       }
2453       if (util::strieq("max-request-header-field"_sr, name, 24)) {
2454         return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
2455       }
2456       break;
2457     case 't':
2458       if (util::strieq("frontend-quic-initial-rt"_sr, name, 24)) {
2459         return SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT;
2460       }
2461       break;
2462     }
2463     break;
2464   case 26:
2465     switch (name[25]) {
2466     case 'a':
2467       if (util::strieq("tls-no-postpone-early-dat"_sr, name, 25)) {
2468         return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA;
2469       }
2470       break;
2471     case 'e':
2472       if (util::strieq("frontend-http2-window-siz"_sr, name, 25)) {
2473         return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
2474       }
2475       if (util::strieq("frontend-http3-window-siz"_sr, name, 25)) {
2476         return SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE;
2477       }
2478       break;
2479     case 's':
2480       if (util::strieq("frontend-http2-window-bit"_sr, name, 25)) {
2481         return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
2482       }
2483       if (util::strieq("max-response-header-field"_sr, name, 25)) {
2484         return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS;
2485       }
2486       break;
2487     case 't':
2488       if (util::strieq("backend-keep-alive-timeou"_sr, name, 25)) {
2489         return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
2490       }
2491       if (util::strieq("frontend-quic-idle-timeou"_sr, name, 25)) {
2492         return SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT;
2493       }
2494       if (util::strieq("no-http2-cipher-black-lis"_sr, name, 25)) {
2495         return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
2496       }
2497       if (util::strieq("no-http2-cipher-block-lis"_sr, name, 25)) {
2498         return SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST;
2499       }
2500       break;
2501     }
2502     break;
2503   case 27:
2504     switch (name[26]) {
2505     case 'd':
2506       if (util::strieq("tls-session-cache-memcache"_sr, name, 26)) {
2507         return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
2508       }
2509       break;
2510     case 'n':
2511       if (util::strieq("frontend-quic-require-toke"_sr, name, 26)) {
2512         return SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN;
2513       }
2514       break;
2515     case 'r':
2516       if (util::strieq("request-header-field-buffe"_sr, name, 26)) {
2517         return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
2518       }
2519       break;
2520     case 's':
2521       if (util::strieq("worker-frontend-connection"_sr, name, 26)) {
2522         return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS;
2523       }
2524       break;
2525     case 't':
2526       if (util::strieq("frontend-http2-idle-timeou"_sr, name, 26)) {
2527         return SHRPX_OPTID_FRONTEND_HTTP2_IDLE_TIMEOUT;
2528       }
2529       if (util::strieq("frontend-http2-read-timeou"_sr, name, 26)) {
2530         return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
2531       }
2532       if (util::strieq("frontend-http3-idle-timeou"_sr, name, 26)) {
2533         return SHRPX_OPTID_FRONTEND_HTTP3_IDLE_TIMEOUT;
2534       }
2535       if (util::strieq("frontend-http3-read-timeou"_sr, name, 26)) {
2536         return SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT;
2537       }
2538       if (util::strieq("frontend-keep-alive-timeou"_sr, name, 26)) {
2539         return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
2540       }
2541       break;
2542     }
2543     break;
2544   case 28:
2545     switch (name[27]) {
2546     case 'a':
2547       if (util::strieq("no-strip-incoming-early-dat"_sr, name, 27)) {
2548         return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA;
2549       }
2550       break;
2551     case 'd':
2552       if (util::strieq("tls-dyn-rec-warmup-threshol"_sr, name, 27)) {
2553         return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
2554       }
2555       break;
2556     case 'r':
2557       if (util::strieq("response-header-field-buffe"_sr, name, 27)) {
2558         return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER;
2559       }
2560       break;
2561     case 's':
2562       if (util::strieq("http2-max-concurrent-stream"_sr, name, 27)) {
2563         return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS;
2564       }
2565       if (util::strieq("tls-ticket-key-memcached-tl"_sr, name, 27)) {
2566         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS;
2567       }
2568       break;
2569     case 't':
2570       if (util::strieq("backend-connections-per-hos"_sr, name, 27)) {
2571         return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST;
2572       }
2573       break;
2574     }
2575     break;
2576   case 30:
2577     switch (name[29]) {
2578     case 'd':
2579       if (util::strieq("verify-client-tolerate-expire"_sr, name, 29)) {
2580         return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
2581       }
2582       break;
2583     case 'e':
2584       if (util::strieq("frontend-http3-max-window-siz"_sr, name, 29)) {
2585         return SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE;
2586       }
2587       break;
2588     case 'r':
2589       if (util::strieq("ignore-per-pattern-mruby-erro"_sr, name, 29)) {
2590         return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
2591       }
2592       if (util::strieq("strip-incoming-x-forwarded-fo"_sr, name, 29)) {
2593         return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
2594       }
2595       break;
2596     case 't':
2597       if (util::strieq("backend-http2-settings-timeou"_sr, name, 29)) {
2598         return SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT;
2599       }
2600       break;
2601     }
2602     break;
2603   case 31:
2604     switch (name[30]) {
2605     case 's':
2606       if (util::strieq("tls-session-cache-memcached-tl"_sr, name, 30)) {
2607         return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS;
2608       }
2609       break;
2610     case 't':
2611       if (util::strieq("frontend-http2-settings-timeou"_sr, name, 30)) {
2612         return SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT;
2613       }
2614       break;
2615     }
2616     break;
2617   case 32:
2618     switch (name[31]) {
2619     case 'd':
2620       if (util::strieq("backend-connections-per-fronten"_sr, name, 31)) {
2621         return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND;
2622       }
2623       break;
2624     }
2625     break;
2626   case 33:
2627     switch (name[32]) {
2628     case 'l':
2629       if (util::strieq("tls-ticket-key-memcached-interva"_sr, name, 32)) {
2630         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL;
2631       }
2632       if (util::strieq("tls-ticket-key-memcached-max-fai"_sr, name, 32)) {
2633         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
2634       }
2635       break;
2636     case 't':
2637       if (util::strieq("client-no-http2-cipher-black-lis"_sr, name, 32)) {
2638         return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
2639       }
2640       if (util::strieq("client-no-http2-cipher-block-lis"_sr, name, 32)) {
2641         return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST;
2642       }
2643       break;
2644     }
2645     break;
2646   case 34:
2647     switch (name[33]) {
2648     case 'e':
2649       if (util::strieq("tls-ticket-key-memcached-cert-fil"_sr, name, 33)) {
2650         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE;
2651       }
2652       break;
2653     case 'r':
2654       if (util::strieq("frontend-http2-dump-request-heade"_sr, name, 33)) {
2655         return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
2656       }
2657       break;
2658     case 't':
2659       if (util::strieq("backend-http1-connections-per-hos"_sr, name, 33)) {
2660         return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST;
2661       }
2662       break;
2663     case 'y':
2664       if (util::strieq("tls-ticket-key-memcached-max-retr"_sr, name, 33)) {
2665         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY;
2666       }
2667       break;
2668     }
2669     break;
2670   case 35:
2671     switch (name[34]) {
2672     case 'e':
2673       if (util::strieq("frontend-http2-optimize-window-siz"_sr, name, 34)) {
2674         return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
2675       }
2676       break;
2677     case 'o':
2678       if (util::strieq("no-strip-incoming-x-forwarded-prot"_sr, name, 34)) {
2679         return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
2680       }
2681       break;
2682     case 'r':
2683       if (util::strieq("frontend-http2-dump-response-heade"_sr, name, 34)) {
2684         return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
2685       }
2686       if (util::strieq("frontend-quic-congestion-controlle"_sr, name, 34)) {
2687         return SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER;
2688       }
2689       break;
2690     }
2691     break;
2692   case 36:
2693     switch (name[35]) {
2694     case 'd':
2695       if (util::strieq("worker-process-grace-shutdown-perio"_sr, name, 35)) {
2696         return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD;
2697       }
2698       break;
2699     case 'e':
2700       if (util::strieq("backend-http2-connection-window-siz"_sr, name, 35)) {
2701         return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
2702       }
2703       break;
2704     case 'r':
2705       if (util::strieq("backend-http2-connections-per-worke"_sr, name, 35)) {
2706         return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER;
2707       }
2708       break;
2709     case 's':
2710       if (util::strieq("backend-http2-connection-window-bit"_sr, name, 35)) {
2711         return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS;
2712       }
2713       if (util::strieq("backend-http2-max-concurrent-stream"_sr, name, 35)) {
2714         return SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS;
2715       }
2716       break;
2717     }
2718     break;
2719   case 37:
2720     switch (name[36]) {
2721     case 'e':
2722       if (util::strieq("frontend-http2-connection-window-siz"_sr, name, 36)) {
2723         return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
2724       }
2725       if (util::strieq("frontend-http3-connection-window-siz"_sr, name, 36)) {
2726         return SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE;
2727       }
2728       if (util::strieq("tls-session-cache-memcached-cert-fil"_sr, name, 36)) {
2729         return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
2730       }
2731       break;
2732     case 's':
2733       if (util::strieq("frontend-http2-connection-window-bit"_sr, name, 36)) {
2734         return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
2735       }
2736       if (util::strieq("frontend-http2-max-concurrent-stream"_sr, name, 36)) {
2737         return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
2738       }
2739       if (util::strieq("frontend-http3-max-concurrent-stream"_sr, name, 36)) {
2740         return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS;
2741       }
2742       break;
2743     }
2744     break;
2745   case 38:
2746     switch (name[37]) {
2747     case 'd':
2748       if (util::strieq("backend-http1-connections-per-fronten"_sr, name, 37)) {
2749         return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND;
2750       }
2751       break;
2752     }
2753     break;
2754   case 39:
2755     switch (name[38]) {
2756     case 'y':
2757       if (util::strieq("tls-ticket-key-memcached-address-famil"_sr, name, 38)) {
2758         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY;
2759       }
2760       break;
2761     }
2762     break;
2763   case 40:
2764     switch (name[39]) {
2765     case 'e':
2766       if (util::strieq("backend-http2-decoder-dynamic-table-siz"_sr, name,
2767                        39)) {
2768         return SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
2769       }
2770       if (util::strieq("backend-http2-encoder-dynamic-table-siz"_sr, name,
2771                        39)) {
2772         return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
2773       }
2774       break;
2775     }
2776     break;
2777   case 41:
2778     switch (name[40]) {
2779     case 'e':
2780       if (util::strieq("frontend-http2-decoder-dynamic-table-siz"_sr, name,
2781                        40)) {
2782         return SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
2783       }
2784       if (util::strieq("frontend-http2-encoder-dynamic-table-siz"_sr, name,
2785                        40)) {
2786         return SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
2787       }
2788       if (util::strieq("frontend-http2-optimize-write-buffer-siz"_sr, name,
2789                        40)) {
2790         return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
2791       }
2792       if (util::strieq("frontend-http3-max-connection-window-siz"_sr, name,
2793                        40)) {
2794         return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE;
2795       }
2796       if (util::strieq("tls-ticket-key-memcached-private-key-fil"_sr, name,
2797                        40)) {
2798         return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
2799       }
2800       break;
2801     }
2802     break;
2803   case 42:
2804     switch (name[41]) {
2805     case 'y':
2806       if (util::strieq("tls-session-cache-memcached-address-famil"_sr, name,
2807                        41)) {
2808         return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY;
2809       }
2810       break;
2811     }
2812     break;
2813   case 44:
2814     switch (name[43]) {
2815     case 'e':
2816       if (util::strieq("tls-session-cache-memcached-private-key-fil"_sr, name,
2817                        43)) {
2818         return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE;
2819       }
2820       break;
2821     }
2822     break;
2823   }
2824   return -1;
2825 }
2826 
parse_config(Config * config,const StringRef & opt,const StringRef & optarg,std::set<StringRef> & included_set,std::map<StringRef,size_t> & pattern_addr_indexer)2827 int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
2828                  std::set<StringRef> &included_set,
2829                  std::map<StringRef, size_t> &pattern_addr_indexer) {
2830   auto optid = option_lookup_token(opt);
2831   return parse_config(config, optid, opt, optarg, included_set,
2832                       pattern_addr_indexer);
2833 }
2834 
parse_config(Config * config,int optid,const StringRef & opt,const StringRef & optarg,std::set<StringRef> & included_set,std::map<StringRef,size_t> & pattern_addr_indexer)2835 int parse_config(Config *config, int optid, const StringRef &opt,
2836                  const StringRef &optarg, std::set<StringRef> &included_set,
2837                  std::map<StringRef, size_t> &pattern_addr_indexer) {
2838   std::array<char, STRERROR_BUFSIZE> errbuf;
2839   char host[NI_MAXHOST];
2840   uint16_t port;
2841 
2842   switch (optid) {
2843   case SHRPX_OPTID_BACKEND: {
2844     auto &downstreamconf = *config->conn.downstream;
2845     auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
2846 
2847     DownstreamAddrConfig addr{};
2848     if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
2849       auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
2850       addr.host =
2851         make_string_ref(downstreamconf.balloc, StringRef{path, addr_end});
2852       addr.host_unix = true;
2853     } else {
2854       if (split_host_port(host, sizeof(host), &port,
2855                           StringRef{std::begin(optarg), addr_end}, opt) == -1) {
2856         return -1;
2857       }
2858 
2859       addr.host = make_string_ref(downstreamconf.balloc, StringRef{host});
2860       addr.port = port;
2861     }
2862 
2863     auto mapping = addr_end == std::end(optarg) ? addr_end : addr_end + 1;
2864     auto mapping_end = std::find(mapping, std::end(optarg), ';');
2865 
2866     auto params =
2867       mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1;
2868 
2869     if (parse_mapping(config, addr, pattern_addr_indexer,
2870                       StringRef{mapping, mapping_end},
2871                       StringRef{params, std::end(optarg)}) != 0) {
2872       return -1;
2873     }
2874 
2875     return 0;
2876   }
2877   case SHRPX_OPTID_FRONTEND: {
2878     auto &apiconf = config->api;
2879 
2880     auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
2881     auto src_params = StringRef{addr_end, std::end(optarg)};
2882 
2883     UpstreamParams params{};
2884     params.tls = true;
2885 
2886     if (parse_upstream_params(params, src_params) != 0) {
2887       return -1;
2888     }
2889 
2890     if (params.sni_fwd && !params.tls) {
2891       LOG(ERROR) << "frontend: sni_fwd requires tls";
2892       return -1;
2893     }
2894 
2895     if (params.quic) {
2896       if (params.alt_mode != UpstreamAltMode::NONE) {
2897         LOG(ERROR) << "frontend: api or healthmon cannot be used with quic";
2898         return -1;
2899       }
2900 
2901       if (!params.tls) {
2902         LOG(ERROR) << "frontend: quic requires TLS";
2903         return -1;
2904       }
2905     }
2906 
2907     UpstreamAddr addr{};
2908     addr.fd = -1;
2909     addr.tls = params.tls;
2910     addr.sni_fwd = params.sni_fwd;
2911     addr.alt_mode = params.alt_mode;
2912     addr.accept_proxy_protocol = params.proxyproto;
2913     addr.quic = params.quic;
2914 
2915     if (addr.alt_mode == UpstreamAltMode::API) {
2916       apiconf.enabled = true;
2917     }
2918 
2919 #ifdef ENABLE_HTTP3
2920     auto &addrs = params.quic ? config->conn.quic_listener.addrs
2921                               : config->conn.listener.addrs;
2922 #else  // !ENABLE_HTTP3
2923     auto &addrs = config->conn.listener.addrs;
2924 #endif // !ENABLE_HTTP3
2925 
2926     if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
2927       if (addr.quic) {
2928         LOG(ERROR) << "frontend: quic cannot be used on UNIX domain socket";
2929         return -1;
2930       }
2931 
2932       auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
2933       addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
2934       addr.host_unix = true;
2935       addr.index = addrs.size();
2936 
2937       addrs.push_back(std::move(addr));
2938 
2939       return 0;
2940     }
2941 
2942     if (split_host_port(host, sizeof(host), &port,
2943                         StringRef{std::begin(optarg), addr_end}, opt) == -1) {
2944       return -1;
2945     }
2946 
2947     addr.host = make_string_ref(config->balloc, StringRef{host});
2948     addr.port = port;
2949 
2950     if (util::numeric_host(host, AF_INET)) {
2951       addr.family = AF_INET;
2952       addr.index = addrs.size();
2953       addrs.push_back(std::move(addr));
2954       return 0;
2955     }
2956 
2957     if (util::numeric_host(host, AF_INET6)) {
2958       addr.family = AF_INET6;
2959       addr.index = addrs.size();
2960       addrs.push_back(std::move(addr));
2961       return 0;
2962     }
2963 
2964     addr.family = AF_INET;
2965     addr.index = addrs.size();
2966     addrs.push_back(addr);
2967 
2968     addr.family = AF_INET6;
2969     addr.index = addrs.size();
2970     addrs.push_back(std::move(addr));
2971 
2972     return 0;
2973   }
2974   case SHRPX_OPTID_WORKERS: {
2975 #ifdef NOTHREADS
2976     LOG(WARN) << "Threading disabled at build time, no threads created.";
2977     return 0;
2978 #else  // !NOTHREADS
2979     size_t n;
2980 
2981     if (parse_uint(&n, opt, optarg) != 0) {
2982       return -1;
2983     }
2984 
2985     if (n > 65530) {
2986       LOG(ERROR) << opt << ": the number of workers must not exceed 65530";
2987 
2988       return -1;
2989     }
2990 
2991     config->num_worker = n;
2992 
2993     return 0;
2994 #endif // !NOTHREADS
2995   }
2996   case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: {
2997     LOG(WARN) << opt << ": deprecated. Use "
2998               << SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS << " and "
2999               << SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS << " instead.";
3000     size_t n;
3001     if (parse_uint(&n, opt, optarg) != 0) {
3002       return -1;
3003     }
3004     auto &http2conf = config->http2;
3005     http2conf.upstream.max_concurrent_streams = n;
3006     http2conf.downstream.max_concurrent_streams = n;
3007 
3008     return 0;
3009   }
3010   case SHRPX_OPTID_LOG_LEVEL: {
3011     auto level = Log::get_severity_level_by_name(optarg);
3012     if (level == -1) {
3013       LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
3014       return -1;
3015     }
3016     config->logging.severity = level;
3017 
3018     return 0;
3019   }
3020   case SHRPX_OPTID_DAEMON:
3021     config->daemon = util::strieq("yes"_sr, optarg);
3022 
3023     return 0;
3024   case SHRPX_OPTID_HTTP2_PROXY:
3025     config->http2_proxy = util::strieq("yes"_sr, optarg);
3026 
3027     return 0;
3028   case SHRPX_OPTID_HTTP2_BRIDGE:
3029     LOG(ERROR) << opt
3030                << ": deprecated.  Use backend=<addr>,<port>;;proto=h2;tls";
3031     return -1;
3032   case SHRPX_OPTID_CLIENT_PROXY:
3033     LOG(ERROR)
3034       << opt
3035       << ": deprecated.  Use http2-proxy, frontend=<addr>,<port>;no-tls "
3036          "and backend=<addr>,<port>;;proto=h2;tls";
3037     return -1;
3038   case SHRPX_OPTID_ADD_X_FORWARDED_FOR:
3039     config->http.xff.add = util::strieq("yes"_sr, optarg);
3040 
3041     return 0;
3042   case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR:
3043     config->http.xff.strip_incoming = util::strieq("yes"_sr, optarg);
3044 
3045     return 0;
3046   case SHRPX_OPTID_NO_VIA:
3047     config->http.no_via = util::strieq("yes"_sr, optarg);
3048 
3049     return 0;
3050   case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT:
3051     LOG(WARN) << opt << ": deprecated.  Use frontend-http2-idle-timeout";
3052     // fall through
3053   case SHRPX_OPTID_FRONTEND_HTTP2_IDLE_TIMEOUT:
3054     return parse_duration(&config->conn.upstream.timeout.http2_idle, opt,
3055                           optarg);
3056   case SHRPX_OPTID_FRONTEND_READ_TIMEOUT:
3057     LOG(WARN) << opt << ": deprecated.  Use frontend-header-timeout";
3058 
3059     return 0;
3060   case SHRPX_OPTID_FRONTEND_HEADER_TIMEOUT:
3061     return parse_duration(&config->http.timeout.header, opt, optarg);
3062   case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT:
3063     return parse_duration(&config->conn.upstream.timeout.write, opt, optarg);
3064   case SHRPX_OPTID_BACKEND_READ_TIMEOUT:
3065     return parse_duration(&config->conn.downstream->timeout.read, opt, optarg);
3066   case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT:
3067     return parse_duration(&config->conn.downstream->timeout.write, opt, optarg);
3068   case SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT:
3069     return parse_duration(&config->conn.downstream->timeout.connect, opt,
3070                           optarg);
3071   case SHRPX_OPTID_STREAM_READ_TIMEOUT:
3072     return parse_duration(&config->http2.timeout.stream_read, opt, optarg);
3073   case SHRPX_OPTID_STREAM_WRITE_TIMEOUT:
3074     return parse_duration(&config->http2.timeout.stream_write, opt, optarg);
3075   case SHRPX_OPTID_ACCESSLOG_FILE:
3076     config->logging.access.file = make_string_ref(config->balloc, optarg);
3077 
3078     return 0;
3079   case SHRPX_OPTID_ACCESSLOG_SYSLOG:
3080     config->logging.access.syslog = util::strieq("yes"_sr, optarg);
3081 
3082     return 0;
3083   case SHRPX_OPTID_ACCESSLOG_FORMAT:
3084     config->logging.access.format = parse_log_format(config->balloc, optarg);
3085 
3086     return 0;
3087   case SHRPX_OPTID_ERRORLOG_FILE:
3088     config->logging.error.file = make_string_ref(config->balloc, optarg);
3089 
3090     return 0;
3091   case SHRPX_OPTID_ERRORLOG_SYSLOG:
3092     config->logging.error.syslog = util::strieq("yes"_sr, optarg);
3093 
3094     return 0;
3095   case SHRPX_OPTID_FASTOPEN:
3096     return parse_uint(&config->conn.listener.fastopen, opt, optarg);
3097   case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT:
3098     return parse_duration(&config->conn.downstream->timeout.idle_read, opt,
3099                           optarg);
3100   case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS:
3101   case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: {
3102     LOG(WARN) << opt << ": deprecated.  Use "
3103               << (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS
3104                     ? SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE
3105                     : SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE);
3106     int32_t *resp;
3107 
3108     if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) {
3109       resp = &config->http2.upstream.window_size;
3110     } else {
3111       resp = &config->http2.downstream.window_size;
3112     }
3113 
3114     errno = 0;
3115 
3116     int n;
3117 
3118     if (parse_uint(&n, opt, optarg) != 0) {
3119       return -1;
3120     }
3121 
3122     if (n >= 31) {
3123       LOG(ERROR) << opt
3124                  << ": specify the integer in the range [0, 30], inclusive";
3125       return -1;
3126     }
3127 
3128     // Make 16 bits to the HTTP/2 default 64KiB - 1.  This is the same
3129     // behaviour of previous code.
3130     *resp = (1 << n) - 1;
3131 
3132     return 0;
3133   }
3134   case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS:
3135   case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: {
3136     LOG(WARN) << opt << ": deprecated.  Use "
3137               << (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS
3138                     ? SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE
3139                     : SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE);
3140     int32_t *resp;
3141 
3142     if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) {
3143       resp = &config->http2.upstream.connection_window_size;
3144     } else {
3145       resp = &config->http2.downstream.connection_window_size;
3146     }
3147 
3148     errno = 0;
3149 
3150     int n;
3151 
3152     if (parse_uint(&n, opt, optarg) != 0) {
3153       return -1;
3154     }
3155 
3156     if (n < 16 || n >= 31) {
3157       LOG(ERROR) << opt
3158                  << ": specify the integer in the range [16, 30], inclusive";
3159       return -1;
3160     }
3161 
3162     *resp = (1 << n) - 1;
3163 
3164     return 0;
3165   }
3166   case SHRPX_OPTID_FRONTEND_NO_TLS:
3167     LOG(WARN) << opt << ": deprecated.  Use no-tls keyword in "
3168               << SHRPX_OPT_FRONTEND;
3169     return 0;
3170   case SHRPX_OPTID_BACKEND_NO_TLS:
3171     LOG(WARN) << opt
3172               << ": deprecated.  backend connection is not encrypted by "
3173                  "default.  See also "
3174               << SHRPX_OPT_BACKEND_TLS;
3175     return 0;
3176   case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
3177     LOG(WARN) << opt
3178               << ": deprecated.  Use sni keyword in --backend option.  "
3179                  "For now, all sni values of all backends are "
3180                  "overridden by the given value "
3181               << optarg;
3182     config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
3183 
3184     return 0;
3185   case SHRPX_OPTID_PID_FILE:
3186     config->pid_file = make_string_ref(config->balloc, optarg);
3187 
3188     return 0;
3189   case SHRPX_OPTID_USER: {
3190     auto pwd = getpwnam(optarg.data());
3191     if (!pwd) {
3192       LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
3193                  << xsi_strerror(errno, errbuf.data(), errbuf.size());
3194       return -1;
3195     }
3196     config->user = make_string_ref(config->balloc, StringRef{pwd->pw_name});
3197     config->uid = pwd->pw_uid;
3198     config->gid = pwd->pw_gid;
3199 
3200     return 0;
3201   }
3202   case SHRPX_OPTID_PRIVATE_KEY_FILE:
3203     config->tls.private_key_file = make_string_ref(config->balloc, optarg);
3204 
3205     return 0;
3206   case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
3207     auto passwd = read_passwd_from_file(opt, optarg);
3208     if (passwd.empty()) {
3209       LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
3210       return -1;
3211     }
3212     config->tls.private_key_passwd =
3213       make_string_ref(config->balloc, StringRef{passwd});
3214 
3215     return 0;
3216   }
3217   case SHRPX_OPTID_CERTIFICATE_FILE:
3218     config->tls.cert_file = make_string_ref(config->balloc, optarg);
3219 
3220     return 0;
3221   case SHRPX_OPTID_DH_PARAM_FILE:
3222     config->tls.dh_param_file = make_string_ref(config->balloc, optarg);
3223 
3224     return 0;
3225   case SHRPX_OPTID_SUBCERT: {
3226     auto end_keys = std::find(std::begin(optarg), std::end(optarg), ';');
3227     auto src_params = StringRef{end_keys, std::end(optarg)};
3228 
3229     SubcertParams params;
3230     if (parse_subcert_params(params, src_params) != 0) {
3231       return -1;
3232     }
3233 
3234     std::vector<uint8_t> sct_data;
3235 
3236     if (!params.sct_dir.empty()) {
3237       // Make sure that dir_path is NULL terminated string.
3238       if (read_tls_sct_from_dir(sct_data, opt,
3239                                 StringRef{std::string{params.sct_dir}}) != 0) {
3240         return -1;
3241       }
3242     }
3243 
3244     // Private Key file and certificate file separated by ':'.
3245     auto sp = std::find(std::begin(optarg), end_keys, ':');
3246     if (sp == end_keys) {
3247       LOG(ERROR) << opt << ": missing ':' in "
3248                  << StringRef{std::begin(optarg), end_keys};
3249       return -1;
3250     }
3251 
3252     auto private_key_file = StringRef{std::begin(optarg), sp};
3253 
3254     if (private_key_file.empty()) {
3255       LOG(ERROR) << opt << ": missing private key file: "
3256                  << StringRef{std::begin(optarg), end_keys};
3257       return -1;
3258     }
3259 
3260     auto cert_file = StringRef{sp + 1, end_keys};
3261 
3262     if (cert_file.empty()) {
3263       LOG(ERROR) << opt << ": missing certificate file: "
3264                  << StringRef{std::begin(optarg), end_keys};
3265       return -1;
3266     }
3267 
3268     config->tls.subcerts.emplace_back(
3269       make_string_ref(config->balloc, private_key_file),
3270       make_string_ref(config->balloc, cert_file), std::move(sct_data));
3271 
3272     return 0;
3273   }
3274   case SHRPX_OPTID_SYSLOG_FACILITY: {
3275     int facility = int_syslog_facility(optarg);
3276     if (facility == -1) {
3277       LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
3278       return -1;
3279     }
3280     config->logging.syslog_facility = facility;
3281 
3282     return 0;
3283   }
3284   case SHRPX_OPTID_BACKLOG:
3285     return parse_uint(&config->conn.listener.backlog, opt, optarg);
3286   case SHRPX_OPTID_CIPHERS:
3287     config->tls.ciphers = make_string_ref(config->balloc, optarg);
3288 
3289     return 0;
3290   case SHRPX_OPTID_TLS13_CIPHERS:
3291     config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg);
3292 
3293     return 0;
3294   case SHRPX_OPTID_CLIENT:
3295     LOG(ERROR) << opt
3296                << ": deprecated.  Use frontend=<addr>,<port>;no-tls, "
3297                   "backend=<addr>,<port>;;proto=h2;tls";
3298     return -1;
3299   case SHRPX_OPTID_INSECURE:
3300     config->tls.insecure = util::strieq("yes"_sr, optarg);
3301 
3302     return 0;
3303   case SHRPX_OPTID_CACERT:
3304     config->tls.cacert = make_string_ref(config->balloc, optarg);
3305 
3306     return 0;
3307   case SHRPX_OPTID_BACKEND_IPV4:
3308     LOG(WARN) << opt
3309               << ": deprecated.  Use backend-address-family=IPv4 instead.";
3310 
3311     config->conn.downstream->family = AF_INET;
3312 
3313     return 0;
3314   case SHRPX_OPTID_BACKEND_IPV6:
3315     LOG(WARN) << opt
3316               << ": deprecated.  Use backend-address-family=IPv6 instead.";
3317 
3318     config->conn.downstream->family = AF_INET6;
3319 
3320     return 0;
3321   case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
3322     auto &proxy = config->downstream_http_proxy;
3323     // Reset here so that multiple option occurrence does not merge
3324     // the results.
3325     proxy = {};
3326     // parse URI and get hostname, port and optionally userinfo.
3327     http_parser_url u{};
3328     int rv = http_parser_parse_url(optarg.data(), optarg.size(), 0, &u);
3329     if (rv == 0) {
3330       if (u.field_set & UF_USERINFO) {
3331         auto uf = util::get_uri_field(optarg.data(), u, UF_USERINFO);
3332         // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
3333         // userinfo component is empty string.
3334         if (!uf.empty()) {
3335           proxy.userinfo = util::percent_decode(config->balloc, uf);
3336         }
3337       }
3338       if (u.field_set & UF_HOST) {
3339         proxy.host = make_string_ref(
3340           config->balloc, util::get_uri_field(optarg.data(), u, UF_HOST));
3341       } else {
3342         LOG(ERROR) << opt << ": no hostname specified";
3343         return -1;
3344       }
3345       if (u.field_set & UF_PORT) {
3346         proxy.port = u.port;
3347       } else {
3348         LOG(ERROR) << opt << ": no port specified";
3349         return -1;
3350       }
3351     } else {
3352       LOG(ERROR) << opt << ": parse error";
3353       return -1;
3354     }
3355 
3356     return 0;
3357   }
3358   case SHRPX_OPTID_READ_RATE:
3359     return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.rate, opt,
3360                                 optarg);
3361   case SHRPX_OPTID_READ_BURST:
3362     return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.burst,
3363                                 opt, optarg);
3364   case SHRPX_OPTID_WRITE_RATE:
3365     return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.rate,
3366                                 opt, optarg);
3367   case SHRPX_OPTID_WRITE_BURST:
3368     return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.burst,
3369                                 opt, optarg);
3370   case SHRPX_OPTID_WORKER_READ_RATE:
3371     LOG(WARN) << opt << ": not implemented yet";
3372     return 0;
3373   case SHRPX_OPTID_WORKER_READ_BURST:
3374     LOG(WARN) << opt << ": not implemented yet";
3375     return 0;
3376   case SHRPX_OPTID_WORKER_WRITE_RATE:
3377     LOG(WARN) << opt << ": not implemented yet";
3378     return 0;
3379   case SHRPX_OPTID_WORKER_WRITE_BURST:
3380     LOG(WARN) << opt << ": not implemented yet";
3381     return 0;
3382   case SHRPX_OPTID_TLS_PROTO_LIST: {
3383     LOG(WARN) << opt
3384               << ": deprecated.  Use tls-min-proto-version and "
3385                  "tls-max-proto-version instead.";
3386     auto list = util::split_str(optarg, ',');
3387     config->tls.tls_proto_list.resize(list.size());
3388     for (size_t i = 0; i < list.size(); ++i) {
3389       config->tls.tls_proto_list[i] = make_string_ref(config->balloc, list[i]);
3390     }
3391 
3392     return 0;
3393   }
3394   case SHRPX_OPTID_VERIFY_CLIENT:
3395     config->tls.client_verify.enabled = util::strieq("yes"_sr, optarg);
3396 
3397     return 0;
3398   case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
3399     config->tls.client_verify.cacert = make_string_ref(config->balloc, optarg);
3400 
3401     return 0;
3402   case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
3403     config->tls.client.private_key_file =
3404       make_string_ref(config->balloc, optarg);
3405 
3406     return 0;
3407   case SHRPX_OPTID_CLIENT_CERT_FILE:
3408     config->tls.client.cert_file = make_string_ref(config->balloc, optarg);
3409 
3410     return 0;
3411   case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
3412     config->http2.upstream.debug.dump.request_header_file =
3413       make_string_ref(config->balloc, optarg);
3414 
3415     return 0;
3416   case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
3417     config->http2.upstream.debug.dump.response_header_file =
3418       make_string_ref(config->balloc, optarg);
3419 
3420     return 0;
3421   case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
3422     config->http2.no_cookie_crumbling = util::strieq("yes"_sr, optarg);
3423 
3424     return 0;
3425   case SHRPX_OPTID_FRONTEND_FRAME_DEBUG:
3426     config->http2.upstream.debug.frame_debug = util::strieq("yes"_sr, optarg);
3427 
3428     return 0;
3429   case SHRPX_OPTID_PADDING:
3430     return parse_uint(&config->padding, opt, optarg);
3431   case SHRPX_OPTID_ALTSVC: {
3432     AltSvc altsvc{};
3433 
3434     if (parse_altsvc(altsvc, opt, optarg) != 0) {
3435       return -1;
3436     }
3437 
3438     config->http.altsvcs.push_back(std::move(altsvc));
3439 
3440     return 0;
3441   }
3442   case SHRPX_OPTID_ADD_REQUEST_HEADER:
3443   case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
3444     auto p = parse_header(config->balloc, optarg);
3445     if (p.name.empty()) {
3446       LOG(ERROR) << opt << ": invalid header field: " << optarg;
3447       return -1;
3448     }
3449     if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) {
3450       config->http.add_request_headers.push_back(std::move(p));
3451     } else {
3452       config->http.add_response_headers.push_back(std::move(p));
3453     }
3454     return 0;
3455   }
3456   case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS:
3457     return parse_uint(&config->conn.upstream.worker_connections, opt, optarg);
3458   case SHRPX_OPTID_NO_LOCATION_REWRITE:
3459     config->http.no_location_rewrite = util::strieq("yes"_sr, optarg);
3460 
3461     return 0;
3462   case SHRPX_OPTID_NO_HOST_REWRITE:
3463     LOG(WARN) << SHRPX_OPT_NO_HOST_REWRITE
3464               << ": deprecated.  :authority and host header fields are NOT "
3465                  "altered by default.  To rewrite these headers, use "
3466                  "--host-rewrite option.";
3467 
3468     return 0;
3469   case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST:
3470     LOG(WARN) << opt
3471               << ": deprecated.  Use backend-connections-per-host instead.";
3472   // fall through
3473   case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST: {
3474     int n;
3475 
3476     if (parse_uint(&n, opt, optarg) != 0) {
3477       return -1;
3478     }
3479 
3480     if (n == 0) {
3481       LOG(ERROR) << opt << ": specify an integer strictly more than 0";
3482 
3483       return -1;
3484     }
3485 
3486     config->conn.downstream->connections_per_host = n;
3487 
3488     return 0;
3489   }
3490   case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND:
3491     LOG(WARN) << opt << ": deprecated.  Use "
3492               << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead.";
3493   // fall through
3494   case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND:
3495     return parse_uint(&config->conn.downstream->connections_per_frontend, opt,
3496                       optarg);
3497   case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
3498     return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg);
3499   case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
3500     config->tls.ticket.files.emplace_back(
3501       make_string_ref(config->balloc, optarg));
3502     return 0;
3503   case SHRPX_OPTID_RLIMIT_NOFILE: {
3504     int n;
3505 
3506     if (parse_uint(&n, opt, optarg) != 0) {
3507       return -1;
3508     }
3509 
3510     if (n < 0) {
3511       LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
3512 
3513       return -1;
3514     }
3515 
3516     config->rlimit_nofile = n;
3517 
3518     return 0;
3519   }
3520   case SHRPX_OPTID_BACKEND_REQUEST_BUFFER:
3521   case SHRPX_OPTID_BACKEND_RESPONSE_BUFFER: {
3522     size_t n;
3523     if (parse_uint_with_unit(&n, opt, optarg) != 0) {
3524       return -1;
3525     }
3526 
3527     if (n == 0) {
3528       LOG(ERROR) << opt << ": specify an integer strictly more than 0";
3529 
3530       return -1;
3531     }
3532 
3533     if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) {
3534       config->conn.downstream->request_buffer_size = n;
3535     } else {
3536       config->conn.downstream->response_buffer_size = n;
3537     }
3538 
3539     return 0;
3540   }
3541 
3542   case SHRPX_OPTID_NO_SERVER_PUSH:
3543     config->http2.no_server_push = util::strieq("yes"_sr, optarg);
3544 
3545     return 0;
3546   case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER:
3547     LOG(WARN) << opt << ": deprecated.";
3548     return 0;
3549   case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
3550     config->tls.ocsp.fetch_ocsp_response_file =
3551       make_string_ref(config->balloc, optarg);
3552 
3553     return 0;
3554   case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
3555     return parse_duration(&config->tls.ocsp.update_interval, opt, optarg);
3556   case SHRPX_OPTID_NO_OCSP:
3557     config->tls.ocsp.disabled = util::strieq("yes"_sr, optarg);
3558 
3559     return 0;
3560   case SHRPX_OPTID_HEADER_FIELD_BUFFER:
3561     LOG(WARN) << opt
3562               << ": deprecated.  Use request-header-field-buffer instead.";
3563   // fall through
3564   case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER:
3565     return parse_uint_with_unit(&config->http.request_header_field_buffer, opt,
3566                                 optarg);
3567   case SHRPX_OPTID_MAX_HEADER_FIELDS:
3568     LOG(WARN) << opt << ": deprecated.  Use max-request-header-fields instead.";
3569   // fall through
3570   case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS:
3571     return parse_uint(&config->http.max_request_header_fields, opt, optarg);
3572   case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER:
3573     return parse_uint_with_unit(&config->http.response_header_field_buffer, opt,
3574                                 optarg);
3575   case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS:
3576     return parse_uint(&config->http.max_response_header_fields, opt, optarg);
3577   case SHRPX_OPTID_INCLUDE: {
3578     if (included_set.count(optarg)) {
3579       LOG(ERROR) << opt << ": " << optarg << " has already been included";
3580       return -1;
3581     }
3582 
3583     included_set.insert(optarg);
3584     auto rv =
3585       load_config(config, optarg.data(), included_set, pattern_addr_indexer);
3586     included_set.erase(optarg);
3587 
3588     if (rv != 0) {
3589       return -1;
3590     }
3591 
3592     return 0;
3593   }
3594   case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
3595     if (util::strieq("aes-128-cbc"_sr, optarg)) {
3596       config->tls.ticket.cipher = EVP_aes_128_cbc();
3597     } else if (util::strieq("aes-256-cbc"_sr, optarg)) {
3598       config->tls.ticket.cipher = EVP_aes_256_cbc();
3599     } else {
3600       LOG(ERROR) << opt
3601                  << ": unsupported cipher for ticket encryption: " << optarg;
3602       return -1;
3603     }
3604     config->tls.ticket.cipher_given = true;
3605 
3606     return 0;
3607   case SHRPX_OPTID_HOST_REWRITE:
3608     config->http.no_host_rewrite = !util::strieq("yes"_sr, optarg);
3609 
3610     return 0;
3611   case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
3612   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
3613     auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
3614     auto src_params = StringRef{addr_end, std::end(optarg)};
3615 
3616     MemcachedConnectionParams params{};
3617     if (parse_memcached_connection_params(params, src_params, StringRef{opt}) !=
3618         0) {
3619       return -1;
3620     }
3621 
3622     if (split_host_port(host, sizeof(host), &port,
3623                         StringRef{std::begin(optarg), addr_end}, opt) == -1) {
3624       return -1;
3625     }
3626 
3627     switch (optid) {
3628     case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: {
3629       auto &memcachedconf = config->tls.session_cache.memcached;
3630       memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
3631       memcachedconf.port = port;
3632       memcachedconf.tls = params.tls;
3633       break;
3634     }
3635     case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
3636       auto &memcachedconf = config->tls.ticket.memcached;
3637       memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
3638       memcachedconf.port = port;
3639       memcachedconf.tls = params.tls;
3640       break;
3641     }
3642     };
3643 
3644     return 0;
3645   }
3646   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL:
3647     return parse_duration(&config->tls.ticket.memcached.interval, opt, optarg);
3648   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: {
3649     int n;
3650     if (parse_uint(&n, opt, optarg) != 0) {
3651       return -1;
3652     }
3653 
3654     if (n > 30) {
3655       LOG(ERROR) << opt << ": must be smaller than or equal to 30";
3656       return -1;
3657     }
3658 
3659     config->tls.ticket.memcached.max_retry = n;
3660     return 0;
3661   }
3662   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
3663     return parse_uint(&config->tls.ticket.memcached.max_fail, opt, optarg);
3664   case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: {
3665     size_t n;
3666     if (parse_uint_with_unit(&n, opt, optarg) != 0) {
3667       return -1;
3668     }
3669 
3670     config->tls.dyn_rec.warmup_threshold = n;
3671 
3672     return 0;
3673   }
3674 
3675   case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT:
3676     return parse_duration(&config->tls.dyn_rec.idle_timeout, opt, optarg);
3677 
3678   case SHRPX_OPTID_MRUBY_FILE:
3679 #ifdef HAVE_MRUBY
3680     config->mruby_file = make_string_ref(config->balloc, optarg);
3681 #else  // !HAVE_MRUBY
3682     LOG(WARN) << opt
3683               << ": ignored because mruby support is disabled at build time.";
3684 #endif // !HAVE_MRUBY
3685     return 0;
3686   case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
3687     LOG(WARN) << opt << ": deprecated.  Use proxyproto keyword in "
3688               << SHRPX_OPT_FRONTEND << " instead.";
3689     config->conn.upstream.accept_proxy_protocol =
3690       util::strieq("yes"_sr, optarg);
3691 
3692     return 0;
3693   case SHRPX_OPTID_ADD_FORWARDED: {
3694     auto &fwdconf = config->http.forwarded;
3695     fwdconf.params = FORWARDED_NONE;
3696     for (const auto &param : util::split_str(optarg, ',')) {
3697       if (util::strieq("by"_sr, param)) {
3698         fwdconf.params |= FORWARDED_BY;
3699         continue;
3700       }
3701       if (util::strieq("for"_sr, param)) {
3702         fwdconf.params |= FORWARDED_FOR;
3703         continue;
3704       }
3705       if (util::strieq("host"_sr, param)) {
3706         fwdconf.params |= FORWARDED_HOST;
3707         continue;
3708       }
3709       if (util::strieq("proto"_sr, param)) {
3710         fwdconf.params |= FORWARDED_PROTO;
3711         continue;
3712       }
3713 
3714       LOG(ERROR) << opt << ": unknown parameter " << optarg;
3715 
3716       return -1;
3717     }
3718 
3719     return 0;
3720   }
3721   case SHRPX_OPTID_STRIP_INCOMING_FORWARDED:
3722     config->http.forwarded.strip_incoming = util::strieq("yes"_sr, optarg);
3723 
3724     return 0;
3725   case SHRPX_OPTID_FORWARDED_BY:
3726   case SHRPX_OPTID_FORWARDED_FOR: {
3727     auto type = parse_forwarded_node_type(optarg);
3728 
3729     if (type == static_cast<ForwardedNode>(-1) ||
3730         (optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
3731       LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
3732                  << optarg;
3733       return -1;
3734     }
3735 
3736     auto &fwdconf = config->http.forwarded;
3737 
3738     switch (optid) {
3739     case SHRPX_OPTID_FORWARDED_BY:
3740       fwdconf.by_node_type = type;
3741       if (optarg[0] == '_') {
3742         fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
3743       } else {
3744         fwdconf.by_obfuscated = ""_sr;
3745       }
3746       break;
3747     case SHRPX_OPTID_FORWARDED_FOR:
3748       fwdconf.for_node_type = type;
3749       break;
3750     }
3751 
3752     return 0;
3753   }
3754   case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
3755     LOG(WARN) << opt << ": deprecated.  Use "
3756               << SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead.";
3757     // fall through
3758   case SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST:
3759     config->tls.no_http2_cipher_block_list = util::strieq("yes"_sr, optarg);
3760     return 0;
3761   case SHRPX_OPTID_BACKEND_HTTP1_TLS:
3762   case SHRPX_OPTID_BACKEND_TLS:
3763     LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
3764               << SHRPX_OPT_BACKEND << " instead.";
3765     return 0;
3766   case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
3767     LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
3768               << SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED;
3769     return 0;
3770   case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
3771     config->tls.session_cache.memcached.cert_file =
3772       make_string_ref(config->balloc, optarg);
3773 
3774     return 0;
3775   case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
3776     config->tls.session_cache.memcached.private_key_file =
3777       make_string_ref(config->balloc, optarg);
3778 
3779     return 0;
3780   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
3781     LOG(WARN) << opt << ": deprecated.  Use tls keyword in "
3782               << SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED;
3783     return 0;
3784   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
3785     config->tls.ticket.memcached.cert_file =
3786       make_string_ref(config->balloc, optarg);
3787 
3788     return 0;
3789   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
3790     config->tls.ticket.memcached.private_key_file =
3791       make_string_ref(config->balloc, optarg);
3792 
3793     return 0;
3794   case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
3795     return parse_address_family(&config->tls.ticket.memcached.family, opt,
3796                                 optarg);
3797   case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
3798     return parse_address_family(&config->tls.session_cache.memcached.family,
3799                                 opt, optarg);
3800   case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
3801     return parse_address_family(&config->conn.downstream->family, opt, optarg);
3802   case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
3803     return parse_uint(&config->http2.upstream.max_concurrent_streams, opt,
3804                       optarg);
3805   case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS:
3806     return parse_uint(&config->http2.downstream.max_concurrent_streams, opt,
3807                       optarg);
3808   case SHRPX_OPTID_ERROR_PAGE:
3809     return parse_error_page(config->http.error_pages, opt, optarg);
3810   case SHRPX_OPTID_NO_KQUEUE:
3811     if ((ev_supported_backends() & EVBACKEND_KQUEUE) == 0) {
3812       LOG(WARN) << opt << ": kqueue is not supported on this platform";
3813       return 0;
3814     }
3815 
3816     config->ev_loop_flags = ev_recommended_backends() & ~EVBACKEND_KQUEUE;
3817 
3818     return 0;
3819   case SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT:
3820     return parse_duration(&config->http2.upstream.timeout.settings, opt,
3821                           optarg);
3822   case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT:
3823     return parse_duration(&config->http2.downstream.timeout.settings, opt,
3824                           optarg);
3825   case SHRPX_OPTID_API_MAX_REQUEST_BODY:
3826     return parse_uint_with_unit(&config->api.max_request_body, opt, optarg);
3827   case SHRPX_OPTID_BACKEND_MAX_BACKOFF:
3828     return parse_duration(&config->conn.downstream->timeout.max_backoff, opt,
3829                           optarg);
3830   case SHRPX_OPTID_SERVER_NAME:
3831     config->http.server_name = make_string_ref(config->balloc, optarg);
3832 
3833     return 0;
3834   case SHRPX_OPTID_NO_SERVER_REWRITE:
3835     config->http.no_server_rewrite = util::strieq("yes"_sr, optarg);
3836 
3837     return 0;
3838   case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE:
3839     config->http2.upstream.optimize_write_buffer_size =
3840       util::strieq("yes"_sr, optarg);
3841 
3842     return 0;
3843   case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE:
3844     config->http2.upstream.optimize_window_size =
3845       util::strieq("yes"_sr, optarg);
3846 
3847     return 0;
3848   case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE:
3849     if (parse_uint_with_unit(&config->http2.upstream.window_size, opt,
3850                              optarg) != 0) {
3851       return -1;
3852     }
3853 
3854     return 0;
3855   case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE:
3856     if (parse_uint_with_unit(&config->http2.upstream.connection_window_size,
3857                              opt, optarg) != 0) {
3858       return -1;
3859     }
3860 
3861     return 0;
3862   case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE:
3863     if (parse_uint_with_unit(&config->http2.downstream.window_size, opt,
3864                              optarg) != 0) {
3865       return -1;
3866     }
3867 
3868     return 0;
3869   case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE:
3870     if (parse_uint_with_unit(&config->http2.downstream.connection_window_size,
3871                              opt, optarg) != 0) {
3872       return -1;
3873     }
3874 
3875     return 0;
3876   case SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
3877     if (parse_uint_with_unit(&config->http2.upstream.encoder_dynamic_table_size,
3878                              opt, optarg) != 0) {
3879       return -1;
3880     }
3881 
3882     nghttp2_option_set_max_deflate_dynamic_table_size(
3883       config->http2.upstream.option,
3884       config->http2.upstream.encoder_dynamic_table_size);
3885     nghttp2_option_set_max_deflate_dynamic_table_size(
3886       config->http2.upstream.alt_mode_option,
3887       config->http2.upstream.encoder_dynamic_table_size);
3888 
3889     return 0;
3890   case SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
3891     return parse_uint_with_unit(
3892       &config->http2.upstream.decoder_dynamic_table_size, opt, optarg);
3893   case SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
3894     if (parse_uint_with_unit(
3895           &config->http2.downstream.encoder_dynamic_table_size, opt, optarg) !=
3896         0) {
3897       return -1;
3898     }
3899 
3900     nghttp2_option_set_max_deflate_dynamic_table_size(
3901       config->http2.downstream.option,
3902       config->http2.downstream.encoder_dynamic_table_size);
3903 
3904     return 0;
3905   case SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
3906     return parse_uint_with_unit(
3907       &config->http2.downstream.decoder_dynamic_table_size, opt, optarg);
3908   case SHRPX_OPTID_ECDH_CURVES:
3909     config->tls.ecdh_curves = make_string_ref(config->balloc, optarg);
3910     return 0;
3911   case SHRPX_OPTID_TLS_SCT_DIR:
3912 #if defined(NGHTTP2_GENUINE_OPENSSL) || defined(NGHTTP2_OPENSSL_IS_BORINGSSL)
3913     return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg);
3914 #else  // !NGHTTP2_GENUINE_OPENSSL && !NGHTTP2_OPENSSL_IS_BORINGSSL
3915     LOG(WARN)
3916       << opt << ": ignored because underlying TLS library does not support SCT";
3917     return 0;
3918 #endif // !NGHTTP2_GENUINE_OPENSSL && !NGHTTP2_OPENSSL_IS_BORINGSSL
3919   case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
3920     return parse_duration(&config->dns.timeout.cache, opt, optarg);
3921   case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
3922     return parse_duration(&config->dns.timeout.lookup, opt, optarg);
3923   case SHRPX_OPTID_DNS_MAX_TRY: {
3924     int n;
3925     if (parse_uint(&n, opt, optarg) != 0) {
3926       return -1;
3927     }
3928 
3929     if (n > 5) {
3930       LOG(ERROR) << opt << ": must be smaller than or equal to 5";
3931       return -1;
3932     }
3933 
3934     config->dns.max_try = n;
3935     return 0;
3936   }
3937   case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
3938     return parse_duration(&config->conn.upstream.timeout.idle, opt, optarg);
3939   case SHRPX_OPTID_PSK_SECRETS:
3940 #ifndef OPENSSL_NO_PSK
3941     return parse_psk_secrets(config, optarg);
3942 #else  // OPENSSL_NO_PSK
3943     LOG(WARN)
3944       << opt << ": ignored because underlying TLS library does not support PSK";
3945     return 0;
3946 #endif // OPENSSL_NO_PSK
3947   case SHRPX_OPTID_CLIENT_PSK_SECRETS:
3948 #ifndef OPENSSL_NO_PSK
3949     return parse_client_psk_secrets(config, optarg);
3950 #else  // OPENSSL_NO_PSK
3951     LOG(WARN)
3952       << opt << ": ignored because underlying TLS library does not support PSK";
3953     return 0;
3954 #endif // OPENSSL_NO_PSK
3955   case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
3956     LOG(WARN) << opt << ": deprecated.  Use "
3957               << SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead.";
3958     // fall through
3959   case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST:
3960     config->tls.client.no_http2_cipher_block_list =
3961       util::strieq("yes"_sr, optarg);
3962 
3963     return 0;
3964   case SHRPX_OPTID_CLIENT_CIPHERS:
3965     config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
3966 
3967     return 0;
3968   case SHRPX_OPTID_TLS13_CLIENT_CIPHERS:
3969     config->tls.client.tls13_ciphers = make_string_ref(config->balloc, optarg);
3970 
3971     return 0;
3972   case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
3973     config->logging.access.write_early = util::strieq("yes"_sr, optarg);
3974 
3975     return 0;
3976   case SHRPX_OPTID_TLS_MIN_PROTO_VERSION:
3977     return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
3978   case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
3979     return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
3980   case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
3981     auto n = util::parse_uint(optarg);
3982     if (!n || n < 0 || n > 65535) {
3983       LOG(ERROR) << opt
3984                  << ": bad value.  Specify an integer in the range [0, "
3985                     "65535], inclusive";
3986       return -1;
3987     }
3988     config->http.redirect_https_port = make_string_ref(config->balloc, optarg);
3989     return 0;
3990   }
3991   case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
3992     return parse_uint(&config->http.max_requests, opt, optarg);
3993   case SHRPX_OPTID_SINGLE_THREAD:
3994     config->single_thread = util::strieq("yes"_sr, optarg);
3995 
3996     return 0;
3997   case SHRPX_OPTID_SINGLE_PROCESS:
3998     config->single_process = util::strieq("yes"_sr, optarg);
3999 
4000     return 0;
4001   case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
4002     config->http.xfp.add = !util::strieq("yes"_sr, optarg);
4003 
4004     return 0;
4005   case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
4006     config->http.xfp.strip_incoming = !util::strieq("yes"_sr, optarg);
4007 
4008     return 0;
4009   case SHRPX_OPTID_OCSP_STARTUP:
4010     config->tls.ocsp.startup = util::strieq("yes"_sr, optarg);
4011 
4012     return 0;
4013   case SHRPX_OPTID_NO_VERIFY_OCSP:
4014     config->tls.ocsp.no_verify = util::strieq("yes"_sr, optarg);
4015 
4016     return 0;
4017   case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED:
4018     config->tls.client_verify.tolerate_expired = util::strieq("yes"_sr, optarg);
4019 
4020     return 0;
4021   case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
4022     config->ignore_per_pattern_mruby_error = util::strieq("yes"_sr, optarg);
4023 
4024     return 0;
4025   case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA:
4026     config->tls.no_postpone_early_data = util::strieq("yes"_sr, optarg);
4027 
4028     return 0;
4029   case SHRPX_OPTID_TLS_MAX_EARLY_DATA: {
4030     return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg);
4031   }
4032   case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA:
4033     config->http.early_data.strip_incoming = !util::strieq("yes"_sr, optarg);
4034 
4035     return 0;
4036   case SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE:
4037 #ifdef ENABLE_HTTP3
4038     config->quic.bpf.prog_file = make_string_ref(config->balloc, optarg);
4039 #endif // ENABLE_HTTP3
4040 
4041     return 0;
4042   case SHRPX_OPTID_NO_QUIC_BPF:
4043 #ifdef ENABLE_HTTP3
4044     config->quic.bpf.disabled = util::strieq("yes"_sr, optarg);
4045 #endif // ENABLE_HTTP3
4046 
4047     return 0;
4048   case SHRPX_OPTID_HTTP2_ALTSVC: {
4049     AltSvc altsvc{};
4050 
4051     if (parse_altsvc(altsvc, opt, optarg) != 0) {
4052       return -1;
4053     }
4054 
4055     config->http.http2_altsvcs.push_back(std::move(altsvc));
4056 
4057     return 0;
4058   }
4059   case SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT:
4060     LOG(WARN) << opt << ": deprecated.  Use frontend-http3-idle-timeout";
4061     // fall through
4062   case SHRPX_OPTID_FRONTEND_HTTP3_IDLE_TIMEOUT:
4063 #ifdef ENABLE_HTTP3
4064     return parse_duration(&config->conn.upstream.timeout.http3_idle, opt,
4065                           optarg);
4066 #else  // !ENABLE_HTTP3
4067     return 0;
4068 #endif // !ENABLE_HTTP3
4069   case SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT:
4070 #ifdef ENABLE_HTTP3
4071     return parse_duration(&config->quic.upstream.timeout.idle, opt, optarg);
4072 #else  // !ENABLE_HTTP3
4073     return 0;
4074 #endif // !ENABLE_HTTP3
4075   case SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG:
4076 #ifdef ENABLE_HTTP3
4077     config->quic.upstream.debug.log = util::strieq("yes"_sr, optarg);
4078 #endif // ENABLE_HTTP3
4079 
4080     return 0;
4081   case SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE:
4082 #ifdef ENABLE_HTTP3
4083     if (parse_uint_with_unit(&config->http3.upstream.window_size, opt,
4084                              optarg) != 0) {
4085       return -1;
4086     }
4087 #endif // ENABLE_HTTP3
4088 
4089     return 0;
4090   case SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE:
4091 #ifdef ENABLE_HTTP3
4092     if (parse_uint_with_unit(&config->http3.upstream.connection_window_size,
4093                              opt, optarg) != 0) {
4094       return -1;
4095     }
4096 #endif // ENABLE_HTTP3
4097 
4098     return 0;
4099   case SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE:
4100 #ifdef ENABLE_HTTP3
4101     if (parse_uint_with_unit(&config->http3.upstream.max_window_size, opt,
4102                              optarg) != 0) {
4103       return -1;
4104     }
4105 #endif // ENABLE_HTTP3
4106 
4107     return 0;
4108   case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE:
4109 #ifdef ENABLE_HTTP3
4110     if (parse_uint_with_unit(&config->http3.upstream.max_connection_window_size,
4111                              opt, optarg) != 0) {
4112       return -1;
4113     }
4114 #endif // ENABLE_HTTP3
4115 
4116     return 0;
4117   case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS:
4118 #ifdef ENABLE_HTTP3
4119     return parse_uint(&config->http3.upstream.max_concurrent_streams, opt,
4120                       optarg);
4121 #else  // !ENABLE_HTTP3
4122     return 0;
4123 #endif // !ENABLE_HTTP3
4124   case SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA:
4125 #ifdef ENABLE_HTTP3
4126     config->quic.upstream.early_data = util::strieq("yes"_sr, optarg);
4127 #endif // ENABLE_HTTP3
4128 
4129     return 0;
4130   case SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR:
4131 #ifdef ENABLE_HTTP3
4132     config->quic.upstream.qlog.dir = make_string_ref(config->balloc, optarg);
4133 #endif // ENABLE_HTTP3
4134 
4135     return 0;
4136   case SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN:
4137 #ifdef ENABLE_HTTP3
4138     config->quic.upstream.require_token = util::strieq("yes"_sr, optarg);
4139 #endif // ENABLE_HTTP3
4140 
4141     return 0;
4142   case SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER:
4143 #ifdef ENABLE_HTTP3
4144     if (util::strieq("cubic"_sr, optarg)) {
4145       config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
4146     } else if (util::strieq("bbr"_sr, optarg)) {
4147       config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR;
4148     } else {
4149       LOG(ERROR) << opt << ": must be either cubic or bbr";
4150       return -1;
4151     }
4152 #endif // ENABLE_HTTP3
4153 
4154     return 0;
4155   case SHRPX_OPTID_QUIC_SERVER_ID:
4156 #ifdef ENABLE_HTTP3
4157     if (optarg.size() != sizeof(config->quic.server_id) * 2 ||
4158         !util::is_hex_string(optarg)) {
4159       LOG(ERROR) << opt << ": must be a hex-string";
4160       return -1;
4161     }
4162     util::decode_hex(reinterpret_cast<uint8_t *>(&config->quic.server_id),
4163                      optarg);
4164 #endif // ENABLE_HTTP3
4165 
4166     return 0;
4167   case SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE:
4168 #ifdef ENABLE_HTTP3
4169     config->quic.upstream.secret_file = make_string_ref(config->balloc, optarg);
4170 #endif // ENABLE_HTTP3
4171 
4172     return 0;
4173   case SHRPX_OPTID_RLIMIT_MEMLOCK: {
4174     int n;
4175 
4176     if (parse_uint(&n, opt, optarg) != 0) {
4177       return -1;
4178     }
4179 
4180     if (n < 0) {
4181       LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
4182 
4183       return -1;
4184     }
4185 
4186     config->rlimit_memlock = n;
4187 
4188     return 0;
4189   }
4190   case SHRPX_OPTID_MAX_WORKER_PROCESSES:
4191     return parse_uint(&config->max_worker_processes, opt, optarg);
4192   case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD:
4193     return parse_duration(&config->worker_process_grace_shutdown_period, opt,
4194                           optarg);
4195   case SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT: {
4196 #ifdef ENABLE_HTTP3
4197     return parse_duration(&config->quic.upstream.initial_rtt, opt, optarg);
4198 #endif // ENABLE_HTTP3
4199 
4200     return 0;
4201   }
4202   case SHRPX_OPTID_REQUIRE_HTTP_SCHEME:
4203     config->http.require_http_scheme = util::strieq("yes"_sr, optarg);
4204     return 0;
4205   case SHRPX_OPTID_TLS_KTLS:
4206     config->tls.ktls = util::strieq("yes"_sr, optarg);
4207     return 0;
4208   case SHRPX_OPTID_NPN_LIST:
4209     LOG(WARN) << opt << ": deprecated.  Use alpn-list instead.";
4210     // fall through
4211   case SHRPX_OPTID_ALPN_LIST: {
4212     auto list = util::split_str(optarg, ',');
4213     config->tls.alpn_list.resize(list.size());
4214     for (size_t i = 0; i < list.size(); ++i) {
4215       config->tls.alpn_list[i] = make_string_ref(config->balloc, list[i]);
4216     }
4217 
4218     return 0;
4219   }
4220   case SHRPX_OPTID_CONF:
4221     LOG(WARN) << "conf: ignored";
4222 
4223     return 0;
4224   }
4225 
4226   LOG(ERROR) << "Unknown option: " << opt;
4227 
4228   return -1;
4229 }
4230 
load_config(Config * config,const char * filename,std::set<StringRef> & include_set,std::map<StringRef,size_t> & pattern_addr_indexer)4231 int load_config(Config *config, const char *filename,
4232                 std::set<StringRef> &include_set,
4233                 std::map<StringRef, size_t> &pattern_addr_indexer) {
4234   std::ifstream in(filename, std::ios::binary);
4235   if (!in) {
4236     LOG(ERROR) << "Could not open config file " << filename;
4237     return -1;
4238   }
4239   std::string line;
4240   int linenum = 0;
4241   while (std::getline(in, line)) {
4242     ++linenum;
4243     if (line.empty() || line[0] == '#') {
4244       continue;
4245     }
4246     auto eq = std::find(std::begin(line), std::end(line), '=');
4247     if (eq == std::end(line)) {
4248       LOG(ERROR) << "Bad configuration format in " << filename << " at line "
4249                  << linenum;
4250       return -1;
4251     }
4252     *eq = '\0';
4253 
4254     if (parse_config(config, StringRef{std::begin(line), eq},
4255                      StringRef{eq + 1, std::end(line)}, include_set,
4256                      pattern_addr_indexer) != 0) {
4257       return -1;
4258     }
4259   }
4260   return 0;
4261 }
4262 
str_syslog_facility(int facility)4263 StringRef str_syslog_facility(int facility) {
4264   switch (facility) {
4265   case (LOG_AUTH):
4266     return "auth"_sr;
4267 #ifdef LOG_AUTHPRIV
4268   case (LOG_AUTHPRIV):
4269     return "authpriv"_sr;
4270 #endif // LOG_AUTHPRIV
4271   case (LOG_CRON):
4272     return "cron"_sr;
4273   case (LOG_DAEMON):
4274     return "daemon"_sr;
4275 #ifdef LOG_FTP
4276   case (LOG_FTP):
4277     return "ftp"_sr;
4278 #endif // LOG_FTP
4279   case (LOG_KERN):
4280     return "kern"_sr;
4281   case (LOG_LOCAL0):
4282     return "local0"_sr;
4283   case (LOG_LOCAL1):
4284     return "local1"_sr;
4285   case (LOG_LOCAL2):
4286     return "local2"_sr;
4287   case (LOG_LOCAL3):
4288     return "local3"_sr;
4289   case (LOG_LOCAL4):
4290     return "local4"_sr;
4291   case (LOG_LOCAL5):
4292     return "local5"_sr;
4293   case (LOG_LOCAL6):
4294     return "local6"_sr;
4295   case (LOG_LOCAL7):
4296     return "local7"_sr;
4297   case (LOG_LPR):
4298     return "lpr"_sr;
4299   case (LOG_MAIL):
4300     return "mail"_sr;
4301   case (LOG_SYSLOG):
4302     return "syslog"_sr;
4303   case (LOG_USER):
4304     return "user"_sr;
4305   case (LOG_UUCP):
4306     return "uucp"_sr;
4307   default:
4308     return "(unknown)"_sr;
4309   }
4310 }
4311 
int_syslog_facility(const StringRef & strfacility)4312 int int_syslog_facility(const StringRef &strfacility) {
4313   if (util::strieq("auth"_sr, strfacility)) {
4314     return LOG_AUTH;
4315   }
4316 
4317 #ifdef LOG_AUTHPRIV
4318   if (util::strieq("authpriv"_sr, strfacility)) {
4319     return LOG_AUTHPRIV;
4320   }
4321 #endif // LOG_AUTHPRIV
4322 
4323   if (util::strieq("cron"_sr, strfacility)) {
4324     return LOG_CRON;
4325   }
4326 
4327   if (util::strieq("daemon"_sr, strfacility)) {
4328     return LOG_DAEMON;
4329   }
4330 
4331 #ifdef LOG_FTP
4332   if (util::strieq("ftp"_sr, strfacility)) {
4333     return LOG_FTP;
4334   }
4335 #endif // LOG_FTP
4336 
4337   if (util::strieq("kern"_sr, strfacility)) {
4338     return LOG_KERN;
4339   }
4340 
4341   if (util::strieq("local0"_sr, strfacility)) {
4342     return LOG_LOCAL0;
4343   }
4344 
4345   if (util::strieq("local1"_sr, strfacility)) {
4346     return LOG_LOCAL1;
4347   }
4348 
4349   if (util::strieq("local2"_sr, strfacility)) {
4350     return LOG_LOCAL2;
4351   }
4352 
4353   if (util::strieq("local3"_sr, strfacility)) {
4354     return LOG_LOCAL3;
4355   }
4356 
4357   if (util::strieq("local4"_sr, strfacility)) {
4358     return LOG_LOCAL4;
4359   }
4360 
4361   if (util::strieq("local5"_sr, strfacility)) {
4362     return LOG_LOCAL5;
4363   }
4364 
4365   if (util::strieq("local6"_sr, strfacility)) {
4366     return LOG_LOCAL6;
4367   }
4368 
4369   if (util::strieq("local7"_sr, strfacility)) {
4370     return LOG_LOCAL7;
4371   }
4372 
4373   if (util::strieq("lpr"_sr, strfacility)) {
4374     return LOG_LPR;
4375   }
4376 
4377   if (util::strieq("mail"_sr, strfacility)) {
4378     return LOG_MAIL;
4379   }
4380 
4381   if (util::strieq("news"_sr, strfacility)) {
4382     return LOG_NEWS;
4383   }
4384 
4385   if (util::strieq("syslog"_sr, strfacility)) {
4386     return LOG_SYSLOG;
4387   }
4388 
4389   if (util::strieq("user"_sr, strfacility)) {
4390     return LOG_USER;
4391   }
4392 
4393   if (util::strieq("uucp"_sr, strfacility)) {
4394     return LOG_UUCP;
4395   }
4396 
4397   return -1;
4398 }
4399 
strproto(Proto proto)4400 StringRef strproto(Proto proto) {
4401   switch (proto) {
4402   case Proto::NONE:
4403     return "none"_sr;
4404   case Proto::HTTP1:
4405     return "http/1.1"_sr;
4406   case Proto::HTTP2:
4407     return "h2"_sr;
4408   case Proto::HTTP3:
4409     return "h3"_sr;
4410   case Proto::MEMCACHED:
4411     return "memcached"_sr;
4412   }
4413 
4414   // gcc needs this.
4415   assert(0);
4416   abort();
4417 }
4418 
4419 namespace {
4420 // Consistent hashing method described in
4421 // https://github.com/RJ/ketama.  Generate 160 32-bit hashes per |s|,
4422 // which is usually backend address.  The each hash is associated to
4423 // index of backend address.  When all hashes for every backend
4424 // address are calculated, sort it in ascending order of hash.  To
4425 // choose the index, compute 32-bit hash based on client IP address,
4426 // and do lower bound search in the array. The returned index is the
4427 // backend to use.
compute_affinity_hash(std::vector<AffinityHash> & res,size_t idx,const StringRef & s)4428 int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
4429                           const StringRef &s) {
4430   int rv;
4431   std::array<uint8_t, 32> buf;
4432 
4433   for (auto i = 0; i < 20; ++i) {
4434     auto t = std::string{s};
4435     t += i;
4436 
4437     rv = util::sha256(buf.data(), StringRef{t});
4438     if (rv != 0) {
4439       return -1;
4440     }
4441 
4442     for (int i = 0; i < 8; ++i) {
4443       auto h = (static_cast<uint32_t>(buf[4 * i]) << 24) |
4444                (static_cast<uint32_t>(buf[4 * i + 1]) << 16) |
4445                (static_cast<uint32_t>(buf[4 * i + 2]) << 8) |
4446                static_cast<uint32_t>(buf[4 * i + 3]);
4447 
4448       res.emplace_back(idx, h);
4449     }
4450   }
4451 
4452   return 0;
4453 }
4454 } // namespace
4455 
4456 // Configures the following member in |config|:
4457 // conn.downstream_router, conn.downstream.addr_groups,
4458 // conn.downstream.addr_group_catch_all.
configure_downstream_group(Config * config,bool http2_proxy,bool numeric_addr_only,const TLSConfig & tlsconf)4459 int configure_downstream_group(Config *config, bool http2_proxy,
4460                                bool numeric_addr_only,
4461                                const TLSConfig &tlsconf) {
4462   int rv;
4463 
4464   auto &downstreamconf = *config->conn.downstream;
4465   auto &addr_groups = downstreamconf.addr_groups;
4466   auto &routerconf = downstreamconf.router;
4467   auto &router = routerconf.router;
4468 
4469   if (addr_groups.empty()) {
4470     DownstreamAddrConfig addr{};
4471     addr.host = DEFAULT_DOWNSTREAM_HOST;
4472     addr.port = DEFAULT_DOWNSTREAM_PORT;
4473     addr.proto = Proto::HTTP1;
4474     addr.weight = 1;
4475     addr.group_weight = 1;
4476 
4477     DownstreamAddrGroupConfig g("/"_sr);
4478     g.addrs.push_back(std::move(addr));
4479     router.add_route(g.pattern, addr_groups.size());
4480     addr_groups.push_back(std::move(g));
4481   }
4482 
4483   // backward compatibility: override all SNI fields with the option
4484   // value --backend-tls-sni-field
4485   if (!tlsconf.backend_sni_name.empty()) {
4486     auto &sni = tlsconf.backend_sni_name;
4487     for (auto &addr_group : addr_groups) {
4488       for (auto &addr : addr_group.addrs) {
4489         addr.sni = sni;
4490       }
4491     }
4492   }
4493 
4494   if (LOG_ENABLED(INFO)) {
4495     LOG(INFO) << "Resolving backend address";
4496   }
4497 
4498   ssize_t catch_all_group = -1;
4499   for (size_t i = 0; i < addr_groups.size(); ++i) {
4500     auto &g = addr_groups[i];
4501     if (g.pattern == "/"_sr) {
4502       catch_all_group = i;
4503     }
4504     if (LOG_ENABLED(INFO)) {
4505       LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
4506                 << "'";
4507       for (auto &addr : g.addrs) {
4508         LOG(INFO) << "group " << i << " -> " << addr.host.data()
4509                   << (addr.host_unix ? "" : ":" + util::utos(addr.port))
4510                   << ", proto=" << strproto(addr.proto)
4511                   << (addr.tls ? ", tls" : "");
4512       }
4513     }
4514 #ifdef HAVE_MRUBY
4515     // Try compile mruby script and catch compile error early.
4516     if (!g.mruby_file.empty()) {
4517       if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
4518         LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
4519           << "backend: Could not compile mruby file for pattern " << g.pattern;
4520         if (!config->ignore_per_pattern_mruby_error) {
4521           return -1;
4522         }
4523         g.mruby_file = StringRef{};
4524       }
4525     }
4526 #endif // HAVE_MRUBY
4527   }
4528 
4529 #ifdef HAVE_MRUBY
4530   // Try compile mruby script (--mruby-file) here to catch compile
4531   // error early.
4532   if (!config->mruby_file.empty()) {
4533     if (mruby::create_mruby_context(config->mruby_file) == nullptr) {
4534       LOG(FATAL) << "mruby-file: Could not compile mruby file";
4535       return -1;
4536     }
4537   }
4538 #endif // HAVE_MRUBY
4539 
4540   if (catch_all_group == -1) {
4541     LOG(FATAL) << "backend: No catch-all backend address is configured";
4542     return -1;
4543   }
4544 
4545   downstreamconf.addr_group_catch_all = catch_all_group;
4546 
4547   if (LOG_ENABLED(INFO)) {
4548     LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
4549   }
4550 
4551   auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
4552 
4553   std::array<char, util::max_hostport> hostport_buf;
4554 
4555   for (auto &g : addr_groups) {
4556     std::unordered_map<StringRef, uint32_t> wgchk;
4557     for (auto &addr : g.addrs) {
4558       if (addr.group_weight) {
4559         auto it = wgchk.find(addr.group);
4560         if (it == std::end(wgchk)) {
4561           wgchk.emplace(addr.group, addr.group_weight);
4562         } else if ((*it).second != addr.group_weight) {
4563           LOG(FATAL) << "backend: inconsistent group-weight for a single group";
4564           return -1;
4565         }
4566       }
4567 
4568       if (addr.host_unix) {
4569         // for AF_UNIX socket, we use "localhost" as host for backend
4570         // hostport.  This is used as Host header field to backend and
4571         // not going to be passed to any syscalls.
4572         addr.hostport = "localhost"_sr;
4573 
4574         auto path = addr.host.data();
4575         auto pathlen = addr.host.size();
4576 
4577         if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
4578           LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
4579                      << sizeof(addr.addr.su.un.sun_path);
4580           return -1;
4581         }
4582 
4583         if (LOG_ENABLED(INFO)) {
4584           LOG(INFO) << "Use UNIX domain socket path " << path
4585                     << " for backend connection";
4586         }
4587 
4588         addr.addr.su.un.sun_family = AF_UNIX;
4589         // copy path including terminal NULL
4590         std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path);
4591         addr.addr.len = sizeof(addr.addr.su.un);
4592 
4593         continue;
4594       }
4595 
4596       addr.hostport =
4597         util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
4598 
4599       auto hostport =
4600         util::make_hostport(std::begin(hostport_buf), addr.host, addr.port);
4601 
4602       if (!addr.dns) {
4603         if (resolve_hostname(&addr.addr, addr.host.data(), addr.port,
4604                              downstreamconf.family, resolve_flags) == -1) {
4605           LOG(FATAL) << "Resolving backend address failed: " << hostport;
4606           return -1;
4607         }
4608 
4609         if (LOG_ENABLED(INFO)) {
4610           LOG(INFO) << "Resolved backend address: " << hostport << " -> "
4611                     << util::to_numeric_addr(&addr.addr);
4612         }
4613       } else {
4614         LOG(INFO) << "Resolving backend address " << hostport
4615                   << " takes place dynamically";
4616       }
4617     }
4618 
4619     for (auto &addr : g.addrs) {
4620       if (addr.group_weight == 0) {
4621         auto it = wgchk.find(addr.group);
4622         if (it == std::end(wgchk)) {
4623           addr.group_weight = 1;
4624         } else {
4625           addr.group_weight = (*it).second;
4626         }
4627       }
4628     }
4629 
4630     if (g.affinity.type != SessionAffinity::NONE) {
4631       size_t idx = 0;
4632       for (auto &addr : g.addrs) {
4633         StringRef key;
4634         if (addr.dns) {
4635           if (addr.host_unix) {
4636             key = addr.host;
4637           } else {
4638             key = addr.hostport;
4639           }
4640         } else {
4641           auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
4642           key = StringRef{p, addr.addr.len};
4643         }
4644         rv = compute_affinity_hash(g.affinity_hash, idx, key);
4645         if (rv != 0) {
4646           return -1;
4647         }
4648 
4649         if (g.affinity.cookie.stickiness ==
4650             SessionAffinityCookieStickiness::STRICT) {
4651           addr.affinity_hash = util::hash32(key);
4652           g.affinity_hash_map.emplace(addr.affinity_hash, idx);
4653         }
4654 
4655         ++idx;
4656       }
4657 
4658       std::sort(std::begin(g.affinity_hash), std::end(g.affinity_hash),
4659                 [](const AffinityHash &lhs, const AffinityHash &rhs) {
4660                   return lhs.hash < rhs.hash;
4661                 });
4662     }
4663 
4664     auto &timeout = g.timeout;
4665     if (timeout.read < 1e-9) {
4666       timeout.read = downstreamconf.timeout.read;
4667     }
4668     if (timeout.write < 1e-9) {
4669       timeout.write = downstreamconf.timeout.write;
4670     }
4671   }
4672 
4673   return 0;
4674 }
4675 
resolve_hostname(Address * addr,const char * hostname,uint16_t port,int family,int additional_flags)4676 int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
4677                      int family, int additional_flags) {
4678   int rv;
4679 
4680   auto service = util::utos(port);
4681 
4682   addrinfo hints{};
4683   hints.ai_family = family;
4684   hints.ai_socktype = SOCK_STREAM;
4685   hints.ai_flags |= additional_flags;
4686 #ifdef AI_ADDRCONFIG
4687   hints.ai_flags |= AI_ADDRCONFIG;
4688 #endif // AI_ADDRCONFIG
4689   addrinfo *res;
4690 
4691   rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
4692 #ifdef AI_ADDRCONFIG
4693   if (rv != 0) {
4694     // Retry without AI_ADDRCONFIG
4695     hints.ai_flags &= ~AI_ADDRCONFIG;
4696     rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
4697   }
4698 #endif // AI_ADDRCONFIG
4699   if (rv != 0) {
4700     LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
4701                << gai_strerror(rv);
4702     return -1;
4703   }
4704 
4705   auto res_d = defer(freeaddrinfo, res);
4706 
4707   char host[NI_MAXHOST];
4708   rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr,
4709                    0, NI_NUMERICHOST);
4710   if (rv != 0) {
4711     LOG(FATAL) << "Address resolution for " << hostname
4712                << " failed: " << gai_strerror(rv);
4713 
4714     return -1;
4715   }
4716 
4717   if (LOG_ENABLED(INFO)) {
4718     LOG(INFO) << "Address resolution for " << hostname
4719               << " succeeded: " << host;
4720   }
4721 
4722   memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
4723   addr->len = res->ai_addrlen;
4724 
4725   return 0;
4726 }
4727 
4728 #ifdef ENABLE_HTTP3
QUICKeyingMaterial(QUICKeyingMaterial && other)4729 QUICKeyingMaterial::QUICKeyingMaterial(QUICKeyingMaterial &&other) noexcept
4730   : cid_encryption_ctx{std::exchange(other.cid_encryption_ctx, nullptr)},
4731     cid_decryption_ctx{std::exchange(other.cid_decryption_ctx, nullptr)},
4732     reserved{other.reserved},
4733     secret{other.secret},
4734     salt{other.salt},
4735     cid_encryption_key{other.cid_encryption_key},
4736     id{other.id} {}
4737 
~QUICKeyingMaterial()4738 QUICKeyingMaterial::~QUICKeyingMaterial() noexcept {
4739   if (cid_encryption_ctx) {
4740     EVP_CIPHER_CTX_free(cid_encryption_ctx);
4741   }
4742 
4743   if (cid_decryption_ctx) {
4744     EVP_CIPHER_CTX_free(cid_decryption_ctx);
4745   }
4746 }
4747 
4748 QUICKeyingMaterial &
operator =(QUICKeyingMaterial && other)4749 QUICKeyingMaterial::operator=(QUICKeyingMaterial &&other) noexcept {
4750   cid_encryption_ctx = std::exchange(other.cid_encryption_ctx, nullptr);
4751   cid_decryption_ctx = std::exchange(other.cid_decryption_ctx, nullptr);
4752   reserved = other.reserved;
4753   secret = other.secret;
4754   salt = other.salt;
4755   cid_encryption_key = other.cid_encryption_key;
4756   id = other.id;
4757 
4758   return *this;
4759 }
4760 #endif // ENABLE_HTTP3
4761 
4762 } // namespace shrpx
4763