dnscrypt-proxy 1.4.0
[tomato.git] / release / src / router / dnscrypt / src / proxy / app.c
blob92bcbe0cd8df74a5c7c14c385da65448203f7ded
2 #include <config.h>
3 #include <sys/types.h>
5 #include <assert.h>
6 #include <errno.h>
7 #include <locale.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <unistd.h>
15 #include <event2/event.h>
16 #include <event2/util.h>
18 #include "app.h"
19 #include "dnscrypt_client.h"
20 #include "dnscrypt_proxy.h"
21 #include "logger.h"
22 #include "options.h"
23 #include "sandboxes.h"
24 #include "sodium.h"
25 #include "stack_trace.h"
26 #include "tcp_request.h"
27 #include "udp_request.h"
28 #ifdef PLUGINS
29 # include "plugin_support.h"
30 #endif
32 #ifndef INET6_ADDRSTRLEN
33 # define INET6_ADDRSTRLEN 46U
34 #endif
36 static AppContext app_context;
37 static volatile sig_atomic_t skip_dispatch;
39 static int
40 sockaddr_from_ip_and_port(struct sockaddr_storage * const sockaddr,
41 ev_socklen_t * const sockaddr_len_p,
42 const char * const ip, const char * const port,
43 const char * const error_msg)
45 char sockaddr_port[INET6_ADDRSTRLEN + sizeof "[]:65535"];
46 int sockaddr_len_int;
47 char *pnt;
48 _Bool has_column = 0;
49 _Bool has_columns = 0;
50 _Bool has_brackets = *ip == '[';
52 if ((pnt = strchr(ip, ':')) != NULL) {
53 has_column = 1;
54 if (strchr(pnt + 1, ':') != NULL) {
55 has_columns = 1;
58 sockaddr_len_int = (int) sizeof *sockaddr;
59 if ((has_brackets != 0 || has_column != has_columns) &&
60 evutil_parse_sockaddr_port(ip, (struct sockaddr *) sockaddr,
61 &sockaddr_len_int) == 0) {
62 *sockaddr_len_p = (ev_socklen_t) sockaddr_len_int;
63 return 0;
65 if (has_columns != 0 && has_brackets == 0) {
66 evutil_snprintf(sockaddr_port, sizeof sockaddr_port, "[%s]:%s",
67 ip, port);
68 } else {
69 evutil_snprintf(sockaddr_port, sizeof sockaddr_port, "%s:%s",
70 ip, port);
72 sockaddr_len_int = (int) sizeof *sockaddr;
73 if (evutil_parse_sockaddr_port(sockaddr_port, (struct sockaddr *) sockaddr,
74 &sockaddr_len_int) != 0) {
75 logger(NULL, LOG_ERR, "%s: %s", error_msg, sockaddr_port);
76 *sockaddr_len_p = (ev_socklen_t) 0U;
78 return -1;
80 *sockaddr_len_p = (ev_socklen_t) sockaddr_len_int;
82 return 0;
85 static int
86 proxy_context_init(ProxyContext * const proxy_context, int argc, char *argv[])
88 memset(proxy_context, 0, sizeof *proxy_context);
89 proxy_context->event_loop = NULL;
90 proxy_context->log_file = NULL;
91 proxy_context->max_log_level = LOG_INFO;
92 proxy_context->tcp_accept_timer = NULL;
93 proxy_context->tcp_conn_listener = NULL;
94 proxy_context->udp_current_max_size = DNS_MAX_PACKET_SIZE_UDP_NO_EDNS_SEND;
95 proxy_context->udp_max_size = proxy_context->udp_current_max_size;
96 proxy_context->udp_listener_event = NULL;
97 proxy_context->udp_proxy_resolver_event = NULL;
98 proxy_context->udp_proxy_resolver_handle = -1;
99 proxy_context->udp_listener_handle = -1;
100 if (options_parse(&app_context, proxy_context, argc, argv) != 0) {
101 return -1;
103 #ifdef _WIN32
104 WSADATA wsa_data;
105 WSAStartup(MAKEWORD(2, 2), &wsa_data);
106 #endif
107 if ((proxy_context->event_loop = event_base_new()) == NULL) {
108 logger(NULL, LOG_ERR, "Unable to initialize the event loop");
109 return -1;
111 if (sockaddr_from_ip_and_port(&proxy_context->resolver_sockaddr,
112 &proxy_context->resolver_sockaddr_len,
113 proxy_context->resolver_ip,
114 DNS_DEFAULT_RESOLVER_PORT,
115 "Unsupported resolver address") != 0) {
116 return -1;
118 if (sockaddr_from_ip_and_port(&proxy_context->local_sockaddr,
119 &proxy_context->local_sockaddr_len,
120 proxy_context->local_ip,
121 DNS_DEFAULT_LOCAL_PORT,
122 "Unsupported local address") != 0) {
123 return -1;
125 return 0;
128 static void
129 proxy_context_free(ProxyContext * const proxy_context)
131 if (proxy_context == NULL) {
132 return;
134 options_free(proxy_context);
135 logger_close(proxy_context);
138 static int
139 init_locale(void)
141 setlocale(LC_CTYPE, "C");
142 setlocale(LC_COLLATE, "C");
144 return 0;
147 static int
148 init_tz(void)
150 static char default_tz_for_putenv[] = "TZ=UTC+00:00";
151 char stbuf[10U];
152 struct tm *tm;
153 time_t now;
155 tzset();
156 time(&now);
157 if ((tm = localtime(&now)) != NULL &&
158 strftime(stbuf, sizeof stbuf, "%z", tm) == (size_t) 5U) {
159 evutil_snprintf(default_tz_for_putenv, sizeof default_tz_for_putenv,
160 "TZ=UTC%c%c%c:%c%c", (*stbuf == '-' ? '+' : '-'),
161 stbuf[1], stbuf[2], stbuf[3], stbuf[4]);
163 putenv(default_tz_for_putenv);
164 (void) localtime(&now);
165 (void) gmtime(&now);
167 return 0;
170 static void
171 revoke_privileges(ProxyContext * const proxy_context)
173 (void) proxy_context;
175 init_locale();
176 init_tz();
177 (void) strerror(ENOENT);
178 #ifndef DEBUG
179 randombytes_stir();
180 # ifndef _WIN32
181 if (proxy_context->user_dir != NULL) {
182 if (chdir(proxy_context->user_dir) != 0 ||
183 chroot(proxy_context->user_dir) != 0 || chdir("/") != 0) {
184 logger(proxy_context, LOG_ERR, "Unable to chroot to [%s]",
185 proxy_context->user_dir);
186 exit(1);
189 if (sandboxes_app() != 0) {
190 logger_noformat(proxy_context, LOG_ERR,
191 "Unable to sandbox the main process");
192 exit(1);
194 if (proxy_context->user_id != (uid_t) 0) {
195 if (setgid(proxy_context->user_group) != 0 ||
196 setegid(proxy_context->user_group) != 0 ||
197 setuid(proxy_context->user_id) != 0 ||
198 seteuid(proxy_context->user_id) != 0) {
199 logger(proxy_context, LOG_ERR, "Unable to switch to user id [%lu]",
200 (unsigned long) proxy_context->user_id);
201 exit(1);
204 # endif
205 #endif
209 dnscrypt_proxy_start_listeners(ProxyContext * const proxy_context)
211 char local_addr_s[INET6_ADDRSTRLEN + sizeof "[]:65535"];
212 char resolver_addr_s[INET6_ADDRSTRLEN + sizeof "[]:65535"];
214 if (proxy_context->listeners_started != 0) {
215 return 0;
217 if (udp_listener_start(proxy_context) != 0 ||
218 tcp_listener_start(proxy_context) != 0) {
219 exit(1);
221 evutil_format_sockaddr_port((const struct sockaddr *)
222 &proxy_context->local_sockaddr,
223 local_addr_s, sizeof local_addr_s);
224 evutil_format_sockaddr_port((const struct sockaddr *)
225 &proxy_context->resolver_sockaddr,
226 resolver_addr_s, sizeof resolver_addr_s);
227 logger(proxy_context, LOG_NOTICE, "Proxying from %s to %s",
228 local_addr_s, resolver_addr_s);
230 proxy_context->listeners_started = 1;
232 return 0;
236 dnscrypt_proxy_loop_break(void)
238 if (app_context.proxy_context != NULL &&
239 app_context.proxy_context->event_loop != NULL) {
240 event_base_loopbreak(app_context.proxy_context->event_loop);
241 } else {
242 skip_dispatch = 1;
244 return 0;
248 dnscrypt_proxy_main(int argc, char *argv[])
250 ProxyContext proxy_context;
252 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
253 stack_trace_on_crash();
254 #ifdef PLUGINS
255 if ((app_context.dcps_context = plugin_support_context_new()) == NULL) {
256 logger_noformat(NULL, LOG_ERR, "Unable to setup plugin support");
257 exit(2);
259 #endif
260 if (proxy_context_init(&proxy_context, argc, argv) != 0) {
261 logger_noformat(NULL, LOG_ERR, "Unable to start the proxy");
262 exit(1);
264 logger_noformat(&proxy_context, LOG_NOTICE, "Starting " PACKAGE_STRING);
265 #ifdef USE_ONLY_PORTABLE_IMPLEMENTATIONS
266 randombytes_stir();
267 #else
268 logger_noformat(&proxy_context, LOG_INFO,
269 "Initializing libsodium for optimal performance");
270 if (sodium_init() != 0) {
271 exit(1);
273 #endif
274 randombytes_set_implementation(&randombytes_salsa20_implementation);
275 #ifdef PLUGINS
276 if (plugin_support_context_load(app_context.dcps_context) != 0) {
277 logger_noformat(NULL, LOG_ERR, "Unable to load plugins");
278 exit(2);
280 #endif
281 app_context.proxy_context = &proxy_context;
282 logger_noformat(&proxy_context, LOG_INFO, "Generating a new key pair");
283 dnscrypt_client_init_with_new_key_pair(&proxy_context.dnscrypt_client);
284 logger_noformat(&proxy_context, LOG_INFO, "Done");
286 if (cert_updater_init(&proxy_context) != 0) {
287 exit(1);
289 if (proxy_context.test_only == 0 &&
290 (udp_listener_bind(&proxy_context) != 0 ||
291 tcp_listener_bind(&proxy_context) != 0)) {
292 exit(1);
294 #ifdef SIGPIPE
295 signal(SIGPIPE, SIG_IGN);
296 #endif
298 revoke_privileges(&proxy_context);
299 if (cert_updater_start(&proxy_context) != 0) {
300 exit(1);
302 if (skip_dispatch == 0) {
303 event_base_dispatch(proxy_context.event_loop);
305 logger_noformat(&proxy_context, LOG_NOTICE, "Stopping proxy");
306 cert_updater_free(&proxy_context);
307 udp_listener_stop(&proxy_context);
308 tcp_listener_stop(&proxy_context);
309 event_base_free(proxy_context.event_loop);
310 #ifdef PLUGINS
311 plugin_support_context_free(app_context.dcps_context);
312 #endif
313 proxy_context_free(&proxy_context);
314 app_context.proxy_context = NULL;
315 randombytes_close();
317 return 0;