7 # include <sys/socket.h>
20 #include <event2/util.h>
23 #include "dnscrypt_proxy.h"
30 #include "windows_service.h"
32 # include "plugin_options.h"
35 static struct option getopt_long_options
[] = {
36 { "local-address", 1, NULL
, 'a' },
38 { "daemonize", 0, NULL
, 'd' },
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' },
50 { "pidfile", 1, NULL
, 'p' },
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' },
61 { "install", 0, NULL
, WIN_OPTION_INSTALL
},
62 { "reinstall", 0, NULL
, WIN_OPTION_REINSTALL
},
63 { "uninstall", 0, NULL
, WIN_OPTION_UNINSTALL
},
68 static const char *getopt_options
= "a:de:Ehk:K:L:l:m:n:p:r:R:t:u:N:TVX";
70 static const char *getopt_options
= "a:e:Ehk:K:L:l:m:n:r:R:t:u:N:TVX";
73 #ifndef DEFAULT_CONNECTIONS_COUNT_MAX
74 # define DEFAULT_CONNECTIONS_COUNT_MAX 250U
86 const struct option
*options
= getopt_long_options
;
91 if (options
->val
< 256) {
92 printf(" -%c\t--%s%s\n", options
->val
, options
->name
,
93 options
->has_arg
? "=..." : "");
95 printf(" \t--%s%s\n", options
->name
,
96 options
->has_arg
? "=..." : "");
99 } while (options
->name
!= NULL
);
100 puts("\nPlease consult the dnscrypt-proxy(8) man page for details.\n");
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
;
123 proxy_context
->user_id
= (uid_t
) 0;
124 proxy_context
->user_group
= (uid_t
) 0;
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;
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
] != '.') {
149 options_read_file(const char * const file_name
)
153 size_t file_size
= (size_t) 0U;
155 assert(file_name
!= NULL
);
156 if ((fp
= fopen(file_name
, "rb")) == NULL
) {
159 while (fgetc(fp
) != EOF
&& file_size
< SIZE_MAX
) {
162 if (feof(fp
) == 0 || file_size
<= (size_t) 0U) {
167 if ((file_buf
= malloc(file_size
)) == NULL
) {
171 if (fread(file_buf
, file_size
, (size_t) 1U, fp
) != 1U) {
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
) {
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
)
206 const char *namecoin
;
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) {
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
,
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]",
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]",
238 if (resolver_ip
== NULL
|| *resolver_ip
== 0) {
239 logger(proxy_context
, LOG_ERR
,
240 "Resolvers list is missing a resolver address for [%s]",
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",
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",
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",
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");
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
];
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) {
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] == '#') {
309 if (options_parse_resolver(proxy_context
, headers
, headers_count
,
310 cols
, cols_count
) > 0) {
319 options_use_resolver_name(ProxyContext
* const proxy_context
)
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");
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
);
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
);
342 free(resolvers_list_rebased
);
348 options_use_client_key_file(ProxyContext
* const proxy_context
)
352 const size_t header_len
= (sizeof OPTIONS_CLIENT_KEY_HEADER
) - 1U;
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");
359 if ((key
= sodium_malloc(header_len
+ crypto_box_SECRETKEYBYTES
)) == NULL
) {
360 logger_noformat(proxy_context
, LOG_EMERG
, "Out of memory");
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");
374 sodium_memzero(key_s
, strlen(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
);
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");
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
);
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");
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
);
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.");
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 "
440 logger_noformat(proxy_context
, LOG_ERR
,
441 "Please consult http://dnscrypt.org "
442 "and the dnscrypt-proxy(8) man page for details.");
446 if (proxy_context
->provider_name
== NULL
||
447 *proxy_context
->provider_name
== 0) {
448 logger_noformat(proxy_context
, LOG_ERR
, "Provider name required");
451 if (options_check_protocol_versions(proxy_context
->provider_name
) != 0) {
452 logger_noformat(proxy_context
, LOG_ERR
,
453 "Unsupported server protocol version");
456 if (proxy_context
->provider_publickey_s
== NULL
) {
457 logger_noformat(proxy_context
, LOG_ERR
, "Provider key required");
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");
465 if (proxy_context
->daemonize
) {
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");
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");
481 if (proxy_context
->log_fp
== NULL
&& proxy_context
->daemonize
) {
482 logger_open_syslog(proxy_context
);
488 options_parse(AppContext
* const app_context
,
489 ProxyContext
* const proxy_context
, int argc
, char *argv
[])
492 int option_index
= 0;
494 _Bool option_install
= 0;
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) {
503 proxy_context
->local_ip
= optarg
;
506 proxy_context
->daemonize
= 1;
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
);
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
;
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
;
533 proxy_context
->ephemeral_keys
= 1;
539 proxy_context
->provider_publickey_s
= optarg
;
542 proxy_context
->client_key_file
= optarg
;
545 proxy_context
->log_file
= optarg
;
548 proxy_context
->resolvers_list
= optarg
;
551 proxy_context
->resolver_name
= optarg
;
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
);
562 proxy_context
->max_log_level
= max_log_level
;
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
);
577 proxy_context
->connections_count_max
=
578 (unsigned int) connections_count_max
;
582 proxy_context
->pid_file
= optarg
;
585 proxy_context
->resolver_ip
= optarg
;
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
);
598 proxy_context
->test_cert_margin
= (time_t) margin
* (time_t) 60U;
599 proxy_context
->test_only
= 1;
604 const struct passwd
* const pw
= getpwnam(optarg
);
606 logger(proxy_context
, LOG_ERR
, "Unknown user: [%s]", optarg
);
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
);
616 proxy_context
->provider_name
= optarg
;
619 proxy_context
->tcp_only
= 1;
626 logger_noformat(proxy_context
, LOG_ERR
,
627 "Support for plugins hasn't been compiled in");
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");
639 case WIN_OPTION_INSTALL
:
640 case WIN_OPTION_REINSTALL
:
643 case WIN_OPTION_UNINSTALL
:
644 if (windows_service_uninstall() != 0) {
645 logger_noformat(NULL
, LOG_ERR
, "Unable to uninstall the service");
648 logger_noformat(NULL
, LOG_INFO
, "The " WINDOWS_SERVICE_NAME
649 " service has been removed from this system");
659 if (options_apply(proxy_context
) != 0) {
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");
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
);
684 options_free(ProxyContext
* const proxy_context
)
686 free(proxy_context
->user_dir
);
687 proxy_context
->user_dir
= NULL
;