Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / shrpx_config.cc
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
45 #include <cstring>
46 #include <cerrno>
47 #include <limits>
48 #include <fstream>
49
50 #include <nghttp2/nghttp2.h>
51
52 #include "http-parser/http_parser.h"
53
54 #include "shrpx_log.h"
55 #include "shrpx_ssl.h"
56 #include "shrpx_http.h"
57 #include "http2.h"
58 #include "util.h"
59 #include "template.h"
60
61 using namespace nghttp2;
62
63 namespace shrpx {
64
65 const char SHRPX_OPT_PRIVATE_KEY_FILE[] = "private-key-file";
66 const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = "private-key-passwd-file";
67 const char SHRPX_OPT_CERTIFICATE_FILE[] = "certificate-file";
68 const char SHRPX_OPT_DH_PARAM_FILE[] = "dh-param-file";
69 const char SHRPX_OPT_SUBCERT[] = "subcert";
70
71 const char SHRPX_OPT_BACKEND[] = "backend";
72 const char SHRPX_OPT_FRONTEND[] = "frontend";
73 const char SHRPX_OPT_WORKERS[] = "workers";
74 const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[] =
75     "http2-max-concurrent-streams";
76 const char SHRPX_OPT_LOG_LEVEL[] = "log-level";
77 const char SHRPX_OPT_DAEMON[] = "daemon";
78 const char SHRPX_OPT_HTTP2_PROXY[] = "http2-proxy";
79 const char SHRPX_OPT_HTTP2_BRIDGE[] = "http2-bridge";
80 const char SHRPX_OPT_CLIENT_PROXY[] = "client-proxy";
81 const char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = "add-x-forwarded-for";
82 const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[] =
83     "strip-incoming-x-forwarded-for";
84 const char SHRPX_OPT_NO_VIA[] = "no-via";
85 const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[] =
86     "frontend-http2-read-timeout";
87 const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout";
88 const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout";
89 const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout";
90 const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout";
91 const char SHRPX_OPT_STREAM_READ_TIMEOUT[] = "stream-read-timeout";
92 const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[] = "stream-write-timeout";
93 const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file";
94 const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog";
95 const char SHRPX_OPT_ACCESSLOG_FORMAT[] = "accesslog-format";
96 const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file";
97 const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog";
98 const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] =
99     "backend-keep-alive-timeout";
100 const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[] =
101     "frontend-http2-window-bits";
102 const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[] = "backend-http2-window-bits";
103 const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[] =
104     "frontend-http2-connection-window-bits";
105 const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[] =
106     "backend-http2-connection-window-bits";
107 const char SHRPX_OPT_FRONTEND_NO_TLS[] = "frontend-no-tls";
108 const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls";
109 const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field";
110 const char SHRPX_OPT_PID_FILE[] = "pid-file";
111 const char SHRPX_OPT_USER[] = "user";
112 const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility";
113 const char SHRPX_OPT_BACKLOG[] = "backlog";
114 const char SHRPX_OPT_CIPHERS[] = "ciphers";
115 const char SHRPX_OPT_CLIENT[] = "client";
116 const char SHRPX_OPT_INSECURE[] = "insecure";
117 const char SHRPX_OPT_CACERT[] = "cacert";
118 const char SHRPX_OPT_BACKEND_IPV4[] = "backend-ipv4";
119 const char SHRPX_OPT_BACKEND_IPV6[] = "backend-ipv6";
120 const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = "backend-http-proxy-uri";
121 const char SHRPX_OPT_READ_RATE[] = "read-rate";
122 const char SHRPX_OPT_READ_BURST[] = "read-burst";
123 const char SHRPX_OPT_WRITE_RATE[] = "write-rate";
124 const char SHRPX_OPT_WRITE_BURST[] = "write-burst";
125 const char SHRPX_OPT_WORKER_READ_RATE[] = "worker-read-rate";
126 const char SHRPX_OPT_WORKER_READ_BURST[] = "worker-read-burst";
127 const char SHRPX_OPT_WORKER_WRITE_RATE[] = "worker-write-rate";
128 const char SHRPX_OPT_WORKER_WRITE_BURST[] = "worker-write-burst";
129 const char SHRPX_OPT_NPN_LIST[] = "npn-list";
130 const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list";
131 const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client";
132 const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert";
133 const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file";
134 const char SHRPX_OPT_CLIENT_CERT_FILE[] = "client-cert-file";
135 const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] =
136     "frontend-http2-dump-request-header";
137 const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] =
138     "frontend-http2-dump-response-header";
139 const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling";
140 const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug";
141 const char SHRPX_OPT_PADDING[] = "padding";
142 const char SHRPX_OPT_ALTSVC[] = "altsvc";
143 const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header";
144 const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] =
145     "worker-frontend-connections";
146 const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite";
147 const char SHRPX_OPT_NO_HOST_REWRITE[] = "no-host-rewrite";
148 const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] =
149     "backend-http1-connections-per-host";
150 const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] =
151     "backend-http1-connections-per-frontend";
152 const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout";
153 const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file";
154 const char SHRPX_OPT_RLIMIT_NOFILE[] = "rlimit-nofile";
155 const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[] = "backend-request-buffer";
156 const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[] = "backend-response-buffer";
157 const char SHRPX_OPT_NO_SERVER_PUSH[] = "no-server-push";
158 const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] =
159     "backend-http2-connections-per-worker";
160 const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file";
161 const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval";
162 const char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
163 const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
164 const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
165
166 namespace {
167 Config *config = nullptr;
168 } // namespace
169
170 const Config *get_config() { return config; }
171
172 Config *mod_config() { return config; }
173
174 void create_config() { config = new Config(); }
175
176 TicketKeys::~TicketKeys() {
177   /* Erase keys from memory */
178   for (auto &key : keys) {
179     memset(&key, 0, sizeof(key));
180   }
181 }
182
183 namespace {
184 int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
185                     const char *hostport) {
186   // host and port in |hostport| is separated by single ','.
187   const char *p = strchr(hostport, ',');
188   if (!p) {
189     LOG(ERROR) << "Invalid host, port: " << hostport;
190     return -1;
191   }
192   size_t len = p - hostport;
193   if (hostlen < len + 1) {
194     LOG(ERROR) << "Hostname too long: " << hostport;
195     return -1;
196   }
197   memcpy(host, hostport, len);
198   host[len] = '\0';
199
200   errno = 0;
201   unsigned long d = strtoul(p + 1, nullptr, 10);
202   if (errno == 0 && 1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
203     *port_ptr = d;
204     return 0;
205   } else {
206     LOG(ERROR) << "Port is invalid: " << p + 1;
207     return -1;
208   }
209 }
210 } // namespace
211
212 namespace {
213 bool is_secure(const char *filename) {
214   struct stat buf;
215   int rv = stat(filename, &buf);
216   if (rv == 0) {
217     if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
218         !(buf.st_mode & S_IRWXO)) {
219       return true;
220     }
221   }
222
223   return false;
224 }
225 } // namespace
226
227 std::unique_ptr<TicketKeys>
228 read_tls_ticket_key_file(const std::vector<std::string> &files) {
229   auto ticket_keys = make_unique<TicketKeys>();
230   auto &keys = ticket_keys->keys;
231   keys.resize(files.size());
232   size_t i = 0;
233   for (auto &file : files) {
234     std::ifstream f(file.c_str());
235     if (!f) {
236       LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
237       return nullptr;
238     }
239     char buf[48];
240     f.read(buf, sizeof(buf));
241     if (f.gcount() != sizeof(buf)) {
242       LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read "
243                  << f.gcount() << " bytes from " << file;
244       return nullptr;
245     }
246
247     auto &key = keys[i++];
248     auto p = buf;
249     memcpy(key.name, p, sizeof(key.name));
250     p += sizeof(key.name);
251     memcpy(key.aes_key, p, sizeof(key.aes_key));
252     p += sizeof(key.aes_key);
253     memcpy(key.hmac_key, p, sizeof(key.hmac_key));
254
255     if (LOG_ENABLED(INFO)) {
256       LOG(INFO) << "session ticket key: " << util::format_hex(key.name,
257                                                               sizeof(key.name));
258     }
259   }
260   return ticket_keys;
261 }
262
263 FILE *open_file_for_write(const char *filename) {
264 #if defined O_CLOEXEC
265   auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
266                  S_IRUSR | S_IWUSR);
267 #else
268   auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
269
270   // We get race condition if execve is called at the same time.
271   if (fd != -1) {
272     util::make_socket_closeonexec(fd);
273   }
274 #endif
275   if (fd == -1) {
276     LOG(ERROR) << "Failed to open " << filename
277                << " for writing. Cause: " << strerror(errno);
278     return nullptr;
279   }
280   auto f = fdopen(fd, "wb");
281   if (f == nullptr) {
282     LOG(ERROR) << "Failed to open " << filename
283                << " for writing. Cause: " << strerror(errno);
284     return nullptr;
285   }
286
287   return f;
288 }
289
290 std::string read_passwd_from_file(const char *filename) {
291   std::string line;
292
293   if (!is_secure(filename)) {
294     LOG(ERROR) << "Private key passwd file " << filename
295                << " has insecure mode.";
296     return line;
297   }
298
299   std::ifstream in(filename, std::ios::binary);
300   if (!in) {
301     LOG(ERROR) << "Could not open key passwd file " << filename;
302     return line;
303   }
304
305   std::getline(in, line);
306   return line;
307 }
308
309 std::unique_ptr<char[]> strcopy(const char *val) {
310   return strcopy(val, strlen(val));
311 }
312
313 std::unique_ptr<char[]> strcopy(const char *val, size_t len) {
314   auto res = make_unique<char[]>(len + 1);
315   memcpy(res.get(), val, len);
316   res[len] = '\0';
317   return res;
318 }
319
320 std::unique_ptr<char[]> strcopy(const std::string &val) {
321   return strcopy(val.c_str(), val.size());
322 }
323
324 std::vector<char *> parse_config_str_list(const char *s) {
325   size_t len = 1;
326   for (const char *first = s, *p = nullptr; (p = strchr(first, ','));
327        ++len, first = p + 1)
328     ;
329   auto list = std::vector<char *>(len);
330   auto first = strdup(s);
331   len = 0;
332   for (;;) {
333     auto p = strchr(first, ',');
334     if (p == nullptr) {
335       break;
336     }
337     list[len++] = first;
338     *p = '\0';
339     first = p + 1;
340   }
341   list[len++] = first;
342
343   return list;
344 }
345
346 void clear_config_str_list(std::vector<char *> &list) {
347   if (list.empty()) {
348     return;
349   }
350
351   free(list[0]);
352   list.clear();
353 }
354
355 std::pair<std::string, std::string> parse_header(const char *optarg) {
356   // We skip possible ":" at the start of optarg.
357   const auto *colon = strchr(optarg + 1, ':');
358
359   // name = ":" is not allowed
360   if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) {
361     return {"", ""};
362   }
363
364   auto value = colon + 1;
365   for (; *value == '\t' || *value == ' '; ++value)
366     ;
367
368   return {std::string(optarg, colon), std::string(value, strlen(value))};
369 }
370
371 template <typename T>
372 int parse_uint(T *dest, const char *opt, const char *optarg) {
373   char *end = nullptr;
374
375   errno = 0;
376
377   auto val = strtol(optarg, &end, 10);
378
379   if (!optarg[0] || errno != 0 || *end || val < 0) {
380     LOG(ERROR) << opt << ": bad value.  Specify an integer >= 0.";
381     return -1;
382   }
383
384   *dest = val;
385
386   return 0;
387 }
388
389 namespace {
390 template <typename T>
391 int parse_uint_with_unit(T *dest, const char *opt, const char *optarg) {
392   auto n = util::parse_uint_with_unit(optarg);
393   if (n == -1) {
394     LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
395     return -1;
396   }
397
398   *dest = n;
399
400   return 0;
401 }
402 } // namespace
403
404 template <typename T>
405 int parse_int(T *dest, const char *opt, const char *optarg) {
406   char *end = nullptr;
407
408   errno = 0;
409
410   auto val = strtol(optarg, &end, 10);
411
412   if (!optarg[0] || errno != 0 || *end) {
413     LOG(ERROR) << opt << ": bad value.  Specify an integer.";
414     return -1;
415   }
416
417   *dest = val;
418
419   return 0;
420 }
421
422 namespace {
423 LogFragment make_log_fragment(LogFragmentType type,
424                               std::unique_ptr<char[]> value = nullptr) {
425   return LogFragment{type, std::move(value)};
426 }
427 } // namespace
428
429 namespace {
430 bool var_token(char c) {
431   return util::isAlpha(c) || util::isDigit(c) || c == '_';
432 }
433 } // namespace
434
435 std::vector<LogFragment> parse_log_format(const char *optarg) {
436   auto literal_start = optarg;
437   auto p = optarg;
438   auto eop = p + strlen(optarg);
439
440   auto res = std::vector<LogFragment>();
441
442   for (; p != eop;) {
443     if (*p != '$') {
444       ++p;
445       continue;
446     }
447
448     auto var_start = p;
449
450     ++p;
451
452     for (; p != eop && var_token(*p); ++p)
453       ;
454
455     auto varlen = p - var_start;
456
457     auto type = SHRPX_LOGF_NONE;
458     const char *value = nullptr;
459     size_t valuelen = 0;
460
461     if (util::strieq_l("$remote_addr", var_start, varlen)) {
462       type = SHRPX_LOGF_REMOTE_ADDR;
463     } else if (util::strieq_l("$time_local", var_start, varlen)) {
464       type = SHRPX_LOGF_TIME_LOCAL;
465     } else if (util::strieq_l("$time_iso8601", var_start, varlen)) {
466       type = SHRPX_LOGF_TIME_ISO8601;
467     } else if (util::strieq_l("$request", var_start, varlen)) {
468       type = SHRPX_LOGF_REQUEST;
469     } else if (util::strieq_l("$status", var_start, varlen)) {
470       type = SHRPX_LOGF_STATUS;
471     } else if (util::strieq_l("$body_bytes_sent", var_start, varlen)) {
472       type = SHRPX_LOGF_BODY_BYTES_SENT;
473     } else if (util::istartsWith(var_start, varlen, "$http_")) {
474       type = SHRPX_LOGF_HTTP;
475       value = var_start + sizeof("$http_") - 1;
476       valuelen = varlen - (sizeof("$http_") - 1);
477     } else if (util::strieq_l("$remote_port", var_start, varlen)) {
478       type = SHRPX_LOGF_REMOTE_PORT;
479     } else if (util::strieq_l("$server_port", var_start, varlen)) {
480       type = SHRPX_LOGF_SERVER_PORT;
481     } else if (util::strieq_l("$request_time", var_start, varlen)) {
482       type = SHRPX_LOGF_REQUEST_TIME;
483     } else if (util::strieq_l("$pid", var_start, varlen)) {
484       type = SHRPX_LOGF_PID;
485     } else if (util::strieq_l("$alpn", var_start, varlen)) {
486       type = SHRPX_LOGF_ALPN;
487     } else {
488       LOG(WARN) << "Unrecognized log format variable: "
489                 << std::string(var_start, varlen);
490       continue;
491     }
492
493     if (literal_start < var_start) {
494       res.push_back(
495           make_log_fragment(SHRPX_LOGF_LITERAL,
496                             strcopy(literal_start, var_start - literal_start)));
497     }
498
499     if (value == nullptr) {
500       res.push_back(make_log_fragment(type));
501     } else {
502       res.push_back(make_log_fragment(type, strcopy(value, valuelen)));
503       auto &v = res.back().value;
504       for (size_t i = 0; v[i]; ++i) {
505         if (v[i] == '_') {
506           v[i] = '-';
507         }
508       }
509     }
510
511     literal_start = var_start + varlen;
512   }
513
514   if (literal_start != eop) {
515     res.push_back(make_log_fragment(
516         SHRPX_LOGF_LITERAL, strcopy(literal_start, eop - literal_start)));
517   }
518
519   return res;
520 }
521
522 namespace {
523 int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
524   auto t = util::parse_duration_with_unit(optarg);
525   if (t == std::numeric_limits<double>::infinity()) {
526     LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
527     return -1;
528   }
529
530   *dest = t;
531
532   return 0;
533 }
534 } // namespace
535
536 int parse_config(const char *opt, const char *optarg) {
537   char host[NI_MAXHOST];
538   uint16_t port;
539   if (util::strieq(opt, SHRPX_OPT_BACKEND)) {
540     if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) {
541       DownstreamAddr addr;
542       auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
543       addr.host = strcopy(path);
544       addr.host_unix = true;
545
546       mod_config()->downstream_addrs.push_back(std::move(addr));
547
548       return 0;
549     }
550
551     if (split_host_port(host, sizeof(host), &port, optarg) == -1) {
552       return -1;
553     }
554
555     DownstreamAddr addr;
556     addr.host = strcopy(host);
557     addr.port = port;
558
559     mod_config()->downstream_addrs.push_back(std::move(addr));
560
561     return 0;
562   }
563
564   if (util::strieq(opt, SHRPX_OPT_FRONTEND)) {
565     if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) {
566       auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
567       mod_config()->host = strcopy(path);
568       mod_config()->port = 0;
569       mod_config()->host_unix = true;
570
571       return 0;
572     }
573
574     if (split_host_port(host, sizeof(host), &port, optarg) == -1) {
575       return -1;
576     }
577
578     mod_config()->host = strcopy(host);
579     mod_config()->port = port;
580     mod_config()->host_unix = false;
581
582     return 0;
583   }
584
585   if (util::strieq(opt, SHRPX_OPT_WORKERS)) {
586     return parse_uint(&mod_config()->num_worker, opt, optarg);
587   }
588
589   if (util::strieq(opt, SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS)) {
590     return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg);
591   }
592
593   if (util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) {
594     if (Log::set_severity_level_by_name(optarg) == -1) {
595       LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
596       return -1;
597     }
598
599     return 0;
600   }
601
602   if (util::strieq(opt, SHRPX_OPT_DAEMON)) {
603     mod_config()->daemon = util::strieq(optarg, "yes");
604
605     return 0;
606   }
607
608   if (util::strieq(opt, SHRPX_OPT_HTTP2_PROXY)) {
609     mod_config()->http2_proxy = util::strieq(optarg, "yes");
610
611     return 0;
612   }
613
614   if (util::strieq(opt, SHRPX_OPT_HTTP2_BRIDGE)) {
615     mod_config()->http2_bridge = util::strieq(optarg, "yes");
616
617     return 0;
618   }
619
620   if (util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) {
621     mod_config()->client_proxy = util::strieq(optarg, "yes");
622
623     return 0;
624   }
625
626   if (util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) {
627     mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes");
628
629     return 0;
630   }
631
632   if (util::strieq(opt, SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR)) {
633     mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes");
634
635     return 0;
636   }
637
638   if (util::strieq(opt, SHRPX_OPT_NO_VIA)) {
639     mod_config()->no_via = util::strieq(optarg, "yes");
640
641     return 0;
642   }
643
644   if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) {
645     return parse_duration(&mod_config()->http2_upstream_read_timeout, opt,
646                           optarg);
647   }
648
649   if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) {
650     return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg);
651   }
652
653   if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) {
654     return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg);
655   }
656
657   if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) {
658     return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg);
659   }
660
661   if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) {
662     return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg);
663   }
664
665   if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) {
666     return parse_duration(&mod_config()->stream_read_timeout, opt, optarg);
667   }
668
669   if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) {
670     return parse_duration(&mod_config()->stream_write_timeout, opt, optarg);
671   }
672
673   if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) {
674     mod_config()->accesslog_file = strcopy(optarg);
675
676     return 0;
677   }
678
679   if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) {
680     mod_config()->accesslog_syslog = util::strieq(optarg, "yes");
681
682     return 0;
683   }
684
685   if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FORMAT)) {
686     mod_config()->accesslog_format = parse_log_format(optarg);
687
688     return 0;
689   }
690
691   if (util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) {
692     mod_config()->errorlog_file = strcopy(optarg);
693
694     return 0;
695   }
696
697   if (util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) {
698     mod_config()->errorlog_syslog = util::strieq(optarg, "yes");
699
700     return 0;
701   }
702
703   if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) {
704     return parse_duration(&mod_config()->downstream_idle_read_timeout, opt,
705                           optarg);
706   }
707
708   if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) ||
709       util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS)) {
710
711     size_t *resp;
712
713     if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS)) {
714       resp = &mod_config()->http2_upstream_window_bits;
715     } else {
716       resp = &mod_config()->http2_downstream_window_bits;
717     }
718
719     errno = 0;
720
721     int n;
722
723     if (parse_uint(&n, opt, optarg) != 0) {
724       return -1;
725     }
726
727     if (n >= 31) {
728       LOG(ERROR) << opt
729                  << ": specify the integer in the range [0, 30], inclusive";
730       return -1;
731     }
732
733     *resp = n;
734
735     return 0;
736   }
737
738   if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) ||
739       util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS)) {
740
741     size_t *resp;
742
743     if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS)) {
744       resp = &mod_config()->http2_upstream_connection_window_bits;
745     } else {
746       resp = &mod_config()->http2_downstream_connection_window_bits;
747     }
748
749     errno = 0;
750
751     int n;
752
753     if (parse_uint(&n, opt, optarg) != 0) {
754       return -1;
755     }
756
757     if (n < 16 || n >= 31) {
758       LOG(ERROR) << opt
759                  << ": specify the integer in the range [16, 30], inclusive";
760       return -1;
761     }
762
763     *resp = n;
764
765     return 0;
766   }
767
768   if (util::strieq(opt, SHRPX_OPT_FRONTEND_NO_TLS)) {
769     mod_config()->upstream_no_tls = util::strieq(optarg, "yes");
770
771     return 0;
772   }
773
774   if (util::strieq(opt, SHRPX_OPT_BACKEND_NO_TLS)) {
775     mod_config()->downstream_no_tls = util::strieq(optarg, "yes");
776
777     return 0;
778   }
779
780   if (util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) {
781     mod_config()->backend_tls_sni_name = strcopy(optarg);
782
783     return 0;
784   }
785
786   if (util::strieq(opt, SHRPX_OPT_PID_FILE)) {
787     mod_config()->pid_file = strcopy(optarg);
788
789     return 0;
790   }
791
792   if (util::strieq(opt, SHRPX_OPT_USER)) {
793     auto pwd = getpwnam(optarg);
794     if (!pwd) {
795       LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
796                  << strerror(errno);
797       return -1;
798     }
799     mod_config()->user = strcopy(pwd->pw_name);
800     mod_config()->uid = pwd->pw_uid;
801     mod_config()->gid = pwd->pw_gid;
802
803     return 0;
804   }
805
806   if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) {
807     mod_config()->private_key_file = strcopy(optarg);
808
809     return 0;
810   }
811
812   if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) {
813     auto passwd = read_passwd_from_file(optarg);
814     if (passwd.empty()) {
815       LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
816       return -1;
817     }
818     mod_config()->private_key_passwd = strcopy(passwd);
819
820     return 0;
821   }
822
823   if (util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) {
824     mod_config()->cert_file = strcopy(optarg);
825
826     return 0;
827   }
828
829   if (util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) {
830     mod_config()->dh_param_file = strcopy(optarg);
831
832     return 0;
833   }
834
835   if (util::strieq(opt, SHRPX_OPT_SUBCERT)) {
836     // Private Key file and certificate file separated by ':'.
837     const char *sp = strchr(optarg, ':');
838     if (sp) {
839       std::string keyfile(optarg, sp);
840       // TODO Do we need private key for subcert?
841       mod_config()->subcerts.emplace_back(keyfile, sp + 1);
842     }
843
844     return 0;
845   }
846
847   if (util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) {
848     int facility = int_syslog_facility(optarg);
849     if (facility == -1) {
850       LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
851       return -1;
852     }
853     mod_config()->syslog_facility = facility;
854
855     return 0;
856   }
857
858   if (util::strieq(opt, SHRPX_OPT_BACKLOG)) {
859     int n;
860     if (parse_int(&n, opt, optarg) != 0) {
861       return -1;
862     }
863
864     if (n < -1) {
865       LOG(ERROR) << opt << ": " << optarg << " is not allowed";
866
867       return -1;
868     }
869
870     mod_config()->backlog = n;
871
872     return 0;
873   }
874
875   if (util::strieq(opt, SHRPX_OPT_CIPHERS)) {
876     mod_config()->ciphers = strcopy(optarg);
877
878     return 0;
879   }
880
881   if (util::strieq(opt, SHRPX_OPT_CLIENT)) {
882     mod_config()->client = util::strieq(optarg, "yes");
883
884     return 0;
885   }
886
887   if (util::strieq(opt, SHRPX_OPT_INSECURE)) {
888     mod_config()->insecure = util::strieq(optarg, "yes");
889
890     return 0;
891   }
892
893   if (util::strieq(opt, SHRPX_OPT_CACERT)) {
894     mod_config()->cacert = strcopy(optarg);
895
896     return 0;
897   }
898
899   if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) {
900     mod_config()->backend_ipv4 = util::strieq(optarg, "yes");
901
902     return 0;
903   }
904
905   if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) {
906     mod_config()->backend_ipv6 = util::strieq(optarg, "yes");
907
908     return 0;
909   }
910
911   if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) {
912     // parse URI and get hostname, port and optionally userinfo.
913     http_parser_url u;
914     memset(&u, 0, sizeof(u));
915     int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u);
916     if (rv == 0) {
917       std::string val;
918       if (u.field_set & UF_USERINFO) {
919         http2::copy_url_component(val, &u, UF_USERINFO, optarg);
920         // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
921         // userinfo component is empty string.
922         if (!val.empty()) {
923           val = util::percentDecode(val.begin(), val.end());
924           mod_config()->downstream_http_proxy_userinfo = strcopy(val);
925         }
926       }
927       if (u.field_set & UF_HOST) {
928         http2::copy_url_component(val, &u, UF_HOST, optarg);
929         mod_config()->downstream_http_proxy_host = strcopy(val);
930       } else {
931         LOG(ERROR) << opt << ": no hostname specified";
932         return -1;
933       }
934       if (u.field_set & UF_PORT) {
935         mod_config()->downstream_http_proxy_port = u.port;
936       } else {
937         LOG(ERROR) << opt << ": no port specified";
938         return -1;
939       }
940     } else {
941       LOG(ERROR) << opt << ": parse error";
942       return -1;
943     }
944
945     return 0;
946   }
947
948   if (util::strieq(opt, SHRPX_OPT_READ_RATE)) {
949     return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg);
950   }
951
952   if (util::strieq(opt, SHRPX_OPT_READ_BURST)) {
953     return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg);
954   }
955
956   if (util::strieq(opt, SHRPX_OPT_WRITE_RATE)) {
957     return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg);
958   }
959
960   if (util::strieq(opt, SHRPX_OPT_WRITE_BURST)) {
961     return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg);
962   }
963
964   if (util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) {
965     LOG(WARN) << opt << ": not implemented yet";
966     return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg);
967   }
968
969   if (util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) {
970     LOG(WARN) << opt << ": not implemented yet";
971     return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg);
972   }
973
974   if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) {
975     LOG(WARN) << opt << ": not implemented yet";
976     return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg);
977   }
978
979   if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) {
980     LOG(WARN) << opt << ": not implemented yet";
981     return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg);
982   }
983
984   if (util::strieq(opt, SHRPX_OPT_NPN_LIST)) {
985     clear_config_str_list(mod_config()->npn_list);
986
987     mod_config()->npn_list = parse_config_str_list(optarg);
988
989     return 0;
990   }
991
992   if (util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) {
993     clear_config_str_list(mod_config()->tls_proto_list);
994
995     mod_config()->tls_proto_list = parse_config_str_list(optarg);
996
997     return 0;
998   }
999
1000   if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) {
1001     mod_config()->verify_client = util::strieq(optarg, "yes");
1002
1003     return 0;
1004   }
1005
1006   if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) {
1007     mod_config()->verify_client_cacert = strcopy(optarg);
1008
1009     return 0;
1010   }
1011
1012   if (util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) {
1013     mod_config()->client_private_key_file = strcopy(optarg);
1014
1015     return 0;
1016   }
1017
1018   if (util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) {
1019     mod_config()->client_cert_file = strcopy(optarg);
1020
1021     return 0;
1022   }
1023
1024   if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER)) {
1025     mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg);
1026
1027     return 0;
1028   }
1029
1030   if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER)) {
1031     mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg);
1032
1033     return 0;
1034   }
1035
1036   if (util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) {
1037     mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes");
1038
1039     return 0;
1040   }
1041
1042   if (util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) {
1043     mod_config()->upstream_frame_debug = util::strieq(optarg, "yes");
1044
1045     return 0;
1046   }
1047
1048   if (util::strieq(opt, SHRPX_OPT_PADDING)) {
1049     return parse_uint(&mod_config()->padding, opt, optarg);
1050   }
1051
1052   if (util::strieq(opt, SHRPX_OPT_ALTSVC)) {
1053     auto tokens = parse_config_str_list(optarg);
1054
1055     if (tokens.size() < 2) {
1056       // Requires at least protocol_id and port
1057       LOG(ERROR) << opt << ": too few parameters: " << optarg;
1058       return -1;
1059     }
1060
1061     if (tokens.size() > 4) {
1062       // We only need protocol_id, port, host and origin
1063       LOG(ERROR) << opt << ": too many parameters: " << optarg;
1064       return -1;
1065     }
1066
1067     int port;
1068
1069     if (parse_uint(&port, opt, tokens[1]) != 0) {
1070       return -1;
1071     }
1072
1073     if (port < 1 ||
1074         port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
1075       LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
1076       return -1;
1077     }
1078
1079     AltSvc altsvc;
1080
1081     altsvc.port = port;
1082
1083     altsvc.protocol_id = tokens[0];
1084     altsvc.protocol_id_len = strlen(altsvc.protocol_id);
1085
1086     if (tokens.size() > 2) {
1087       altsvc.host = tokens[2];
1088       altsvc.host_len = strlen(altsvc.host);
1089
1090       if (tokens.size() > 3) {
1091         altsvc.origin = tokens[3];
1092         altsvc.origin_len = strlen(altsvc.origin);
1093       }
1094     }
1095
1096     mod_config()->altsvcs.push_back(std::move(altsvc));
1097
1098     return 0;
1099   }
1100
1101   if (util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) {
1102     auto p = parse_header(optarg);
1103     if (p.first.empty()) {
1104       LOG(ERROR) << opt << ": header field name is empty: " << optarg;
1105       return -1;
1106     }
1107     mod_config()->add_response_headers.push_back(std::move(p));
1108
1109     return 0;
1110   }
1111
1112   if (util::strieq(opt, SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS)) {
1113     return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg);
1114   }
1115
1116   if (util::strieq(opt, SHRPX_OPT_NO_LOCATION_REWRITE)) {
1117     mod_config()->no_location_rewrite = util::strieq(optarg, "yes");
1118
1119     return 0;
1120   }
1121
1122   if (util::strieq(opt, SHRPX_OPT_NO_HOST_REWRITE)) {
1123     mod_config()->no_host_rewrite = util::strieq(optarg, "yes");
1124
1125     return 0;
1126   }
1127
1128   if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST)) {
1129     int n;
1130
1131     if (parse_uint(&n, opt, optarg) != 0) {
1132       return -1;
1133     }
1134
1135     if (n == 0) {
1136       LOG(ERROR) << opt << ": specify an integer strictly more than 0";
1137
1138       return -1;
1139     }
1140
1141     mod_config()->downstream_connections_per_host = n;
1142
1143     return 0;
1144   }
1145
1146   if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND)) {
1147     return parse_uint(&mod_config()->downstream_connections_per_frontend, opt,
1148                       optarg);
1149   }
1150
1151   if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) {
1152     return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg);
1153   }
1154
1155   if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) {
1156     mod_config()->tls_ticket_key_files.push_back(optarg);
1157     return 0;
1158   }
1159
1160   if (util::strieq(opt, SHRPX_OPT_RLIMIT_NOFILE)) {
1161     int n;
1162
1163     if (parse_uint(&n, opt, optarg) != 0) {
1164       return -1;
1165     }
1166
1167     if (n < 0) {
1168       LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
1169
1170       return -1;
1171     }
1172
1173     mod_config()->rlimit_nofile = n;
1174
1175     return 0;
1176   }
1177
1178   if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER) ||
1179       util::strieq(opt, SHRPX_OPT_BACKEND_RESPONSE_BUFFER)) {
1180     size_t n;
1181     if (parse_uint_with_unit(&n, opt, optarg) != 0) {
1182       return -1;
1183     }
1184
1185     if (n == 0) {
1186       LOG(ERROR) << opt << ": specify an integer strictly more than 0";
1187
1188       return -1;
1189     }
1190
1191     if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER)) {
1192       mod_config()->downstream_request_buffer_size = n;
1193     } else {
1194       mod_config()->downstream_response_buffer_size = n;
1195     }
1196
1197     return 0;
1198   }
1199
1200   if (util::strieq(opt, SHRPX_OPT_NO_SERVER_PUSH)) {
1201     mod_config()->no_server_push = util::strieq(optarg, "yes");
1202
1203     return 0;
1204   }
1205
1206   if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER)) {
1207     return parse_uint(&mod_config()->http2_downstream_connections_per_worker,
1208                       opt, optarg);
1209   }
1210
1211   if (util::strieq(opt, SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE)) {
1212     mod_config()->fetch_ocsp_response_file = strcopy(optarg);
1213
1214     return 0;
1215   }
1216
1217   if (util::strieq(opt, SHRPX_OPT_OCSP_UPDATE_INTERVAL)) {
1218     return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg);
1219   }
1220
1221   if (util::strieq(opt, SHRPX_OPT_NO_OCSP)) {
1222     mod_config()->no_ocsp = util::strieq(optarg, "yes");
1223
1224     return 0;
1225   }
1226
1227   if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) {
1228     return parse_uint_with_unit(&mod_config()->header_field_buffer, opt,
1229                                 optarg);
1230   }
1231
1232   if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) {
1233     return parse_uint(&mod_config()->max_header_fields, opt, optarg);
1234   }
1235
1236   if (util::strieq(opt, "conf")) {
1237     LOG(WARN) << "conf: ignored";
1238
1239     return 0;
1240   }
1241
1242   LOG(ERROR) << "Unknown option: " << opt;
1243
1244   return -1;
1245 }
1246
1247 int load_config(const char *filename) {
1248   std::ifstream in(filename, std::ios::binary);
1249   if (!in) {
1250     LOG(ERROR) << "Could not open config file " << filename;
1251     return -1;
1252   }
1253   std::string line;
1254   int linenum = 0;
1255   while (std::getline(in, line)) {
1256     ++linenum;
1257     if (line.empty() || line[0] == '#') {
1258       continue;
1259     }
1260     size_t i;
1261     size_t size = line.size();
1262     for (i = 0; i < size && line[i] != '='; ++i)
1263       ;
1264     if (i == size) {
1265       LOG(ERROR) << "Bad configuration format at line " << linenum;
1266       return -1;
1267     }
1268     line[i] = '\0';
1269     auto s = line.c_str();
1270     if (parse_config(s, s + i + 1) == -1) {
1271       return -1;
1272     }
1273   }
1274   return 0;
1275 }
1276
1277 const char *str_syslog_facility(int facility) {
1278   switch (facility) {
1279   case (LOG_AUTH):
1280     return "auth";
1281   case (LOG_AUTHPRIV):
1282     return "authpriv";
1283   case (LOG_CRON):
1284     return "cron";
1285   case (LOG_DAEMON):
1286     return "daemon";
1287   case (LOG_FTP):
1288     return "ftp";
1289   case (LOG_KERN):
1290     return "kern";
1291   case (LOG_LOCAL0):
1292     return "local0";
1293   case (LOG_LOCAL1):
1294     return "local1";
1295   case (LOG_LOCAL2):
1296     return "local2";
1297   case (LOG_LOCAL3):
1298     return "local3";
1299   case (LOG_LOCAL4):
1300     return "local4";
1301   case (LOG_LOCAL5):
1302     return "local5";
1303   case (LOG_LOCAL6):
1304     return "local6";
1305   case (LOG_LOCAL7):
1306     return "local7";
1307   case (LOG_LPR):
1308     return "lpr";
1309   case (LOG_MAIL):
1310     return "mail";
1311   case (LOG_SYSLOG):
1312     return "syslog";
1313   case (LOG_USER):
1314     return "user";
1315   case (LOG_UUCP):
1316     return "uucp";
1317   default:
1318     return "(unknown)";
1319   }
1320 }
1321
1322 int int_syslog_facility(const char *strfacility) {
1323   if (util::strieq(strfacility, "auth")) {
1324     return LOG_AUTH;
1325   }
1326
1327   if (util::strieq(strfacility, "authpriv")) {
1328     return LOG_AUTHPRIV;
1329   }
1330
1331   if (util::strieq(strfacility, "cron")) {
1332     return LOG_CRON;
1333   }
1334
1335   if (util::strieq(strfacility, "daemon")) {
1336     return LOG_DAEMON;
1337   }
1338
1339   if (util::strieq(strfacility, "ftp")) {
1340     return LOG_FTP;
1341   }
1342
1343   if (util::strieq(strfacility, "kern")) {
1344     return LOG_KERN;
1345   }
1346
1347   if (util::strieq(strfacility, "local0")) {
1348     return LOG_LOCAL0;
1349   }
1350
1351   if (util::strieq(strfacility, "local1")) {
1352     return LOG_LOCAL1;
1353   }
1354
1355   if (util::strieq(strfacility, "local2")) {
1356     return LOG_LOCAL2;
1357   }
1358
1359   if (util::strieq(strfacility, "local3")) {
1360     return LOG_LOCAL3;
1361   }
1362
1363   if (util::strieq(strfacility, "local4")) {
1364     return LOG_LOCAL4;
1365   }
1366
1367   if (util::strieq(strfacility, "local5")) {
1368     return LOG_LOCAL5;
1369   }
1370
1371   if (util::strieq(strfacility, "local6")) {
1372     return LOG_LOCAL6;
1373   }
1374
1375   if (util::strieq(strfacility, "local7")) {
1376     return LOG_LOCAL7;
1377   }
1378
1379   if (util::strieq(strfacility, "lpr")) {
1380     return LOG_LPR;
1381   }
1382
1383   if (util::strieq(strfacility, "mail")) {
1384     return LOG_MAIL;
1385   }
1386
1387   if (util::strieq(strfacility, "news")) {
1388     return LOG_NEWS;
1389   }
1390
1391   if (util::strieq(strfacility, "syslog")) {
1392     return LOG_SYSLOG;
1393   }
1394
1395   if (util::strieq(strfacility, "user")) {
1396     return LOG_USER;
1397   }
1398
1399   if (util::strieq(strfacility, "uucp")) {
1400     return LOG_UUCP;
1401   }
1402
1403   return -1;
1404 }
1405
1406 } // namespace shrpx