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