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