dnscrypt-proxy update: 1.4.1
[tomato.git] / release / src / router / dnscrypt / src / proxy / windows_service.c
blob4ec3f13b784a7cc7e222447d613850ea0f50670c
2 #include <config.h>
4 #include "app.h"
5 #include "dnscrypt_proxy.h"
6 #include "logger.h"
7 #include "windows_service.h"
9 #ifndef _WIN32
11 int
12 main(int argc, char *argv[])
14 return dnscrypt_proxy_main(argc, argv);
17 #else
19 #include <assert.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <windows.h>
28 #include "logger.h"
29 #include "utils.h"
31 static SERVICE_STATUS service_status;
32 static SERVICE_STATUS_HANDLE service_status_handle;
33 static _Bool app_is_running_as_a_service;
35 static void WINAPI
36 control_handler(const DWORD wanted_state)
38 if (wanted_state == SERVICE_CONTROL_STOP &&
39 dnscrypt_proxy_loop_break() == 0) {
40 service_status.dwCurrentState = SERVICE_STOPPED;
42 SetServiceStatus(service_status_handle, &service_status);
45 static char **
46 cmdline_clone_options(const int argc, char ** const argv)
48 char **argv_new;
50 if (argc >= INT_MAX || (size_t) argc >= SIZE_MAX / sizeof *argv_new ||
51 (argv_new = calloc((unsigned int) argc + 1U,
52 sizeof *argv_new)) == NULL) {
53 return NULL;
55 memcpy(argv_new, argv, (unsigned int) (argc + 1U) * sizeof *argv_new);
57 return argv_new;
60 static int
61 cmdline_add_option(int * const argc_p, char *** const argv_p,
62 const char * const arg)
64 char *arg_dup;
65 char **argv_new;
67 if (*argc_p >= INT_MAX ||
68 SIZE_MAX / sizeof *argv_new <= (unsigned int) (*argc_p + 2U)) {
69 return -1;
71 if ((argv_new = realloc(*argv_p, (unsigned int) (*argc_p + 2U) *
72 sizeof *argv_new)) == NULL) {
73 return -1;
75 if ((arg_dup = strdup(arg)) == NULL) {
76 free(argv_new);
77 return -1;
79 argv_new[(*argc_p)++] = arg_dup;
80 argv_new[*argc_p] = NULL;
81 *argv_p = argv_new;
82 logger(NULL, LOG_INFO, "Adding command-line option: [%s]", arg_dup);
84 return 0;
87 typedef struct WindowsServiceParseMultiSzCb_ {
88 void (*cb)(struct WindowsServiceParseMultiSzCb_ *, const char *string);
89 int * const argc_p;
90 char *** const argv_p;
91 int * const err_p;
92 } WindowsServiceParseMultiSzCb;
94 static void
95 windows_service_parse_multi_sz_cb(WindowsServiceParseMultiSzCb * const cb,
96 const char *string)
98 assert(cb->cb == windows_service_parse_multi_sz_cb);
99 *(cb->err_p) += cmdline_add_option(cb->argc_p, cb->argv_p, "--plugin");
100 *(cb->err_p) += cmdline_add_option(cb->argc_p, cb->argv_p, string);
103 static int
104 windows_service_parse_multi_sz(WindowsServiceParseMultiSzCb * const cb,
105 const char * const multi_sz,
106 const size_t multi_sz_len)
108 const char *multi_sz_pnt = multi_sz;
109 const char *zero;
110 size_t len;
111 size_t multi_sz_remaining_len = multi_sz_len;
112 size_t zlen;
114 while (multi_sz_remaining_len > (size_t) 0U &&
115 (zero = memchr(multi_sz_pnt, 0, multi_sz_remaining_len)) != NULL) {
116 if ((len = (size_t) (zero - multi_sz_pnt)) > (size_t) 0U) {
117 cb->cb(cb, multi_sz_pnt);
119 zlen = len + (size_t) 1U;
120 assert(zlen <= multi_sz_remaining_len);
121 multi_sz_remaining_len -= zlen;
122 multi_sz_pnt += zlen;
124 return 0;
127 static int
128 windows_service_registry_read_multi_sz(const char * const key,
129 WindowsServiceParseMultiSzCb * const cb)
131 BYTE *value = NULL;
132 HKEY hk = NULL;
133 DWORD value_len;
134 DWORD value_type;
136 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
137 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
138 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
139 return -1;
141 if (RegQueryValueEx(hk, key, 0,
142 &value_type, NULL, &value_len) == ERROR_SUCCESS &&
143 value_type == (DWORD) REG_MULTI_SZ &&
144 value_len <= SIZE_MAX && value_len > (DWORD) 0 &&
145 (value = malloc((size_t) value_len)) != NULL) {
146 if (RegQueryValueEx(hk, key, 0,
147 &value_type, value, &value_len) != ERROR_SUCCESS ||
148 value_type != (DWORD) REG_MULTI_SZ) {
149 free(value);
150 value = NULL;
152 assert(value == NULL || value_len == 0 ||
153 (value_len > 0 && value[value_len - 1] == 0));
155 RegCloseKey(hk);
156 if (value == NULL) {
157 return -1;
159 windows_service_parse_multi_sz(cb, (const char *) value,
160 (size_t) value_len);
161 free(value);
163 return 0;
166 static int
167 windows_service_registry_read_string(const char * const key,
168 char ** const value_p)
170 BYTE *value = NULL;
171 HKEY hk = NULL;
172 DWORD value_len;
173 DWORD value_type;
175 *value_p = NULL;
176 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
177 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
178 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
179 return -1;
181 if (RegQueryValueEx(hk, key, 0,
182 &value_type, NULL, &value_len) == ERROR_SUCCESS &&
183 value_type == (DWORD) REG_SZ &&
184 value_len <= SIZE_MAX && value_len > (DWORD) 0 &&
185 (value = malloc((size_t) value_len)) != NULL) {
186 if (RegQueryValueEx(hk, key, 0,
187 &value_type, value, &value_len) != ERROR_SUCCESS ||
188 value_type != (DWORD) REG_SZ) {
189 free(value);
190 value = NULL;
192 assert(value == NULL || value_len == 0 ||
193 (value_len > 0 && value[value_len - 1] == 0));
195 RegCloseKey(hk);
196 *value_p = (char *) value;
198 return - (value == NULL);
201 static int
202 windows_service_registry_read_dword(const char * const key,
203 DWORD * const value_p)
205 HKEY hk = NULL;
206 DWORD value = 0;
207 DWORD value_len = (DWORD) sizeof value;
208 DWORD value_type;
209 int ret = -1;
211 *value_p = (DWORD) 0;
212 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
213 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
214 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
215 return -1;
217 if (RegQueryValueEx(hk, key, 0, &value_type, (void *) &value, &value_len)
218 == ERROR_SUCCESS && value_type == (DWORD) REG_DWORD) {
219 *value_p = value;
220 ret = 0;
222 RegCloseKey(hk);
224 return ret;
227 static int
228 windows_service_registry_write_string(const char * const key,
229 const char * const value)
231 HKEY hk = NULL;
232 size_t value_len;
233 int ret = 0;
235 value_len = strlen(value);
236 if (value_len > 0x7fffffff) {
237 return -1;
239 if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,
240 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
241 (DWORD) 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE,
242 NULL, &hk, NULL) != ERROR_SUCCESS) {
243 return -1;
245 if (RegSetValueEx(hk, key, NULL, REG_SZ, (const BYTE *) value,
246 (DWORD) value_len) != ERROR_SUCCESS) {
247 ret = -1;
249 RegCloseKey(hk);
251 return ret;
254 static int
255 windows_build_command_line_from_registry(int * const argc_p,
256 char *** const argv_p)
258 char dword_string[sizeof "2147483648"];
259 char *string_value;
260 DWORD dword_value;
261 int err = 0;
263 if ((*argv_p = cmdline_clone_options(*argc_p, *argv_p)) == NULL) {
264 exit(1);
266 if (windows_service_registry_read_string
267 ("ResolversList", &string_value) == 0) {
268 err += cmdline_add_option(argc_p, argv_p, "--resolvers-list");
269 err += cmdline_add_option(argc_p, argv_p, string_value);
270 free(string_value);
272 if (windows_service_registry_read_string
273 ("ResolverName", &string_value) == 0) {
274 err += cmdline_add_option(argc_p, argv_p, "--resolver-name");
275 err += cmdline_add_option(argc_p, argv_p, string_value);
276 free(string_value);
278 if (windows_service_registry_read_string
279 ("LocalAddress", &string_value) == 0) {
280 err += cmdline_add_option(argc_p, argv_p, "--local-address");
281 err += cmdline_add_option(argc_p, argv_p, string_value);
282 free(string_value);
284 if (windows_service_registry_read_string
285 ("ProviderKey", &string_value) == 0) {
286 err += cmdline_add_option(argc_p, argv_p, "--provider-key");
287 err += cmdline_add_option(argc_p, argv_p, string_value);
288 free(string_value);
290 if (windows_service_registry_read_string
291 ("ProviderName", &string_value) == 0) {
292 err += cmdline_add_option(argc_p, argv_p, "--provider-name");
293 err += cmdline_add_option(argc_p, argv_p, string_value);
294 free(string_value);
296 if (windows_service_registry_read_string
297 ("ResolverAddress", &string_value) == 0) {
298 err += cmdline_add_option(argc_p, argv_p, "--resolver-address");
299 err += cmdline_add_option(argc_p, argv_p, string_value);
300 free(string_value);
302 if (windows_service_registry_read_dword
303 ("EDNSPayloadSize", &dword_value) == 0) {
304 snprintf(dword_string, sizeof dword_string, "%ld", (long) dword_value);
305 err += cmdline_add_option(argc_p, argv_p, "--edns-payload-size");
306 err += cmdline_add_option(argc_p, argv_p, dword_string);
308 if (windows_service_registry_read_dword
309 ("MaxActiveRequests", &dword_value) == 0) {
310 snprintf(dword_string, sizeof dword_string, "%ld", (long) dword_value);
311 err += cmdline_add_option(argc_p, argv_p, "--max-active-requests");
312 err += cmdline_add_option(argc_p, argv_p, dword_string);
314 if (windows_service_registry_read_dword
315 ("TCPOnly", &dword_value) == 0 && dword_value > (DWORD) 0) {
316 err += cmdline_add_option(argc_p, argv_p, "--tcp-only");
318 windows_service_registry_read_multi_sz
319 ("Plugins", & (WindowsServiceParseMultiSzCb) {
320 .cb = windows_service_parse_multi_sz_cb,
321 .argc_p = argc_p,
322 .argv_p = argv_p,
323 .err_p = &err
325 if (err != 0) {
326 return -1;
328 return 0;
331 static void WINAPI
332 service_main(DWORD argc_, LPTSTR *argv_)
334 char **argv = (char **) argv_;
335 int argc = (int) argc_;
337 assert(argc_ < INT_MAX);
338 if (windows_build_command_line_from_registry(&argc, &argv) != 0) {
339 logger_noformat(NULL, LOG_ERR,
340 "Unable to build a command line from the registry");
341 return;
343 memset(&service_status, 0, sizeof service_status);
344 service_status.dwServiceType = SERVICE_WIN32;
345 service_status.dwCurrentState = SERVICE_START_PENDING;
346 service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
347 service_status_handle =
348 RegisterServiceCtrlHandler(WINDOWS_SERVICE_NAME, control_handler);
349 if (service_status_handle == 0) {
350 return;
352 service_status.dwCurrentState = SERVICE_RUNNING;
353 SetServiceStatus(service_status_handle, &service_status);
355 dnscrypt_proxy_main(argc, argv);
358 static int
359 windows_main(int argc, char *argv[])
361 static SERVICE_TABLE_ENTRY service_table[2];
362 char *service_name;
364 if ((service_name = strdup(WINDOWS_SERVICE_NAME)) == NULL) {
365 perror("strdup");
366 return 1;
368 memcpy(service_table, (SERVICE_TABLE_ENTRY[2]) {
369 { .lpServiceName = service_name, .lpServiceProc = service_main },
370 { .lpServiceName = NULL, .lpServiceProc = (void *) NULL }
371 }, sizeof service_table);
372 if (StartServiceCtrlDispatcher(service_table) == 0) {
373 free(service_name);
374 return dnscrypt_proxy_main(argc, argv);
376 app_is_running_as_a_service = 1;
378 return 0;
382 windows_service_uninstall(void)
384 SC_HANDLE scm_handle;
385 SC_HANDLE service_handle;
386 SERVICE_STATUS service_status;
387 int ret = 0;
389 scm_handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
390 if (scm_handle == NULL) {
391 return -1;
393 service_handle = OpenService(scm_handle, WINDOWS_SERVICE_NAME,
394 DELETE | SERVICE_STOP);
395 if (service_handle == NULL) {
396 CloseServiceHandle(scm_handle);
397 return 0;
399 ControlService(service_handle, SERVICE_CONTROL_STOP, &service_status);
400 if (DeleteService(service_handle) == 0) {
401 ret = -1;
403 CloseServiceHandle(service_handle);
404 CloseServiceHandle(scm_handle);
406 return ret;
409 static int
410 windows_registry_install(ProxyContext * const proxy_context)
412 if (proxy_context->resolvers_list != NULL) {
413 windows_service_registry_write_string("ResolversList",
414 proxy_context->resolvers_list);
416 if (proxy_context->resolver_name != NULL) {
417 windows_service_registry_write_string("ResolverName",
418 proxy_context->resolver_name);
420 if (proxy_context->local_ip != NULL) {
421 windows_service_registry_write_string("LocalAddress",
422 proxy_context->local_ip);
424 return 0;
428 windows_service_install(ProxyContext * const proxy_context)
430 char self_path[MAX_PATH];
431 SC_HANDLE scm_handle;
432 SC_HANDLE service_handle;
434 if (windows_registry_install(proxy_context) != 0) {
435 logger_noformat(proxy_context, LOG_ERR,
436 "Unable to set up registry keys");
437 return -1;
439 if (GetModuleFileName(NULL, self_path, MAX_PATH) <= (DWORD) 0) {
440 return -1;
442 scm_handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
443 if (scm_handle == NULL) {
444 return -1;
446 service_handle = CreateService
447 (scm_handle, WINDOWS_SERVICE_NAME,
448 WINDOWS_SERVICE_NAME, SERVICE_ALL_ACCESS,
449 SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
450 SERVICE_ERROR_NORMAL, self_path, NULL, NULL, NULL, NULL, NULL);
451 if (service_handle == NULL) {
452 CloseServiceHandle(scm_handle);
453 return -1;
455 StartService(service_handle, (DWORD) 0, NULL);
456 CloseServiceHandle(service_handle);
457 CloseServiceHandle(scm_handle);
459 return 0;
463 main(int argc, char *argv[])
465 return windows_main(argc, argv);
468 #endif