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