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