static void cert_query_cb(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { Bincert *bincert = NULL; ProxyContext *proxy_context = arg; struct ares_txt_reply *txt_out; struct ares_txt_reply *txt_out_current; (void) timeouts; DNSCRYPT_PROXY_CERTS_UPDATE_RECEIVED(); if (status != ARES_SUCCESS || ares_parse_txt_reply(abuf, alen, &txt_out) != ARES_SUCCESS) { logger_noformat(proxy_context, LOG_ERR, "Unable to retrieve server certificates"); cert_reschedule_query_after_failure(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_COMMUNICATION(); return; } txt_out_current = txt_out; while (txt_out_current != NULL) { cert_open_bincert(proxy_context, (const SignedBincert *) txt_out_current->txt, txt_out_current->length, &bincert); txt_out_current = txt_out_current->next; } ares_free_data(txt_out); if (bincert == NULL) { logger_noformat(proxy_context, LOG_ERR, "No useable certificates found"); cert_reschedule_query_after_failure(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_NOCERTS(); return; } COMPILER_ASSERT(sizeof proxy_context->resolver_publickey == sizeof bincert->server_publickey); memcpy(proxy_context->resolver_publickey, bincert->server_publickey, sizeof proxy_context->resolver_publickey); COMPILER_ASSERT(sizeof proxy_context->dnscrypt_magic_query == sizeof bincert->magic_query); memcpy(proxy_context->dnscrypt_magic_query, bincert->magic_query, sizeof proxy_context->dnscrypt_magic_query); cert_print_server_key(proxy_context); dnscrypt_client_init_magic_query(&proxy_context->dnscrypt_client, bincert->magic_query); memset(bincert, 0, sizeof *bincert); free(bincert); dnscrypt_client_init_nmkey(&proxy_context->dnscrypt_client, proxy_context->resolver_publickey); dnscrypt_proxy_start_listeners(proxy_context); proxy_context->cert_updater.query_retry_step = 0U; cert_reschedule_query_after_success(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_DONE((unsigned char *) proxy_context->resolver_publickey); }
static int cert_parse_bincert(ProxyContext * const proxy_context, const Bincert * const bincert, const Bincert * const previous_bincert) { uint32_t serial; memcpy(&serial, bincert->serial, sizeof serial); serial = htonl(serial); logger(proxy_context, LOG_INFO, "Server certificate #%" PRIu32 " received", serial); uint32_t ts_begin; memcpy(&ts_begin, bincert->ts_begin, sizeof ts_begin); ts_begin = htonl(ts_begin); uint32_t ts_end; memcpy(&ts_end, bincert->ts_end, sizeof ts_end); ts_end = htonl(ts_end); const uint32_t now_u32 = (uint32_t) time(NULL); if (now_u32 < ts_begin) { logger_noformat(proxy_context, LOG_INFO, "This certificate has not been activated yet"); return -1; } if (now_u32 > ts_end) { logger_noformat(proxy_context, LOG_INFO, "This certificate has expired"); return -1; } logger_noformat(proxy_context, LOG_INFO, "This certificate looks valid"); if (previous_bincert == NULL) { return 0; } uint32_t previous_serial; memcpy(&previous_serial, previous_bincert->serial, sizeof previous_serial); previous_serial = htonl(previous_serial); if (previous_serial > serial) { logger(proxy_context, LOG_INFO, "Certificate #%" PRIu32 " " "has been superseded by certificate #%" PRIu32, previous_serial, serial); return -1; } logger(proxy_context, LOG_INFO, "This certificate supersedes certificate #%" PRIu32, previous_serial); return 0; }
static int options_use_resolver_name(ProxyContext * const proxy_context) { char *file_buf; char *resolvers_list_rebased; if ((resolvers_list_rebased = path_from_app_folder(proxy_context->resolvers_list)) == NULL) { logger_noformat(proxy_context, LOG_EMERG, "Out of memory"); exit(1); } file_buf = options_read_file(resolvers_list_rebased); if (file_buf == NULL) { logger(proxy_context, LOG_ERR, "Unable to read [%s]", resolvers_list_rebased); exit(1); } assert(proxy_context->resolver_name != NULL); if (options_parse_resolvers_list(proxy_context, file_buf) < 0) { logger(proxy_context, LOG_ERR, "No resolver named [%s] found in the [%s] list", proxy_context->resolver_name, resolvers_list_rebased); exit(1); } free(file_buf); free(resolvers_list_rebased); return 0; }
static int options_apply(ProxyContext * const proxy_context) { if (proxy_context->resolver_ip == NULL) { options_usage(); exit(1); } if (proxy_context->provider_name == NULL || *proxy_context->provider_name == 0) { logger_noformat(proxy_context, LOG_ERR, "Provider name required"); exit(1); } if (proxy_context->provider_publickey_s == NULL) { logger_noformat(proxy_context, LOG_ERR, "Provider key required"); exit(1); } if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s, proxy_context->provider_publickey) != 0) { logger_noformat(proxy_context, LOG_ERR, "Invalid provider key"); exit(1); } if (proxy_context->daemonize) { do_daemonize(); } #ifndef _WIN32 if (proxy_context->pid_file != NULL && pid_file_create(proxy_context->pid_file, proxy_context->user_id != (uid_t) 0) != 0) { logger_error(proxy_context, "Unable to create pid file"); } #endif if (proxy_context->log_file != NULL && (proxy_context->log_fd = open(proxy_context->log_file, O_WRONLY | O_APPEND | O_CREAT, (mode_t) 0600)) == -1) { logger_error(proxy_context, "Unable to open log file"); exit(1); } if (proxy_context->log_fd == -1 && proxy_context->daemonize) { logger_open_syslog(proxy_context); } return 0; }
static int options_apply(ProxyContext * const proxy_context) { if (proxy_context->resolver_ip == NULL || *proxy_context->resolver_ip == 0) { logger_noformat(proxy_context, LOG_ERR, "Resolver IP address required"); #ifdef _WIN32 logger_noformat(proxy_context, LOG_ERR, "Consult http://dnscrypt.org for details."); #else logger_noformat(proxy_context, LOG_ERR, "Please consult the dnscrypt-proxy(8) man page for details."); #endif exit(1); } if (proxy_context->provider_name == NULL || *proxy_context->provider_name == 0) { logger_noformat(proxy_context, LOG_ERR, "Provider name required"); exit(1); } if (options_check_protocol_versions(proxy_context->provider_name) != 0) { logger_noformat(proxy_context, LOG_ERR, "Unsupported server protocol version"); exit(1); } if (proxy_context->provider_publickey_s == NULL) { logger_noformat(proxy_context, LOG_ERR, "Provider key required"); exit(1); } if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s, proxy_context->provider_publickey) != 0) { logger_noformat(proxy_context, LOG_ERR, "Invalid provider key"); exit(1); } if (proxy_context->daemonize) { do_daemonize(); } #ifndef _WIN32 if (proxy_context->pid_file != NULL && pid_file_create(proxy_context->pid_file, proxy_context->user_id != (uid_t) 0) != 0) { logger_error(proxy_context, "Unable to create pid file"); } #endif if (proxy_context->log_file != NULL && (proxy_context->log_fd = open(proxy_context->log_file, O_WRONLY | O_APPEND | O_CREAT, (mode_t) 0600)) == -1) { logger_error(proxy_context, "Unable to open log file"); exit(1); } if (proxy_context->log_fd == -1 && proxy_context->daemonize) { logger_open_syslog(proxy_context); } return 0; }
static int cert_parse_version(ProxyContext * const proxy_context, const SignedBincert * const signed_bincert, const size_t signed_bincert_len) { if (signed_bincert_len <= (size_t) (signed_bincert->signed_data - signed_bincert->magic_cert) || memcmp(signed_bincert->magic_cert, CERT_MAGIC_CERT, sizeof signed_bincert->magic_cert) != 0) { logger_noformat(proxy_context, LOG_DEBUG, "TXT record with no certificates received"); return -1; } if (signed_bincert->version_major[0] != 0U || signed_bincert->version_major[1] != 1U) { logger_noformat(proxy_context, LOG_WARNING, "Unsupported certificate version"); return -1; } return 0; }
static void cert_timer_cb(evutil_socket_t handle, const short event, void * const proxy_context_) { ProxyContext * const proxy_context = proxy_context_; (void) handle; (void) event; logger_noformat(proxy_context, LOG_INFO, "Refetching server certificates"); cert_updater_update(proxy_context); }
static void cert_timer_cb(uv_timer_t *handle, int status) { ProxyContext * const proxy_context = handle->data; CertUpdater * const cert_updater = &proxy_context->cert_updater; (void) status; cert_updater->has_cert_timer = 0; logger_noformat(proxy_context, LOG_INFO, "Refetching server certificates"); cert_updater_start(proxy_context); }
int dnscrypt_proxy_main(int argc, char *argv[]) { ProxyContext proxy_context; setvbuf(stdout, NULL, _IOLBF, BUFSIZ); stack_trace_on_crash(); #ifdef PLUGINS if ((app_context.dcps_context = plugin_support_context_new()) == NULL) { logger_noformat(NULL, LOG_ERR, "Unable to setup plugin support"); exit(2); } #endif if (proxy_context_init(&proxy_context, argc, argv) != 0) { logger_noformat(NULL, LOG_ERR, "Unable to start the proxy"); exit(1); } #ifdef PLUGINS if (plugin_support_context_load(app_context.dcps_context) != 0) { logger_noformat(NULL, LOG_ERR, "Unable to load plugins"); exit(2); } #endif app_context.proxy_context = &proxy_context; logger_noformat(&proxy_context, LOG_INFO, "Generating a new key pair"); dnscrypt_client_init_with_new_key_pair(&proxy_context.dnscrypt_client); logger_noformat(&proxy_context, LOG_INFO, "Done"); if (cert_updater_init(&proxy_context) != 0 || udp_listener_bind(&proxy_context) != 0 || tcp_listener_bind(&proxy_context) != 0) { exit(1); } #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif revoke_privileges(&proxy_context); if (cert_updater_start(&proxy_context) != 0) { exit(1); } if (skip_dispatch == 0) { event_base_dispatch(proxy_context.event_loop); } logger_noformat(&proxy_context, LOG_INFO, "Stopping proxy"); cert_updater_free(&proxy_context); udp_listener_stop(&proxy_context); tcp_listener_stop(&proxy_context); event_base_free(proxy_context.event_loop); #ifdef PLUGINS plugin_support_context_free(app_context.dcps_context); #endif proxy_context_free(&proxy_context); app_context.proxy_context = NULL; salsa20_random_close(); return 0; }
static int options_use_client_key_file(ProxyContext * const proxy_context) { unsigned char *key; char *key_s; const size_t header_len = (sizeof OPTIONS_CLIENT_KEY_HEADER) - 1U; size_t key_s_len; if ((key_s = options_read_file(proxy_context->client_key_file)) == NULL) { logger_error(proxy_context, "Unable to read the client key file"); return -1; } if ((key = sodium_malloc(header_len + crypto_box_SECRETKEYBYTES)) == NULL) { logger_noformat(proxy_context, LOG_EMERG, "Out of memory"); free(key_s); return -1; } if (sodium_hex2bin(key, header_len + crypto_box_SECRETKEYBYTES, key_s, strlen(key_s), ": -", &key_s_len, NULL) != 0 || key_s_len < (header_len + crypto_box_SECRETKEYBYTES) || memcmp(key, OPTIONS_CLIENT_KEY_HEADER, header_len) != 0) { logger_noformat(proxy_context, LOG_ERR, "The client key file doesn't seem to contain a supported key format"); sodium_free(key); free(key_s); return -1; } sodium_memzero(key_s, strlen(key_s)); free(key_s); assert(sizeof proxy_context->dnscrypt_client.secretkey <= key_s_len - header_len); memcpy(proxy_context->dnscrypt_client.secretkey, key + header_len, sizeof proxy_context->dnscrypt_client.secretkey); sodium_free(key); return 0; }
int main(int argc, char *argv[]) { ProxyContext proxy_context; uv_loop_t *event_loop = uv_loop_new(); stack_trace_on_crash(); proxy_context_init(&proxy_context, event_loop, argc, argv); app_context.proxy_context = &proxy_context; logger_noformat(&proxy_context, LOG_INFO, "Generating a new key pair"); dnscrypt_client_init_with_new_key_pair(&proxy_context.dnscrypt_client); logger_noformat(&proxy_context, LOG_INFO, "Done"); if (cert_updater_init(&proxy_context) != 0 || tcp_listener_bind(&proxy_context) != 0 || udp_listener_bind(&proxy_context) != 0) { exit(1); } signal(SIGPIPE, SIG_IGN); revoke_privileges(&proxy_context); if (cert_updater_start(&proxy_context) != 0) { exit(1); } uv_run(event_loop); logger_noformat(&proxy_context, LOG_INFO, "Stopping proxy"); cert_updater_stop(&proxy_context); tcp_listener_stop(&proxy_context); udp_listener_stop(&proxy_context); uv_loop_delete(event_loop); proxy_context_free(&proxy_context); alt_arc4random_close(); return 0; }
static int cert_open_bincert(ProxyContext * const proxy_context, const SignedBincert * const signed_bincert, const size_t signed_bincert_len, Bincert ** const bincert_p) { Bincert *bincert; unsigned long long bincert_data_len_ul; size_t bincert_size; size_t signed_data_len; if (cert_parse_version(proxy_context, signed_bincert, signed_bincert_len) != 0) { DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_COMMUNICATION(); return -1; } bincert_size = signed_bincert_len; if ((bincert = malloc(bincert_size)) == NULL) { DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_COMMUNICATION(); return -1; } assert(signed_bincert_len >= (size_t) (signed_bincert->signed_data - signed_bincert->magic_cert)); signed_data_len = signed_bincert_len - (size_t) (signed_bincert->signed_data - signed_bincert->magic_cert); assert(bincert_size - (size_t) (bincert->server_publickey - bincert->magic_cert) == signed_data_len); if (crypto_sign_ed25519_open(bincert->server_publickey, &bincert_data_len_ul, signed_bincert->signed_data, signed_data_len, proxy_context->provider_publickey) != 0) { free(bincert); logger_noformat(proxy_context, LOG_ERR, "Suspicious certificate received"); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_SECURITY(); return -1; } if (cert_parse_bincert(proxy_context, bincert, *bincert_p) != 0) { memset(bincert, 0, sizeof *bincert); free(bincert); return -1; } if (*bincert_p != NULL) { memset(*bincert_p, 0, sizeof **bincert_p); free(*bincert_p); } *bincert_p = bincert; return 0; }
static void revoke_privileges(ProxyContext * const proxy_context) { (void) proxy_context; init_locale(); init_tz(); (void) strerror(ENOENT); #ifndef DEBUG salsa20_random_stir(); # ifndef _WIN32 if (proxy_context->user_dir != NULL) { if (chdir(proxy_context->user_dir) != 0 || chroot(proxy_context->user_dir) != 0 || chdir("/") != 0) { logger(proxy_context, LOG_ERR, "Unable to chroot to [%s]", proxy_context->user_dir); exit(1); } } if (sandboxes_app() != 0) { logger_noformat(proxy_context, LOG_ERR, "Unable to sandbox the main process"); exit(1); } if (proxy_context->user_id != (uid_t) 0) { if (setgid(proxy_context->user_group) != 0 || setegid(proxy_context->user_group) != 0 || setuid(proxy_context->user_id) != 0 || seteuid(proxy_context->user_id) != 0) { logger(proxy_context, LOG_ERR, "Unable to switch to user id [%lu]", (unsigned long) proxy_context->user_id); exit(1); } } # endif #endif }
int options_parse(AppContext * const app_context, ProxyContext * const proxy_context, int argc, char *argv[]) { int opt_flag; int option_index = 0; #ifdef _WIN32 _Bool option_install = 0; #endif options_init_with_default(app_context, proxy_context); while ((opt_flag = getopt_long(argc, argv, getopt_options, getopt_long_options, &option_index)) != -1) { switch (opt_flag) { case 'a': proxy_context->local_ip = optarg; break; case 'd': proxy_context->daemonize = 1; break; case 'e': { char *endptr; const unsigned long edns_payload_size = strtoul(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || edns_payload_size > DNS_MAX_PACKET_SIZE_UDP_RECV) { logger(proxy_context, LOG_ERR, "Invalid EDNS payload size: [%s]", optarg); exit(1); } if (edns_payload_size <= DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND) { proxy_context->edns_payload_size = (size_t) 0U; proxy_context->udp_max_size = DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND; } else { proxy_context->edns_payload_size = (size_t) edns_payload_size; assert(proxy_context->udp_max_size >= DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND); if (proxy_context->edns_payload_size > DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND) { proxy_context->udp_max_size = proxy_context->edns_payload_size; } } break; } case 'E': proxy_context->ephemeral_keys = 1; break; case 'h': options_usage(); exit(0); case 'k': proxy_context->provider_publickey_s = optarg; break; case 'K': proxy_context->client_key_file = optarg; break; case 'l': proxy_context->log_file = optarg; break; case 'L': proxy_context->resolvers_list = optarg; break; case 'R': proxy_context->resolver_name = optarg; break; #ifndef _WIN32 case 'S': proxy_context->syslog = 1; break; case 'Z': proxy_context->syslog = 1; proxy_context->syslog_prefix = optarg; break; #endif case 'm': { char *endptr; const long max_log_level = strtol(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || max_log_level < 0) { logger(proxy_context, LOG_ERR, "Invalid max log level: [%s]", optarg); exit(1); } proxy_context->max_log_level = max_log_level; break; } case 'n': { char *endptr; const unsigned long connections_count_max = strtoul(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || connections_count_max <= 0U || connections_count_max > UINT_MAX) { logger(proxy_context, LOG_ERR, "Invalid max number of active request: [%s]", optarg); exit(1); } proxy_context->connections_count_max = (unsigned int) connections_count_max; break; } case 'p': proxy_context->pid_file = optarg; break; case 'r': proxy_context->resolver_ip = optarg; break; case 't': { char *endptr; const unsigned long margin = strtoul(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || margin > UINT32_MAX / 60U) { logger(proxy_context, LOG_ERR, "Invalid certificate grace period: [%s]", optarg); exit(1); } proxy_context->test_cert_margin = (time_t) margin * (time_t) 60U; proxy_context->test_only = 1; break; } #ifdef HAVE_GETPWNAM case 'u': { const struct passwd * const pw = getpwnam(optarg); if (pw == NULL) { logger(proxy_context, LOG_ERR, "Unknown user: [%s]", optarg); exit(1); } proxy_context->user_id = pw->pw_uid; proxy_context->user_group = pw->pw_gid; proxy_context->user_dir = strdup(pw->pw_dir); break; } #endif case 'N': proxy_context->provider_name = optarg; break; case 'T': proxy_context->tcp_only = 1; break; case 'V': options_version(); exit(0); case 'X': #ifndef PLUGINS logger_noformat(proxy_context, LOG_ERR, "Support for plugins hasn't been compiled in"); exit(1); #else if (plugin_options_parse_str (proxy_context->app_context->dcps_context, optarg) != 0) { logger_noformat(proxy_context, LOG_ERR, "Error while parsing plugin options"); exit(2); } #endif break; #ifdef _WIN32 case WIN_OPTION_INSTALL: case WIN_OPTION_REINSTALL: option_install = 1; break; case WIN_OPTION_UNINSTALL: if (windows_service_uninstall() != 0) { logger_noformat(NULL, LOG_ERR, "Unable to uninstall the service"); exit(1); } else { logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME " service has been removed from this system"); exit(0); } break; #endif default: options_usage(); exit(1); } } if (options_apply(proxy_context) != 0) { return -1; } #ifdef _WIN32 if (option_install != 0) { if (windows_service_install(proxy_context) != 0) { logger_noformat(NULL, LOG_ERR, "Unable to install the service"); logger_noformat(NULL, LOG_ERR, "Make sure that you are using an elevated command prompt " "and that the service hasn't been already installed"); exit(1); } logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME " service has been installed and started"); logger_noformat(NULL, LOG_INFO, "The registry key used for this " "service is " WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY); logger(NULL, LOG_INFO, "Now, change your resolver settings to %s", proxy_context->local_ip); exit(0); } #endif return 0; }
static int options_apply(ProxyContext * const proxy_context) { if (proxy_context->client_key_file != NULL) { if (proxy_context->ephemeral_keys != 0) { logger_noformat(proxy_context, LOG_ERR, "--client-key and --ephemeral-keys are mutually exclusive"); exit(1); } if (options_use_client_key_file(proxy_context) != 0) { logger(proxy_context, LOG_ERR, "Client key file [%s] could not be used", proxy_context->client_key_file); exit(1); } } if (proxy_context->resolver_name != NULL) { if (proxy_context->resolvers_list == NULL) { logger_noformat(proxy_context, LOG_ERR, "Resolvers list (-L command-line switch) required"); exit(1); } if (options_use_resolver_name(proxy_context) != 0) { logger(proxy_context, LOG_ERR, "Resolver name (-R command-line switch) required. " "See [%s] for a list of public resolvers.", proxy_context->resolvers_list); exit(1); } } if (proxy_context->resolver_ip == NULL || *proxy_context->resolver_ip == 0 || proxy_context->provider_name == NULL || *proxy_context->provider_name == 0 || proxy_context->provider_publickey_s == NULL || *proxy_context->provider_publickey_s == 0) { logger_noformat(proxy_context, LOG_ERR, "Resolver information required."); logger_noformat(proxy_context, LOG_ERR, "The easiest way to do so is to provide a resolver name."); logger_noformat(proxy_context, LOG_ERR, "Example: dnscrypt-proxy -R mydnsprovider"); logger(proxy_context, LOG_ERR, "See the file [%s] for a list of compatible public resolvers", proxy_context->resolvers_list); logger_noformat(proxy_context, LOG_ERR, "The name is the first column in this table."); logger_noformat(proxy_context, LOG_ERR, "Alternatively, an IP address, a provider name " "and a provider key can be supplied."); #ifdef _WIN32 logger_noformat(proxy_context, LOG_ERR, "Consult https://dnscrypt.org " "and https://github.com/jedisct1/dnscrypt-proxy/blob/master/README-WINDOWS.markdown " "for details."); #else logger_noformat(proxy_context, LOG_ERR, "Please consult https://dnscrypt.org " "and the dnscrypt-proxy(8) man page for details."); #endif exit(1); } if (proxy_context->provider_name == NULL || *proxy_context->provider_name == 0) { logger_noformat(proxy_context, LOG_ERR, "Provider name required"); exit(1); } if (options_check_protocol_versions(proxy_context->provider_name) != 0) { logger_noformat(proxy_context, LOG_ERR, "Unsupported server protocol version"); exit(1); } if (proxy_context->provider_publickey_s == NULL) { logger_noformat(proxy_context, LOG_ERR, "Provider key required"); exit(1); } if (dnscrypt_fingerprint_to_key(proxy_context->provider_publickey_s, proxy_context->provider_publickey) != 0) { logger_noformat(proxy_context, LOG_ERR, "Invalid provider key"); exit(1); } if (proxy_context->daemonize != 0) { if (proxy_context->log_file == NULL) { proxy_context->syslog = 1; } do_daemonize(); } #ifndef _WIN32 if (proxy_context->pid_file != NULL && pid_file_create(proxy_context->pid_file, proxy_context->user_id != (uid_t) 0) != 0) { logger_error(proxy_context, "Unable to create pid file"); exit(1); } #endif if (proxy_context->log_file != NULL && proxy_context->syslog != 0) { logger_noformat(proxy_context, LOG_ERR, "--logfile and --syslog are mutually exclusive"); exit(1); } if (proxy_context->log_file != NULL && (proxy_context->log_fp = fopen(proxy_context->log_file, "a")) == NULL) { logger_error(proxy_context, "Unable to open log file"); exit(1); } if (proxy_context->syslog != 0) { assert(proxy_context->log_fp == NULL); logger_open_syslog(proxy_context); } return 0; }
static int options_parse_resolver(ProxyContext * const proxy_context, char * const * const headers, const size_t headers_count, char * const * const cols, const size_t cols_count) { const char *dnssec; const char *namecoin; const char *nologs; const char *provider_name; const char *provider_publickey_s; const char *resolver_ip; const char *resolver_name; resolver_name = options_get_col(headers, headers_count, cols, cols_count, "Name"); if (resolver_name == NULL) { logger(proxy_context, LOG_ERR, "Invalid resolvers list file: missing 'Name' column"); exit(1); } if (*resolver_name == 0) { logger(proxy_context, LOG_ERR, "Resolver with an empty name"); return -1; } if (evutil_ascii_strcasecmp(resolver_name, proxy_context->resolver_name) != 0) { return 0; } provider_name = options_get_col(headers, headers_count, cols, cols_count, "Provider name"); provider_publickey_s = options_get_col(headers, headers_count, cols, cols_count, "Provider public key"); resolver_ip = options_get_col(headers, headers_count, cols, cols_count, "Resolver address"); if (provider_name == NULL || *provider_name == 0) { logger(proxy_context, LOG_ERR, "Resolvers list is missing a provider name for [%s]", resolver_name); return -1; } if (provider_publickey_s == NULL || *provider_publickey_s == 0) { logger(proxy_context, LOG_ERR, "Resolvers list is missing a public key for [%s]", resolver_name); return -1; } if (resolver_ip == NULL || *resolver_ip == 0) { logger(proxy_context, LOG_ERR, "Resolvers list is missing a resolver address for [%s]", resolver_name); return -1; } dnssec = options_get_col(headers, headers_count, cols, cols_count, "DNSSEC validation"); if (dnssec != NULL && evutil_ascii_strcasecmp(dnssec, "yes") != 0) { logger(proxy_context, LOG_INFO, "- [%s] does not support DNS Security Extensions", resolver_name); } else { logger(proxy_context, LOG_INFO, "+ DNS Security Extensions are supported"); } namecoin = options_get_col(headers, headers_count, cols, cols_count, "Namecoin"); if (namecoin != NULL && evutil_ascii_strcasecmp(namecoin, "yes") == 0) { logger(proxy_context, LOG_INFO, "+ Namecoin domains can be resolved"); } nologs = options_get_col(headers, headers_count, cols, cols_count, "No logs"); if (nologs != NULL && evutil_ascii_strcasecmp(nologs, "no") == 0) { logger(proxy_context, LOG_WARNING, "- [%s] logs your activity - " "a different provider might be better a choice if privacy is a concern", resolver_name); } else { logger(proxy_context, LOG_INFO, "+ Provider supposedly doesn't keep logs"); } proxy_context->provider_name = strdup(provider_name); proxy_context->provider_publickey_s = strdup(provider_publickey_s); proxy_context->resolver_ip = strdup(resolver_ip); if (proxy_context->provider_name == NULL || proxy_context->provider_publickey_s == NULL || proxy_context->resolver_ip == NULL) { logger_noformat(proxy_context, LOG_EMERG, "Out of memory"); exit(1); } return 1; }
int options_parse(AppContext * const app_context, ProxyContext * const proxy_context, int argc, char *argv[]) { int opt_flag; int option_index = 0; options_init_with_default(app_context, proxy_context); while ((opt_flag = getopt_long(argc, argv, getopt_options, getopt_long_options, &option_index)) != -1) { switch (opt_flag) { case 'a': proxy_context->local_ip = optarg; break; case 'd': proxy_context->daemonize = 1; break; case 'e': { char *endptr; const unsigned long edns_payload_size = strtoul(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || edns_payload_size > DNS_MAX_PACKET_SIZE_UDP_RECV) { logger(proxy_context, LOG_ERR, "Invalid EDNS payload size: [%s]", optarg); exit(1); } if (edns_payload_size <= DNS_MAX_PACKET_SIZE_UDP_SEND) { proxy_context->edns_payload_size = (size_t) 0U; } else { proxy_context->edns_payload_size = (size_t) edns_payload_size; } break; } case 'h': options_usage(); exit(0); case 'k': proxy_context->provider_publickey_s = optarg; break; case 'l': proxy_context->log_file = optarg; break; case 'n': { char *endptr; const unsigned long connections_count_max = strtoul(optarg, &endptr, 10); if (*optarg == 0 || *endptr != 0 || connections_count_max <= 0U || connections_count_max > UINT_MAX) { logger(proxy_context, LOG_ERR, "Invalid max number of active request: [%s]", optarg); exit(1); } proxy_context->connections_count_max = (unsigned int) connections_count_max; break; } case 'p': proxy_context->pid_file = optarg; break; case 'r': proxy_context->resolver_ip = optarg; break; #ifdef HAVE_GETPWNAM case 'u': { const struct passwd * const pw = getpwnam(optarg); if (pw == NULL) { logger(proxy_context, LOG_ERR, "Unknown user: [%s]", optarg); exit(1); } proxy_context->user_id = pw->pw_uid; proxy_context->user_group = pw->pw_gid; proxy_context->user_dir = strdup(pw->pw_dir); break; } #endif case 'N': proxy_context->provider_name = optarg; break; case 'T': proxy_context->tcp_only = 1; break; case 'V': options_version(); exit(0); case 'X': #ifndef PLUGINS logger_noformat(proxy_context, LOG_ERR, "Support for plugins hasn't been compiled in"); exit(1); #else if (plugin_options_parse_str (proxy_context->app_context->dcps_context, optarg) != 0) { logger_noformat(proxy_context, LOG_ERR, "Error while parsing plugin options"); exit(2); } #endif break; #ifdef _WIN32 case WIN_OPTION_INSTALL: case WIN_OPTION_UNINSTALL: if (windows_service_option(opt_flag, argc, argv) != 0) { options_usage(); exit(1); } break; #endif default: options_usage(); exit(1); } } if (options_apply(proxy_context) != 0) { return -1; } return 0; }
static void cert_query_cb(int result, char type, int count, int ttl, void * const txt_records_, void * const arg) { Bincert *bincert = NULL; ProxyContext *proxy_context = arg; const struct txt_record *txt_records = txt_records_; int i = 0; (void) type; (void) ttl; DNSCRYPT_PROXY_CERTS_UPDATE_RECEIVED(); evdns_base_free(proxy_context->cert_updater.evdns_base, 0); proxy_context->cert_updater.evdns_base = NULL; if (result != DNS_ERR_NONE) { logger_noformat(proxy_context, LOG_ERR, "Unable to retrieve server certificates"); cert_reschedule_query_after_failure(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_COMMUNICATION(); return; } assert(count >= 0); while (i < count) { cert_open_bincert(proxy_context, (const SignedBincert *) txt_records[i].txt, txt_records[i].len, &bincert); i++; } if (bincert == NULL) { logger_noformat(proxy_context, LOG_ERR, "No useable certificates found"); cert_reschedule_query_after_failure(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_NOCERTS(); if (proxy_context->test_only) { exit(DNSCRYPT_EXIT_CERT_NOCERTS); } return; } if (proxy_context->test_only != 0) { const uint32_t now_u32 = (uint32_t) time(NULL); uint32_t ts_end; memcpy(&ts_end, bincert->ts_end, sizeof ts_end); ts_end = htonl(ts_end); if (ts_end < (uint32_t) proxy_context->test_cert_margin || now_u32 > ts_end - (uint32_t) proxy_context->test_cert_margin) { logger_noformat(proxy_context, LOG_WARNING, "The certificate is not valid for the given safety margin"); DNSCRYPT_PROXY_CERTS_UPDATE_ERROR_NOCERTS(); exit(DNSCRYPT_EXIT_CERT_MARGIN); } } COMPILER_ASSERT(sizeof proxy_context->resolver_publickey == sizeof bincert->server_publickey); memcpy(proxy_context->resolver_publickey, bincert->server_publickey, sizeof proxy_context->resolver_publickey); COMPILER_ASSERT(sizeof proxy_context->dnscrypt_magic_query == sizeof bincert->magic_query); memcpy(proxy_context->dnscrypt_magic_query, bincert->magic_query, sizeof proxy_context->dnscrypt_magic_query); cert_print_bincert_info(proxy_context, bincert); cert_print_server_key(proxy_context); dnscrypt_client_init_magic_query(&proxy_context->dnscrypt_client, bincert->magic_query); memset(bincert, 0, sizeof *bincert); free(bincert); if (proxy_context->test_only) { DNSCRYPT_PROXY_CERTS_UPDATE_DONE((unsigned char *) proxy_context->resolver_publickey); exit(0); } dnscrypt_client_init_nmkey(&proxy_context->dnscrypt_client, proxy_context->resolver_publickey); dnscrypt_proxy_start_listeners(proxy_context); proxy_context->cert_updater.query_retry_step = 0U; cert_reschedule_query_after_success(proxy_context); DNSCRYPT_PROXY_CERTS_UPDATE_DONE((unsigned char *) proxy_context->resolver_publickey); }