dnscrypt-proxy 1.6.0
[tomato.git] / release / src / router / dnscrypt / src / proxy / options.c
blob5c55c035512d7cefc634ecfd884e2ccc45512ef5
2 #include <config.h>
3 #include <sys/types.h>
4 #ifdef _WIN32
5 # include <winsock2.h>
6 #else
7 # include <sys/socket.h>
8 #endif
10 #include <assert.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <limits.h>
14 #include <stdio.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
20 #include <event2/util.h>
21 #include <sodium.h>
23 #include "dnscrypt_proxy.h"
24 #include "getpwnam.h"
25 #include "options.h"
26 #include "logger.h"
27 #include "minicsv.h"
28 #include "pid_file.h"
29 #include "utils.h"
30 #include "windows_service.h"
31 #ifdef PLUGINS
32 # include "plugin_options.h"
33 #endif
35 static struct option getopt_long_options[] = {
36 { "local-address", 1, NULL, 'a' },
37 #ifndef _WIN32
38 { "daemonize", 0, NULL, 'd' },
39 #endif
40 { "edns-payload-size", 1, NULL, 'e' },
41 { "ephemeral-keys", 0, NULL, 'E' },
42 { "client-key", 1, NULL, 'K' },
43 { "help", 0, NULL, 'h' },
44 { "resolvers-list", 1, NULL, 'L' },
45 { "resolver-name", 1, NULL, 'R' },
46 { "logfile", 1, NULL, 'l' },
47 { "loglevel", 1, NULL, 'm' },
48 { "max-active-requests", 1, NULL, 'n' },
49 #ifndef _WIN32
50 { "pidfile", 1, NULL, 'p' },
51 #endif
52 { "plugin", 1, NULL, 'X' },
53 { "provider-name", 1, NULL, 'N' },
54 { "provider-key", 1, NULL, 'k' },
55 { "resolver-address", 1, NULL, 'r' },
56 { "user", 1, NULL, 'u' },
57 { "test", 1, NULL, 't' },
58 { "tcp-only", 0, NULL, 'T' },
59 { "version", 0, NULL, 'V' },
60 #ifdef _WIN32
61 { "install", 0, NULL, WIN_OPTION_INSTALL },
62 { "reinstall", 0, NULL, WIN_OPTION_REINSTALL },
63 { "uninstall", 0, NULL, WIN_OPTION_UNINSTALL },
64 #endif
65 { NULL, 0, NULL, 0 }
67 #ifndef _WIN32
68 static const char *getopt_options = "a:de:Ehk:K:L:l:m:n:p:r:R:t:u:N:TVX";
69 #else
70 static const char *getopt_options = "a:e:Ehk:K:L:l:m:n:r:R:t:u:N:TVX";
71 #endif
73 #ifndef DEFAULT_CONNECTIONS_COUNT_MAX
74 # define DEFAULT_CONNECTIONS_COUNT_MAX 250U
75 #endif
77 static void
78 options_version(void)
80 puts(PACKAGE_STRING);
83 static void
84 options_usage(void)
86 const struct option *options = getopt_long_options;
88 options_version();
89 puts("\nOptions:\n");
90 do {
91 if (options->val < 256) {
92 printf(" -%c\t--%s%s\n", options->val, options->name,
93 options->has_arg ? "=..." : "");
94 } else {
95 printf(" \t--%s%s\n", options->name,
96 options->has_arg ? "=..." : "");
98 options++;
99 } while (options->name != NULL);
100 puts("\nPlease consult the dnscrypt-proxy(8) man page for details.\n");
103 static
104 void options_init_with_default(AppContext * const app_context,
105 ProxyContext * const proxy_context)
107 assert(proxy_context->event_loop == NULL);
108 proxy_context->app_context = app_context;
109 proxy_context->connections_count = 0U;
110 proxy_context->connections_count_max = DEFAULT_CONNECTIONS_COUNT_MAX;
111 proxy_context->edns_payload_size = (size_t) DNS_DEFAULT_EDNS_PAYLOAD_SIZE;
112 proxy_context->client_key_file = NULL;
113 proxy_context->local_ip = "127.0.0.1:53";
114 proxy_context->log_fp = NULL;
115 proxy_context->log_file = NULL;
116 proxy_context->pid_file = NULL;
117 proxy_context->resolvers_list = DEFAULT_RESOLVERS_LIST;
118 proxy_context->resolver_name = DEFAULT_RESOLVER_NAME;
119 proxy_context->provider_name = NULL;
120 proxy_context->provider_publickey_s = NULL;
121 proxy_context->resolver_ip = NULL;
122 #ifndef _WIN32
123 proxy_context->user_id = (uid_t) 0;
124 proxy_context->user_group = (uid_t) 0;
125 #endif
126 proxy_context->user_dir = NULL;
127 proxy_context->daemonize = 0;
128 proxy_context->test_cert_margin = (time_t) -1;
129 proxy_context->test_only = 0;
130 proxy_context->tcp_only = 0;
131 proxy_context->ephemeral_keys = 0;
134 static int
135 options_check_protocol_versions(const char * const provider_name)
137 const size_t dnscrypt_protocol_versions_len =
138 sizeof DNSCRYPT_PROTOCOL_VERSIONS - (size_t) 1U;
140 if (strncmp(provider_name, DNSCRYPT_PROTOCOL_VERSIONS,
141 dnscrypt_protocol_versions_len) != 0 ||
142 provider_name[dnscrypt_protocol_versions_len] != '.') {
143 return -1;
145 return 0;
148 static char *
149 options_read_file(const char * const file_name)
151 FILE *fp;
152 char *file_buf;
153 size_t file_size = (size_t) 0U;
155 assert(file_name != NULL);
156 if ((fp = fopen(file_name, "rb")) == NULL) {
157 return NULL;
159 while (fgetc(fp) != EOF && file_size < SIZE_MAX) {
160 file_size++;
162 if (feof(fp) == 0 || file_size <= (size_t) 0U) {
163 fclose(fp);
164 return NULL;
166 rewind(fp);
167 if ((file_buf = malloc(file_size)) == NULL) {
168 fclose(fp);
169 return NULL;
171 if (fread(file_buf, file_size, (size_t) 1U, fp) != 1U) {
172 fclose(fp);
173 free(file_buf);
174 return NULL;
176 (void) fclose(fp);
178 return file_buf;
181 static const char *
182 options_get_col(char * const * const headers, const size_t headers_count,
183 char * const * const cols, const size_t cols_count,
184 const char * const header)
186 size_t i = (size_t) 0U;
188 while (i < headers_count) {
189 if (strcmp(header, headers[i]) == 0) {
190 if (i < cols_count) {
191 return cols[i];
193 break;
195 i++;
197 return NULL;
200 static int
201 options_parse_resolver(ProxyContext * const proxy_context,
202 char * const * const headers, const size_t headers_count,
203 char * const * const cols, const size_t cols_count)
205 const char *dnssec;
206 const char *namecoin;
207 const char *nologs;
208 const char *provider_name;
209 const char *provider_publickey_s;
210 const char *resolver_ip;
211 const char *resolver_name;
213 resolver_name = options_get_col(headers, headers_count,
214 cols, cols_count, "Name");
215 if (evutil_ascii_strcasecmp(resolver_name,
216 proxy_context->resolver_name) != 0) {
217 return 0;
219 provider_name = options_get_col(headers, headers_count,
220 cols, cols_count, "Provider name");
221 provider_publickey_s = options_get_col(headers, headers_count,
222 cols, cols_count,
223 "Provider public key");
224 resolver_ip = options_get_col(headers, headers_count,
225 cols, cols_count, "Resolver address");
226 if (provider_name == NULL || *provider_name == 0) {
227 logger(proxy_context, LOG_ERR,
228 "Resolvers list is missing a provider name for [%s]",
229 resolver_name);
230 return -1;
232 if (provider_publickey_s == NULL || *provider_publickey_s == 0) {
233 logger(proxy_context, LOG_ERR,
234 "Resolvers list is missing a public key for [%s]",
235 resolver_name);
236 return -1;
238 if (resolver_ip == NULL || *resolver_ip == 0) {
239 logger(proxy_context, LOG_ERR,
240 "Resolvers list is missing a resolver address for [%s]",
241 resolver_name);
242 return -1;
244 dnssec = options_get_col(headers, headers_count,
245 cols, cols_count, "DNSSEC validation");
246 if (dnssec != NULL && strcasecmp(dnssec, "yes") != 0) {
247 logger(proxy_context, LOG_INFO,
248 "- [%s] does not support DNS Security Extensions",
249 resolver_name);
250 } else {
251 logger(proxy_context, LOG_INFO,
252 "+ DNS Security Extensions are supported");
254 namecoin = options_get_col(headers, headers_count,
255 cols, cols_count, "Namecoin");
256 if (namecoin != NULL && strcasecmp(namecoin, "yes") != 0) {
257 logger(proxy_context, LOG_INFO,
258 "- [%s] does not support Namecoin domains",
259 resolver_name);
260 } else {
261 logger(proxy_context, LOG_INFO,
262 "+ Namecoin domains can be resolved");
264 nologs = options_get_col(headers, headers_count,
265 cols, cols_count, "No logs");
266 if (nologs != NULL && strcasecmp(nologs, "no") == 0) {
267 logger(proxy_context, LOG_WARNING,
268 "- [%s] logs your activity - "
269 "a different provider might be better a choice if privacy is a concern",
270 resolver_name);
271 } else {
272 logger(proxy_context, LOG_INFO,
273 "+ Provider supposedly doesn't keep logs");
276 proxy_context->provider_name = strdup(provider_name);
277 proxy_context->provider_publickey_s = strdup(provider_publickey_s);
278 proxy_context->resolver_ip = strdup(resolver_ip);
279 if (proxy_context->provider_name == NULL ||
280 proxy_context->provider_publickey_s == NULL ||
281 proxy_context->resolver_ip == NULL) {
282 logger_noformat(proxy_context, LOG_EMERG, "Out of memory");
283 exit(1);
285 return 1;
288 static int
289 options_parse_resolvers_list(ProxyContext * const proxy_context, char *buf)
291 char *cols[OPTIONS_RESOLVERS_LIST_MAX_COLS];
292 char *headers[OPTIONS_RESOLVERS_LIST_MAX_COLS];
293 size_t cols_count;
294 size_t headers_count;
296 assert(proxy_context->resolver_name != NULL);
297 buf = minicsv_parse_line(buf, headers, &headers_count,
298 sizeof headers / sizeof headers[0]);
299 if (headers_count < 4U) {
300 return -1;
302 do {
303 buf = minicsv_parse_line(buf, cols, &cols_count,
304 sizeof cols / sizeof cols[0]);
305 minicsv_trim_cols(cols, cols_count);
306 if (cols_count < 4U || *cols[0] == 0 || *cols[0] == '#') {
307 continue;
309 if (options_parse_resolver(proxy_context, headers, headers_count,
310 cols, cols_count) > 0) {
311 return 0;
313 } while (*buf != 0);
315 return -1;
318 static int
319 options_use_resolver_name(ProxyContext * const proxy_context)
321 char *file_buf;
322 char *resolvers_list_rebased;
324 if ((resolvers_list_rebased =
325 path_from_app_folder(proxy_context->resolvers_list)) == NULL) {
326 logger_noformat(proxy_context, LOG_EMERG, "Out of memory");
327 exit(1);
329 file_buf = options_read_file(resolvers_list_rebased);
330 if (file_buf == NULL) {
331 logger(proxy_context, LOG_ERR, "Unable to read [%s]",
332 resolvers_list_rebased);
333 exit(1);
335 assert(proxy_context->resolver_name != NULL);
336 if (options_parse_resolvers_list(proxy_context, file_buf) < 0) {
337 logger(proxy_context, LOG_ERR,
338 "No resolver named [%s] found in the [%s] list",
339 proxy_context->resolver_name, resolvers_list_rebased);
341 free(file_buf);
342 free(resolvers_list_rebased);
344 return 0;
347 static int
348 options_use_client_key_file(ProxyContext * const proxy_context)
350 unsigned char *key;
351 char *key_s;
352 const size_t header_len = (sizeof OPTIONS_CLIENT_KEY_HEADER) - 1U;
353 size_t key_s_len;
355 if ((key_s = options_read_file(proxy_context->client_key_file)) == NULL) {
356 logger_error(proxy_context, "Unable to read the client key file");
357 return -1;
359 if ((key = sodium_malloc(header_len + crypto_box_SECRETKEYBYTES)) == NULL) {
360 logger_noformat(proxy_context, LOG_EMERG, "Out of memory");
361 free(key_s);
362 return -1;
364 if (sodium_hex2bin(key, header_len + crypto_box_SECRETKEYBYTES,
365 key_s, strlen(key_s), ": -", &key_s_len, NULL) != 0 ||
366 key_s_len < (header_len + crypto_box_SECRETKEYBYTES) ||
367 memcmp(key, OPTIONS_CLIENT_KEY_HEADER, header_len) != 0) {
368 logger_noformat(proxy_context, LOG_ERR,
369 "The client key file doesn't seem to contain a supported key format");
370 sodium_free(key);
371 free(key_s);
372 return -1;
374 sodium_memzero(key_s, strlen(key_s));
375 free(key_s);
376 assert(sizeof proxy_context->dnscrypt_client.secretkey <=
377 key_s_len - header_len);
378 memcpy(proxy_context->dnscrypt_client.secretkey, key + header_len,
379 sizeof proxy_context->dnscrypt_client.secretkey);
380 sodium_free(key);
382 return 0;
385 static int
386 options_apply(ProxyContext * const proxy_context)
388 if (proxy_context->client_key_file != NULL) {
389 if (proxy_context->ephemeral_keys != 0) {
390 logger_noformat(proxy_context, LOG_ERR,
391 "--client-key and --ephemeral-keys are mutually exclusive");
392 exit(1);
394 if (options_use_client_key_file(proxy_context) != 0) {
395 logger(proxy_context, LOG_ERR,
396 "Client key file [%s] could not be used", proxy_context->client_key_file);
397 exit(1);
400 if (proxy_context->resolver_name != NULL) {
401 if (proxy_context->resolvers_list == NULL) {
402 logger_noformat(proxy_context, LOG_ERR,
403 "Resolvers list (-L command-line switch) required");
404 exit(1);
406 if (options_use_resolver_name(proxy_context) != 0) {
407 logger(proxy_context, LOG_ERR,
408 "Resolver name (-R command-line switch) required. "
409 "See [%s] for a list of public resolvers.",
410 proxy_context->resolvers_list);
411 exit(1);
414 if (proxy_context->resolver_ip == NULL ||
415 *proxy_context->resolver_ip == 0 ||
416 proxy_context->provider_name == NULL ||
417 *proxy_context->provider_name == 0 ||
418 proxy_context->provider_publickey_s == NULL ||
419 *proxy_context->provider_publickey_s == 0) {
420 logger_noformat(proxy_context, LOG_ERR,
421 "Resolver information required.");
422 logger_noformat(proxy_context, LOG_ERR,
423 "The easiest way to do so is to provide a resolver name.");
424 logger_noformat(proxy_context, LOG_ERR,
425 "Example: dnscrypt-proxy -R mydnsprovider");
426 logger(proxy_context, LOG_ERR,
427 "See the file [%s] for a list of compatible public resolvers",
428 proxy_context->resolvers_list);
429 logger_noformat(proxy_context, LOG_ERR,
430 "The name is the first column in this table.");
431 logger_noformat(proxy_context, LOG_ERR,
432 "Alternatively, an IP address, a provider name "
433 "and a provider key can be supplied.");
434 #ifdef _WIN32
435 logger_noformat(proxy_context, LOG_ERR,
436 "Consult http://dnscrypt.org "
437 "and https://github.com/jedisct1/dnscrypt-proxy/blob/master/README-WINDOWS.markdown "
438 "for details.");
439 #else
440 logger_noformat(proxy_context, LOG_ERR,
441 "Please consult http://dnscrypt.org "
442 "and the dnscrypt-proxy(8) man page for details.");
443 #endif
444 exit(1);
446 if (proxy_context->provider_name == NULL ||
447 *proxy_context->provider_name == 0) {
448 logger_noformat(proxy_context, LOG_ERR, "Provider name required");
449 exit(1);
451 if (options_check_protocol_versions(proxy_context->provider_name) != 0) {
452 logger_noformat(proxy_context, LOG_ERR,
453 "Unsupported server protocol version");
454 exit(1);
456 if (proxy_context->provider_publickey_s == NULL) {
457 logger_noformat(proxy_context, LOG_ERR, "Provider key required");
458 exit(1);
460 if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s,
461 proxy_context->provider_publickey) != 0) {
462 logger_noformat(proxy_context, LOG_ERR, "Invalid provider key");
463 exit(1);
465 if (proxy_context->daemonize) {
466 do_daemonize();
468 #ifndef _WIN32
469 if (proxy_context->pid_file != NULL &&
470 pid_file_create(proxy_context->pid_file,
471 proxy_context->user_id != (uid_t) 0) != 0) {
472 logger_error(proxy_context, "Unable to create pid file");
473 exit(1);
475 #endif
476 if (proxy_context->log_file != NULL &&
477 (proxy_context->log_fp = fopen(proxy_context->log_file, "a")) == NULL) {
478 logger_error(proxy_context, "Unable to open log file");
479 exit(1);
481 if (proxy_context->log_fp == NULL && proxy_context->daemonize) {
482 logger_open_syslog(proxy_context);
484 return 0;
488 options_parse(AppContext * const app_context,
489 ProxyContext * const proxy_context, int argc, char *argv[])
491 int opt_flag;
492 int option_index = 0;
493 #ifdef _WIN32
494 _Bool option_install = 0;
495 #endif
497 options_init_with_default(app_context, proxy_context);
498 while ((opt_flag = getopt_long(argc, argv,
499 getopt_options, getopt_long_options,
500 &option_index)) != -1) {
501 switch (opt_flag) {
502 case 'a':
503 proxy_context->local_ip = optarg;
504 break;
505 case 'd':
506 proxy_context->daemonize = 1;
507 break;
508 case 'e': {
509 char *endptr;
510 const unsigned long edns_payload_size = strtoul(optarg, &endptr, 10);
512 if (*optarg == 0 || *endptr != 0 ||
513 edns_payload_size > DNS_MAX_PACKET_SIZE_UDP_RECV) {
514 logger(proxy_context, LOG_ERR,
515 "Invalid EDNS payload size: [%s]", optarg);
516 exit(1);
518 if (edns_payload_size <= DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND) {
519 proxy_context->edns_payload_size = (size_t) 0U;
520 proxy_context->udp_max_size = DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND;
521 } else {
522 proxy_context->edns_payload_size = (size_t) edns_payload_size;
523 assert(proxy_context->udp_max_size >=
524 DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND);
525 if (proxy_context->edns_payload_size > DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND) {
526 proxy_context->udp_max_size =
527 proxy_context->edns_payload_size;
530 break;
532 case 'E':
533 proxy_context->ephemeral_keys = 1;
534 break;
535 case 'h':
536 options_usage();
537 exit(0);
538 case 'k':
539 proxy_context->provider_publickey_s = optarg;
540 break;
541 case 'K':
542 proxy_context->client_key_file = optarg;
543 break;
544 case 'l':
545 proxy_context->log_file = optarg;
546 break;
547 case 'L':
548 proxy_context->resolvers_list = optarg;
549 break;
550 case 'R':
551 proxy_context->resolver_name = optarg;
552 break;
553 case 'm': {
554 char *endptr;
555 const long max_log_level = strtol(optarg, &endptr, 10);
557 if (*optarg == 0 || *endptr != 0 || max_log_level < 0) {
558 logger(proxy_context, LOG_ERR,
559 "Invalid max log level: [%s]", optarg);
560 exit(1);
562 proxy_context->max_log_level = max_log_level;
563 break;
565 case 'n': {
566 char *endptr;
567 const unsigned long connections_count_max =
568 strtoul(optarg, &endptr, 10);
570 if (*optarg == 0 || *endptr != 0 ||
571 connections_count_max <= 0U ||
572 connections_count_max > UINT_MAX) {
573 logger(proxy_context, LOG_ERR,
574 "Invalid max number of active request: [%s]", optarg);
575 exit(1);
577 proxy_context->connections_count_max =
578 (unsigned int) connections_count_max;
579 break;
581 case 'p':
582 proxy_context->pid_file = optarg;
583 break;
584 case 'r':
585 proxy_context->resolver_ip = optarg;
586 break;
587 case 't': {
588 char *endptr;
589 const unsigned long margin =
590 strtoul(optarg, &endptr, 10);
592 if (*optarg == 0 || *endptr != 0 ||
593 margin > UINT32_MAX / 60U) {
594 logger(proxy_context, LOG_ERR,
595 "Invalid certificate grace period: [%s]", optarg);
596 exit(1);
598 proxy_context->test_cert_margin = (time_t) margin * (time_t) 60U;
599 proxy_context->test_only = 1;
600 break;
602 #ifdef HAVE_GETPWNAM
603 case 'u': {
604 const struct passwd * const pw = getpwnam(optarg);
605 if (pw == NULL) {
606 logger(proxy_context, LOG_ERR, "Unknown user: [%s]", optarg);
607 exit(1);
609 proxy_context->user_id = pw->pw_uid;
610 proxy_context->user_group = pw->pw_gid;
611 proxy_context->user_dir = strdup(pw->pw_dir);
612 break;
614 #endif
615 case 'N':
616 proxy_context->provider_name = optarg;
617 break;
618 case 'T':
619 proxy_context->tcp_only = 1;
620 break;
621 case 'V':
622 options_version();
623 exit(0);
624 case 'X':
625 #ifndef PLUGINS
626 logger_noformat(proxy_context, LOG_ERR,
627 "Support for plugins hasn't been compiled in");
628 exit(1);
629 #else
630 if (plugin_options_parse_str
631 (proxy_context->app_context->dcps_context, optarg) != 0) {
632 logger_noformat(proxy_context, LOG_ERR,
633 "Error while parsing plugin options");
634 exit(2);
636 #endif
637 break;
638 #ifdef _WIN32
639 case WIN_OPTION_INSTALL:
640 case WIN_OPTION_REINSTALL:
641 option_install = 1;
642 break;
643 case WIN_OPTION_UNINSTALL:
644 if (windows_service_uninstall() != 0) {
645 logger_noformat(NULL, LOG_ERR, "Unable to uninstall the service");
646 exit(1);
647 } else {
648 logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME
649 " service has been removed from this system");
650 exit(0);
652 break;
653 #endif
654 default:
655 options_usage();
656 exit(1);
659 if (options_apply(proxy_context) != 0) {
660 return -1;
662 #ifdef _WIN32
663 if (option_install != 0) {
664 if (windows_service_install(proxy_context) != 0) {
665 logger_noformat(NULL, LOG_ERR, "Unable to install the service");
666 logger_noformat(NULL, LOG_ERR,
667 "Make sure that you are using an elevated command prompt "
668 "and that the service hasn't been already installed");
669 exit(1);
671 logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME
672 " service has been installed and started");
673 logger_noformat(NULL, LOG_INFO, "The registry key used for this "
674 "service is " WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY);
675 logger(NULL, LOG_INFO, "Now, change your resolver settings to %s",
676 proxy_context->local_ip);
677 exit(0);
679 #endif
680 return 0;
683 void
684 options_free(ProxyContext * const proxy_context)
686 free(proxy_context->user_dir);
687 proxy_context->user_dir = NULL;