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