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