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