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