dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / dnscrypt / src / proxy / app.c
blobb190beb63aaa7e311b8b32930264bb79ba9d35da
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_listener_event = NULL;
95 proxy_context->udp_proxy_resolver_event = NULL;
96 proxy_context->udp_proxy_resolver_handle = -1;
97 proxy_context->udp_listener_handle = -1;
98 if (options_parse(&app_context, proxy_context, argc, argv) != 0) {
99 return -1;
101 #ifdef _WIN32
102 WSADATA wsa_data;
103 WSAStartup(MAKEWORD(2, 2), &wsa_data);
104 #endif
105 if ((proxy_context->event_loop = event_base_new()) == NULL) {
106 logger(NULL, LOG_ERR, "Unable to initialize the event loop");
107 return -1;
109 if (sockaddr_from_ip_and_port(&proxy_context->resolver_sockaddr,
110 &proxy_context->resolver_sockaddr_len,
111 proxy_context->resolver_ip,
112 DNS_DEFAULT_RESOLVER_PORT,
113 "Unsupported resolver address") != 0) {
114 return -1;
116 if (sockaddr_from_ip_and_port(&proxy_context->local_sockaddr,
117 &proxy_context->local_sockaddr_len,
118 proxy_context->local_ip,
119 DNS_DEFAULT_LOCAL_PORT,
120 "Unsupported local address") != 0) {
121 return -1;
123 return 0;
126 static void
127 proxy_context_free(ProxyContext * const proxy_context)
129 if (proxy_context == NULL) {
130 return;
132 options_free(proxy_context);
133 logger_close(proxy_context);
136 static int
137 init_locale(void)
139 setlocale(LC_CTYPE, "C");
140 setlocale(LC_COLLATE, "C");
142 return 0;
145 static int
146 init_tz(void)
148 static char default_tz_for_putenv[] = "TZ=UTC+00:00";
149 char stbuf[10U];
150 struct tm *tm;
151 time_t now;
153 tzset();
154 time(&now);
155 if ((tm = localtime(&now)) != NULL &&
156 strftime(stbuf, sizeof stbuf, "%z", tm) == (size_t) 5U) {
157 evutil_snprintf(default_tz_for_putenv, sizeof default_tz_for_putenv,
158 "TZ=UTC%c%c%c:%c%c", (*stbuf == '-' ? '+' : '-'),
159 stbuf[1], stbuf[2], stbuf[3], stbuf[4]);
161 putenv(default_tz_for_putenv);
162 (void) localtime(&now);
163 (void) gmtime(&now);
165 return 0;
168 static void
169 revoke_privileges(ProxyContext * const proxy_context)
171 (void) proxy_context;
173 init_locale();
174 init_tz();
175 (void) strerror(ENOENT);
176 #ifndef DEBUG
177 randombytes_stir();
178 # ifndef _WIN32
179 if (proxy_context->user_dir != NULL) {
180 if (chdir(proxy_context->user_dir) != 0 ||
181 chroot(proxy_context->user_dir) != 0 || chdir("/") != 0) {
182 logger(proxy_context, LOG_ERR, "Unable to chroot to [%s]",
183 proxy_context->user_dir);
184 exit(1);
187 if (sandboxes_app() != 0) {
188 logger_noformat(proxy_context, LOG_ERR,
189 "Unable to sandbox the main process");
190 exit(1);
192 if (proxy_context->user_id != (uid_t) 0) {
193 if (setgid(proxy_context->user_group) != 0 ||
194 setegid(proxy_context->user_group) != 0 ||
195 setuid(proxy_context->user_id) != 0 ||
196 seteuid(proxy_context->user_id) != 0) {
197 logger(proxy_context, LOG_ERR, "Unable to switch to user id [%lu]",
198 (unsigned long) proxy_context->user_id);
199 exit(1);
202 # endif
203 #endif
207 dnscrypt_proxy_start_listeners(ProxyContext * const proxy_context)
209 char local_addr_s[INET6_ADDRSTRLEN + sizeof "[]:65535"];
210 char resolver_addr_s[INET6_ADDRSTRLEN + sizeof "[]:65535"];
212 if (proxy_context->listeners_started != 0) {
213 return 0;
215 if (udp_listener_start(proxy_context) != 0 ||
216 tcp_listener_start(proxy_context) != 0) {
217 exit(1);
219 evutil_format_sockaddr_port((const struct sockaddr *)
220 &proxy_context->local_sockaddr,
221 local_addr_s, sizeof local_addr_s);
222 evutil_format_sockaddr_port((const struct sockaddr *)
223 &proxy_context->resolver_sockaddr,
224 resolver_addr_s, sizeof resolver_addr_s);
225 logger(proxy_context, LOG_INFO, "Proxying from %s to %s",
226 local_addr_s, resolver_addr_s);
228 proxy_context->listeners_started = 1;
230 return 0;
234 dnscrypt_proxy_loop_break(void)
236 if (app_context.proxy_context != NULL &&
237 app_context.proxy_context->event_loop != NULL) {
238 event_base_loopbreak(app_context.proxy_context->event_loop);
239 } else {
240 skip_dispatch = 1;
242 return 0;
246 dnscrypt_proxy_main(int argc, char *argv[])
248 ProxyContext proxy_context;
250 setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
251 stack_trace_on_crash();
252 #ifdef PLUGINS
253 if ((app_context.dcps_context = plugin_support_context_new()) == NULL) {
254 logger_noformat(NULL, LOG_ERR, "Unable to setup plugin support");
255 exit(2);
257 #endif
258 if (proxy_context_init(&proxy_context, argc, argv) != 0) {
259 logger_noformat(NULL, LOG_ERR, "Unable to start the proxy");
260 exit(1);
262 logger_noformat(&proxy_context, LOG_INFO,
263 "Initializing libsodium for optimal performance");
264 if (sodium_init() != 0) {
265 exit(1);
267 randombytes_set_implementation(&randombytes_salsa20_implementation);
268 #ifdef PLUGINS
269 if (plugin_support_context_load(app_context.dcps_context) != 0) {
270 logger_noformat(NULL, LOG_ERR, "Unable to load plugins");
271 exit(2);
273 #endif
274 app_context.proxy_context = &proxy_context;
275 logger_noformat(&proxy_context, LOG_INFO, "Generating a new key pair");
276 dnscrypt_client_init_with_new_key_pair(&proxy_context.dnscrypt_client);
277 logger_noformat(&proxy_context, LOG_INFO, "Done");
279 if (cert_updater_init(&proxy_context) != 0 ||
280 udp_listener_bind(&proxy_context) != 0 ||
281 tcp_listener_bind(&proxy_context) != 0) {
282 exit(1);
284 #ifdef SIGPIPE
285 signal(SIGPIPE, SIG_IGN);
286 #endif
288 revoke_privileges(&proxy_context);
289 if (cert_updater_start(&proxy_context) != 0) {
290 exit(1);
292 if (skip_dispatch == 0) {
293 event_base_dispatch(proxy_context.event_loop);
295 logger_noformat(&proxy_context, LOG_INFO, "Stopping proxy");
296 cert_updater_free(&proxy_context);
297 udp_listener_stop(&proxy_context);
298 tcp_listener_stop(&proxy_context);
299 event_base_free(proxy_context.event_loop);
300 #ifdef PLUGINS
301 plugin_support_context_free(app_context.dcps_context);
302 #endif
303 proxy_context_free(&proxy_context);
304 app_context.proxy_context = NULL;
305 randombytes_close();
307 return 0;