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