dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / dnscrypt / src / proxy / windows_service.c
blob385aeaa8b0ccd007bc7f4f94a46e9536e6f7da9d
2 #include <config.h>
4 #include "app.h"
5 #include "windows_service.h"
7 #ifndef _WIN32
9 int
10 main(int argc, char *argv[])
12 return dnscrypt_proxy_main(argc, argv);
15 #else
17 #include <assert.h>
18 #include <getopt.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <windows.h>
26 #include "logger.h"
27 #include "utils.h"
29 #ifndef WINDOWS_SERVICE_NAME
30 # define WINDOWS_SERVICE_NAME "dnscrypt-proxy"
31 #endif
32 #ifndef WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY
33 # define WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY \
34 "SYSTEM\\CurrentControlSet\\Services\\" \
35 WINDOWS_SERVICE_NAME "\\Parameters"
36 #endif
38 static SERVICE_STATUS service_status;
39 static SERVICE_STATUS_HANDLE service_status_handle;
40 static _Bool app_is_running_as_a_service;
42 static void WINAPI
43 control_handler(const DWORD wanted_state)
45 if (wanted_state == SERVICE_CONTROL_STOP &&
46 dnscrypt_proxy_loop_break() == 0) {
47 service_status.dwCurrentState = SERVICE_STOPPED;
49 SetServiceStatus(service_status_handle, &service_status);
52 static char **
53 cmdline_clone_options(const int argc, char ** const argv)
55 char **argv_new;
57 if (argc >= INT_MAX || (size_t) argc >= SIZE_MAX / sizeof *argv_new ||
58 (argv_new = calloc((unsigned int) argc + 1U,
59 sizeof *argv_new)) == NULL) {
60 return NULL;
62 memcpy(argv_new, argv, (unsigned int) (argc + 1U) * sizeof *argv_new);
64 return argv_new;
67 static int
68 cmdline_add_option(int * const argc_p, char *** const argv_p,
69 const char * const arg)
71 char *arg_dup;
72 char **argv_new;
74 if (*argc_p >= INT_MAX ||
75 SIZE_MAX / sizeof *argv_new <= (unsigned int) (*argc_p + 2U)) {
76 return -1;
78 if ((argv_new = realloc(*argv_p, (unsigned int) (*argc_p + 2U) *
79 sizeof *argv_new)) == NULL) {
80 return -1;
82 if ((arg_dup = strdup(arg)) == NULL) {
83 free(argv_new);
84 return -1;
86 argv_new[(*argc_p)++] = arg_dup;
87 argv_new[*argc_p] = NULL;
88 *argv_p = argv_new;
89 logger(NULL, LOG_INFO, "Adding command-line option: [%s]", arg_dup);
91 return 0;
94 typedef struct WindowsServiceParseMultiSzCb_ {
95 void (*cb)(struct WindowsServiceParseMultiSzCb_ *, const char *string);
96 int * const argc_p;
97 char *** const argv_p;
98 int * const err_p;
99 } WindowsServiceParseMultiSzCb;
101 static void
102 windows_service_parse_multi_sz_cb(WindowsServiceParseMultiSzCb * const cb,
103 const char *string)
105 assert(cb->cb == windows_service_parse_multi_sz_cb);
106 *(cb->err_p) += cmdline_add_option(cb->argc_p, cb->argv_p, "--plugin");
107 *(cb->err_p) += cmdline_add_option(cb->argc_p, cb->argv_p, string);
110 static int
111 windows_service_parse_multi_sz(WindowsServiceParseMultiSzCb * const cb,
112 const char * const multi_sz,
113 const size_t multi_sz_len)
115 const char *multi_sz_pnt = multi_sz;
116 const char *zero;
117 size_t len;
118 size_t multi_sz_remaining_len = multi_sz_len;
119 size_t zlen;
121 while (multi_sz_remaining_len > (size_t) 0U &&
122 (zero = memchr(multi_sz_pnt, 0, multi_sz_remaining_len)) != NULL) {
123 if ((len = (size_t) (zero - multi_sz_pnt)) > (size_t) 0U) {
124 cb->cb(cb, multi_sz_pnt);
126 zlen = len + (size_t) 1U;
127 assert(zlen <= multi_sz_remaining_len);
128 multi_sz_remaining_len -= zlen;
129 multi_sz_pnt += zlen;
131 return 0;
134 static int
135 windows_service_registry_read_multi_sz(const char * const key,
136 WindowsServiceParseMultiSzCb * const cb)
138 BYTE *value = NULL;
139 HKEY hk = NULL;
140 DWORD value_len;
141 DWORD value_type;
143 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
144 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
145 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
146 return -1;
148 if (RegQueryValueEx(hk, key, 0,
149 &value_type, NULL, &value_len) == ERROR_SUCCESS &&
150 value_type == (DWORD) REG_MULTI_SZ &&
151 value_len <= SIZE_MAX && value_len > (DWORD) 0 &&
152 (value = malloc((size_t) value_len)) != NULL) {
153 if (RegQueryValueEx(hk, key, 0,
154 &value_type, value, &value_len) != ERROR_SUCCESS ||
155 value_type != (DWORD) REG_MULTI_SZ) {
156 free(value);
157 value = NULL;
159 assert(value == NULL || value_len == 0 ||
160 (value_len > 0 && value[value_len - 1] == 0));
162 RegCloseKey(hk);
163 if (value == NULL) {
164 return -1;
166 windows_service_parse_multi_sz(cb, (const char *) value,
167 (size_t) value_len);
168 free(value);
170 return 0;
173 static int
174 windows_service_registry_read_string(const char * const key,
175 char ** const value_p)
177 BYTE *value = NULL;
178 HKEY hk = NULL;
179 DWORD value_len;
180 DWORD value_type;
182 *value_p = NULL;
183 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
184 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
185 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
186 return -1;
188 if (RegQueryValueEx(hk, key, 0,
189 &value_type, NULL, &value_len) == ERROR_SUCCESS &&
190 value_type == (DWORD) REG_SZ &&
191 value_len <= SIZE_MAX && value_len > (DWORD) 0 &&
192 (value = malloc((size_t) value_len)) != NULL) {
193 if (RegQueryValueEx(hk, key, 0,
194 &value_type, value, &value_len) != ERROR_SUCCESS ||
195 value_type != (DWORD) REG_SZ) {
196 free(value);
197 value = NULL;
199 assert(value == NULL || value_len == 0 ||
200 (value_len > 0 && value[value_len - 1] == 0));
202 RegCloseKey(hk);
203 *value_p = (char *) value;
205 return - (value == NULL);
208 static int
209 windows_service_registry_read_dword(const char * const key,
210 DWORD * const value_p)
212 HKEY hk = NULL;
213 DWORD value = 0;
214 DWORD value_len = (DWORD) sizeof value;
215 DWORD value_type;
216 int ret = -1;
218 *value_p = (DWORD) 0;
219 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
220 WINDOWS_SERVICE_REGISTRY_PARAMETERS_KEY,
221 (DWORD) 0, KEY_READ, &hk) != ERROR_SUCCESS) {
222 return -1;
224 if (RegQueryValueEx(hk, key, 0, &value_type, (void *) &value, &value_len)
225 == ERROR_SUCCESS && value_type == (DWORD) REG_DWORD) {
226 *value_p = value;
227 ret = 0;
229 RegCloseKey(hk);
231 return ret;
234 static int
235 windows_build_command_line_from_registry(int * const argc_p,
236 char *** const argv_p)
238 char dword_string[sizeof "2147483648"];
239 char *string_value;
240 DWORD dword_value;
241 int err = 0;
243 if ((*argv_p = cmdline_clone_options(*argc_p, *argv_p)) == NULL) {
244 exit(1);
246 if (windows_service_registry_read_string
247 ("LocalAddress", &string_value) == 0) {
248 err += cmdline_add_option(argc_p, argv_p, "--local-address");
249 err += cmdline_add_option(argc_p, argv_p, string_value);
250 free(string_value);
252 if (windows_service_registry_read_string
253 ("ProviderKey", &string_value) == 0) {
254 err += cmdline_add_option(argc_p, argv_p, "--provider-key");
255 err += cmdline_add_option(argc_p, argv_p, string_value);
256 free(string_value);
258 if (windows_service_registry_read_string
259 ("ProviderName", &string_value) == 0) {
260 err += cmdline_add_option(argc_p, argv_p, "--provider-name");
261 err += cmdline_add_option(argc_p, argv_p, string_value);
262 free(string_value);
264 if (windows_service_registry_read_string
265 ("ResolverAddress", &string_value) == 0) {
266 err += cmdline_add_option(argc_p, argv_p, "--resolver-address");
267 err += cmdline_add_option(argc_p, argv_p, string_value);
268 free(string_value);
270 if (windows_service_registry_read_dword
271 ("EDNSPayloadSize", &dword_value) == 0) {
272 snprintf(dword_string, sizeof dword_string, "%ld", (long) dword_value);
273 err += cmdline_add_option(argc_p, argv_p, "--edns-payload-size");
274 err += cmdline_add_option(argc_p, argv_p, dword_string);
276 if (windows_service_registry_read_dword
277 ("MaxActiveRequests", &dword_value) == 0) {
278 snprintf(dword_string, sizeof dword_string, "%ld", (long) dword_value);
279 err += cmdline_add_option(argc_p, argv_p, "--max-active-requests");
280 err += cmdline_add_option(argc_p, argv_p, dword_string);
282 if (windows_service_registry_read_dword
283 ("TCPOnly", &dword_value) == 0 && dword_value > (DWORD) 0) {
284 err += cmdline_add_option(argc_p, argv_p, "--tcp-only");
286 windows_service_registry_read_multi_sz
287 ("Plugins", & (WindowsServiceParseMultiSzCb) {
288 .cb = windows_service_parse_multi_sz_cb,
289 .argc_p = argc_p,
290 .argv_p = argv_p,
291 .err_p = &err
293 if (err != 0) {
294 return -1;
296 return 0;
299 static void WINAPI
300 service_main(DWORD argc_, LPTSTR *argv_)
302 char **argv = (char **) argv_;
303 int argc = (int) argc_;
305 assert(argc_ < INT_MAX);
306 if (windows_build_command_line_from_registry(&argc, &argv) != 0) {
307 logger_noformat(NULL, LOG_ERR,
308 "Unable to build a command line from the registry");
309 return;
311 memset(&service_status, 0, sizeof service_status);
312 service_status.dwServiceType = SERVICE_WIN32;
313 service_status.dwCurrentState = SERVICE_START_PENDING;
314 service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
315 service_status_handle =
316 RegisterServiceCtrlHandler(WINDOWS_SERVICE_NAME, control_handler);
317 if (service_status_handle == 0) {
318 return;
320 service_status.dwCurrentState = SERVICE_RUNNING;
321 SetServiceStatus(service_status_handle, &service_status);
323 dnscrypt_proxy_main(argc, argv);
326 static int
327 windows_main(int argc, char *argv[])
329 static SERVICE_TABLE_ENTRY service_table[2];
330 char *service_name;
332 if ((service_name = strdup(WINDOWS_SERVICE_NAME)) == NULL) {
333 perror("strdup");
334 return 1;
336 memcpy(service_table, (SERVICE_TABLE_ENTRY[2]) {
337 { .lpServiceName = service_name, .lpServiceProc = service_main },
338 { .lpServiceName = NULL, .lpServiceProc = (void *) NULL }
339 }, sizeof service_table);
340 if (StartServiceCtrlDispatcher(service_table) == 0) {
341 free(service_name);
342 return dnscrypt_proxy_main(argc, argv);
344 app_is_running_as_a_service = 1;
346 return 0;
349 static int
350 windows_service_uninstall(void)
352 SC_HANDLE scm_handle;
353 SC_HANDLE service_handle;
354 int ret = 0;
356 scm_handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
357 if (scm_handle == NULL) {
358 return -1;
360 service_handle = OpenService(scm_handle, WINDOWS_SERVICE_NAME, DELETE);
361 if (service_handle == NULL) {
362 CloseServiceHandle(scm_handle);
363 return 0;
365 if (DeleteService(service_handle) == 0) {
366 ret = -1;
368 CloseServiceHandle(service_handle);
369 CloseServiceHandle(scm_handle);
371 return ret;
374 static int
375 windows_service_install(const int argc, const char * const argv[])
377 char self_path[MAX_PATH];
378 SC_HANDLE scm_handle;
379 SC_HANDLE service_handle;
381 (void) argc;
382 (void) argv;
383 if (GetModuleFileName(NULL, self_path, MAX_PATH) <= (DWORD) 0) {
384 return -1;
386 scm_handle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
387 if (scm_handle == NULL) {
388 return -1;
390 service_handle = CreateService
391 (scm_handle, WINDOWS_SERVICE_NAME,
392 WINDOWS_SERVICE_NAME, SERVICE_ALL_ACCESS,
393 SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
394 SERVICE_ERROR_NORMAL, self_path, NULL, NULL, NULL, NULL, NULL);
395 if (service_handle == NULL) {
396 CloseServiceHandle(scm_handle);
397 return -1;
399 StartService(service_handle, (DWORD) 0, NULL);
400 CloseServiceHandle(service_handle);
401 CloseServiceHandle(scm_handle);
403 return 0;
407 windows_service_option(const int opt_flag, const int argc,
408 const char *argv[])
410 if (app_is_running_as_a_service != 0) {
411 return 0;
413 switch (opt_flag) {
414 case WIN_OPTION_INSTALL:
415 case WIN_OPTION_REINSTALL:
416 windows_service_uninstall();
417 if (windows_service_install(argc, argv) != 0) {
418 logger_noformat(NULL, LOG_ERR, "Unable to install the service");
419 exit(1);
420 } else {
421 logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME
422 " service has been installed and started");
423 exit(0);
425 break;
426 case WIN_OPTION_UNINSTALL:
427 if (windows_service_uninstall() != 0) {
428 logger_noformat(NULL, LOG_ERR, "Unable to uninstall the service");
429 exit(1);
430 } else {
431 logger_noformat(NULL, LOG_INFO, "The " WINDOWS_SERVICE_NAME
432 " service has been removed from this system");
433 exit(0);
435 break;
436 default:
437 return -1;
439 return 0;
443 main(int argc, char *argv[])
445 return windows_main(argc, argv);
448 #endif