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 ¶m : 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