xref: /freebsd/contrib/unbound/winrc/win_svc.c (revision be771a7b7f4580a30d99e41a5bb1b93a385a119d)
1*be771a7bSCy Schubert /*
2*be771a7bSCy Schubert  * winrc/win_svc.c - windows services API implementation for unbound
3*be771a7bSCy Schubert  *
4*be771a7bSCy Schubert  * Copyright (c) 2009, NLnet Labs. All rights reserved.
5*be771a7bSCy Schubert  *
6*be771a7bSCy Schubert  * This software is open source.
7*be771a7bSCy Schubert  *
8*be771a7bSCy Schubert  * Redistribution and use in source and binary forms, with or without
9*be771a7bSCy Schubert  * modification, are permitted provided that the following conditions
10*be771a7bSCy Schubert  * are met:
11*be771a7bSCy Schubert  *
12*be771a7bSCy Schubert  * Redistributions of source code must retain the above copyright notice,
13*be771a7bSCy Schubert  * this list of conditions and the following disclaimer.
14*be771a7bSCy Schubert  *
15*be771a7bSCy Schubert  * Redistributions in binary form must reproduce the above copyright notice,
16*be771a7bSCy Schubert  * this list of conditions and the following disclaimer in the documentation
17*be771a7bSCy Schubert  * and/or other materials provided with the distribution.
18*be771a7bSCy Schubert  *
19*be771a7bSCy Schubert  * Neither the name of the NLNET LABS nor the names of its contributors may
20*be771a7bSCy Schubert  * be used to endorse or promote products derived from this software without
21*be771a7bSCy Schubert  * specific prior written permission.
22*be771a7bSCy Schubert  *
23*be771a7bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*be771a7bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*be771a7bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*be771a7bSCy Schubert  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*be771a7bSCy Schubert  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*be771a7bSCy Schubert  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*be771a7bSCy Schubert  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*be771a7bSCy Schubert  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*be771a7bSCy Schubert  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*be771a7bSCy Schubert  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*be771a7bSCy Schubert  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*be771a7bSCy Schubert  */
35*be771a7bSCy Schubert 
36*be771a7bSCy Schubert /**
37*be771a7bSCy Schubert  * \file
38*be771a7bSCy Schubert  *
39*be771a7bSCy Schubert  * This file contains functions to integrate with the windows services API.
40*be771a7bSCy Schubert  * This means it handles the commandline switches to install and remove
41*be771a7bSCy Schubert  * the service (via CreateService and DeleteService), it handles
42*be771a7bSCy Schubert  * the ServiceMain() main service entry point when started as a service,
43*be771a7bSCy Schubert  * and it handles the Handler[_ex]() to process requests to the service
44*be771a7bSCy Schubert  * (such as start and stop and status).
45*be771a7bSCy Schubert  */
46*be771a7bSCy Schubert #include "config.h"
47*be771a7bSCy Schubert #include "winrc/win_svc.h"
48*be771a7bSCy Schubert #include "winrc/w_inst.h"
49*be771a7bSCy Schubert #include "daemon/daemon.h"
50*be771a7bSCy Schubert #include "daemon/worker.h"
51*be771a7bSCy Schubert #include "daemon/remote.h"
52*be771a7bSCy Schubert #include "util/config_file.h"
53*be771a7bSCy Schubert #include "util/netevent.h"
54*be771a7bSCy Schubert #include "util/ub_event.h"
55*be771a7bSCy Schubert #include "util/net_help.h"
56*be771a7bSCy Schubert 
57*be771a7bSCy Schubert /** global service status */
58*be771a7bSCy Schubert static SERVICE_STATUS	service_status;
59*be771a7bSCy Schubert /** global service status handle */
60*be771a7bSCy Schubert static SERVICE_STATUS_HANDLE service_status_handle;
61*be771a7bSCy Schubert /** global service stop event */
62*be771a7bSCy Schubert static WSAEVENT service_stop_event = NULL;
63*be771a7bSCy Schubert /** event struct for stop callbacks */
64*be771a7bSCy Schubert static struct ub_event* service_stop_ev = NULL;
65*be771a7bSCy Schubert /** if stop even means shutdown or restart */
66*be771a7bSCy Schubert static int service_stop_shutdown = 0;
67*be771a7bSCy Schubert /** config file to open. global communication to service_main() */
68*be771a7bSCy Schubert static char* service_cfgfile = CONFIGFILE;
69*be771a7bSCy Schubert /** commandline verbosity. global communication to service_main() */
70*be771a7bSCy Schubert static int service_cmdline_verbose = 0;
71*be771a7bSCy Schubert /** the cron callback */
72*be771a7bSCy Schubert static struct comm_timer* service_cron = NULL;
73*be771a7bSCy Schubert /** the cron thread */
74*be771a7bSCy Schubert static ub_thread_type cron_thread = NULL;
75*be771a7bSCy Schubert /** if cron has already done its quick check */
76*be771a7bSCy Schubert static int cron_was_quick = 0;
77*be771a7bSCy Schubert 
78*be771a7bSCy Schubert /**
79*be771a7bSCy Schubert  * Report current service status to service control manager
80*be771a7bSCy Schubert  * @param state: current state
81*be771a7bSCy Schubert  * @param exitcode: error code (when stopped)
82*be771a7bSCy Schubert  * @param wait: pending operation estimated time in milliseconds.
83*be771a7bSCy Schubert  */
84*be771a7bSCy Schubert static void report_status(DWORD state, DWORD exitcode, DWORD wait)
85*be771a7bSCy Schubert {
86*be771a7bSCy Schubert 	static DWORD checkpoint = 1;
87*be771a7bSCy Schubert 	service_status.dwCurrentState = state;
88*be771a7bSCy Schubert 	service_status.dwWin32ExitCode = exitcode;
89*be771a7bSCy Schubert 	service_status.dwWaitHint = wait;
90*be771a7bSCy Schubert 	if(state == SERVICE_START_PENDING)
91*be771a7bSCy Schubert 		service_status.dwControlsAccepted = 0;
92*be771a7bSCy Schubert 	else 	service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
93*be771a7bSCy Schubert 	if(state == SERVICE_RUNNING || state == SERVICE_STOPPED)
94*be771a7bSCy Schubert 		service_status.dwCheckPoint = 0;
95*be771a7bSCy Schubert 	else	service_status.dwCheckPoint = checkpoint++;
96*be771a7bSCy Schubert 	SetServiceStatus(service_status_handle, &service_status);
97*be771a7bSCy Schubert }
98*be771a7bSCy Schubert 
99*be771a7bSCy Schubert /**
100*be771a7bSCy Schubert  * Service control handler. Called by serviceControlManager when a control
101*be771a7bSCy Schubert  * code is sent to the service (with ControlService).
102*be771a7bSCy Schubert  * @param ctrl: control code
103*be771a7bSCy Schubert  */
104*be771a7bSCy Schubert static void
105*be771a7bSCy Schubert hdlr(DWORD ctrl)
106*be771a7bSCy Schubert {
107*be771a7bSCy Schubert 	if(ctrl == SERVICE_CONTROL_STOP) {
108*be771a7bSCy Schubert 		report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
109*be771a7bSCy Schubert 		service_stop_shutdown = 1;
110*be771a7bSCy Schubert 		/* send signal to stop */
111*be771a7bSCy Schubert 		if(!WSASetEvent(service_stop_event))
112*be771a7bSCy Schubert 			log_err("Could not WSASetEvent: %s",
113*be771a7bSCy Schubert 				wsa_strerror(WSAGetLastError()));
114*be771a7bSCy Schubert 		return;
115*be771a7bSCy Schubert 	} else {
116*be771a7bSCy Schubert 		/* ctrl == SERVICE_CONTROL_INTERROGATE or whatever */
117*be771a7bSCy Schubert 		/* update status */
118*be771a7bSCy Schubert 		report_status(service_status.dwCurrentState, NO_ERROR, 0);
119*be771a7bSCy Schubert 	}
120*be771a7bSCy Schubert }
121*be771a7bSCy Schubert 
122*be771a7bSCy Schubert /**
123*be771a7bSCy Schubert  * report event to system event log
124*be771a7bSCy Schubert  * For use during startup and shutdown.
125*be771a7bSCy Schubert  * @param str: the error
126*be771a7bSCy Schubert  */
127*be771a7bSCy Schubert static void
128*be771a7bSCy Schubert reportev(const char* str)
129*be771a7bSCy Schubert {
130*be771a7bSCy Schubert 	char b[256];
131*be771a7bSCy Schubert 	char e[256];
132*be771a7bSCy Schubert 	HANDLE* s;
133*be771a7bSCy Schubert 	LPCTSTR msg = b;
134*be771a7bSCy Schubert 	/* print quickly to keep GetLastError value */
135*be771a7bSCy Schubert 	wsvc_err2str(e, sizeof(e), str, GetLastError());
136*be771a7bSCy Schubert 	snprintf(b, sizeof(b), "%s: %s", SERVICE_NAME, e);
137*be771a7bSCy Schubert 	s = RegisterEventSource(NULL, SERVICE_NAME);
138*be771a7bSCy Schubert 	if(!s) return;
139*be771a7bSCy Schubert 	ReportEvent(s, /* event log */
140*be771a7bSCy Schubert 		EVENTLOG_ERROR_TYPE, /* event type */
141*be771a7bSCy Schubert 		0, /* event category */
142*be771a7bSCy Schubert 		MSG_GENERIC_ERR, /* event ID (from gen_msg.mc) */
143*be771a7bSCy Schubert 		NULL, /* user security context */
144*be771a7bSCy Schubert 		1, /* numstrings */
145*be771a7bSCy Schubert 		0, /* binary size */
146*be771a7bSCy Schubert 		&msg, /* strings */
147*be771a7bSCy Schubert 		NULL); /* binary data */
148*be771a7bSCy Schubert 	DeregisterEventSource(s);
149*be771a7bSCy Schubert }
150*be771a7bSCy Schubert 
151*be771a7bSCy Schubert /**
152*be771a7bSCy Schubert  * Obtain registry string (if it exists).
153*be771a7bSCy Schubert  * @param key: key string
154*be771a7bSCy Schubert  * @param name: name of value to fetch.
155*be771a7bSCy Schubert  * @return malloced string with the result or NULL if it did not
156*be771a7bSCy Schubert  * exist on an error (logged) was encountered.
157*be771a7bSCy Schubert  */
158*be771a7bSCy Schubert static char*
159*be771a7bSCy Schubert lookup_reg_str(const char* key, const char* name)
160*be771a7bSCy Schubert {
161*be771a7bSCy Schubert 	HKEY hk = NULL;
162*be771a7bSCy Schubert 	DWORD type = 0;
163*be771a7bSCy Schubert 	BYTE buf[1024];
164*be771a7bSCy Schubert 	DWORD len = (DWORD)sizeof(buf);
165*be771a7bSCy Schubert 	LONG ret;
166*be771a7bSCy Schubert 	char* result = NULL;
167*be771a7bSCy Schubert 	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
168*be771a7bSCy Schubert 	if(ret == ERROR_FILE_NOT_FOUND)
169*be771a7bSCy Schubert 		return NULL; /* key does not exist */
170*be771a7bSCy Schubert 	else if(ret != ERROR_SUCCESS) {
171*be771a7bSCy Schubert 		reportev("RegOpenKeyEx failed");
172*be771a7bSCy Schubert 		return NULL;
173*be771a7bSCy Schubert 	}
174*be771a7bSCy Schubert 	ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
175*be771a7bSCy Schubert 	if(RegCloseKey(hk))
176*be771a7bSCy Schubert 		reportev("RegCloseKey");
177*be771a7bSCy Schubert 	if(ret == ERROR_FILE_NOT_FOUND)
178*be771a7bSCy Schubert 		return NULL; /* name does not exist */
179*be771a7bSCy Schubert 	else if(ret != ERROR_SUCCESS) {
180*be771a7bSCy Schubert 		reportev("RegQueryValueEx failed");
181*be771a7bSCy Schubert 		return NULL;
182*be771a7bSCy Schubert 	}
183*be771a7bSCy Schubert 	if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
184*be771a7bSCy Schubert 		buf[sizeof(buf)-1] = 0;
185*be771a7bSCy Schubert 		buf[sizeof(buf)-2] = 0; /* for multi_sz */
186*be771a7bSCy Schubert 		result = strdup((char*)buf);
187*be771a7bSCy Schubert 		if(!result) reportev("out of memory");
188*be771a7bSCy Schubert 	}
189*be771a7bSCy Schubert 	return result;
190*be771a7bSCy Schubert }
191*be771a7bSCy Schubert 
192*be771a7bSCy Schubert /**
193*be771a7bSCy Schubert  * Obtain registry integer (if it exists).
194*be771a7bSCy Schubert  * @param key: key string
195*be771a7bSCy Schubert  * @param name: name of value to fetch.
196*be771a7bSCy Schubert  * @return integer value (if it exists), or 0 on error.
197*be771a7bSCy Schubert  */
198*be771a7bSCy Schubert static int
199*be771a7bSCy Schubert lookup_reg_int(const char* key, const char* name)
200*be771a7bSCy Schubert {
201*be771a7bSCy Schubert 	HKEY hk = NULL;
202*be771a7bSCy Schubert 	DWORD type = 0;
203*be771a7bSCy Schubert 	BYTE buf[1024];
204*be771a7bSCy Schubert 	DWORD len = (DWORD)sizeof(buf);
205*be771a7bSCy Schubert 	LONG ret;
206*be771a7bSCy Schubert 	int result = 0;
207*be771a7bSCy Schubert 	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
208*be771a7bSCy Schubert 	if(ret == ERROR_FILE_NOT_FOUND)
209*be771a7bSCy Schubert 		return 0; /* key does not exist */
210*be771a7bSCy Schubert 	else if(ret != ERROR_SUCCESS) {
211*be771a7bSCy Schubert 		reportev("RegOpenKeyEx failed");
212*be771a7bSCy Schubert 		return 0;
213*be771a7bSCy Schubert 	}
214*be771a7bSCy Schubert 	ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
215*be771a7bSCy Schubert 	if(RegCloseKey(hk))
216*be771a7bSCy Schubert 		reportev("RegCloseKey");
217*be771a7bSCy Schubert 	if(ret == ERROR_FILE_NOT_FOUND)
218*be771a7bSCy Schubert 		return 0; /* name does not exist */
219*be771a7bSCy Schubert 	else if(ret != ERROR_SUCCESS) {
220*be771a7bSCy Schubert 		reportev("RegQueryValueEx failed");
221*be771a7bSCy Schubert 		return 0;
222*be771a7bSCy Schubert 	}
223*be771a7bSCy Schubert 	if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
224*be771a7bSCy Schubert 		buf[sizeof(buf)-1] = 0;
225*be771a7bSCy Schubert 		buf[sizeof(buf)-2] = 0; /* for multi_sz */
226*be771a7bSCy Schubert 		result = atoi((char*)buf);
227*be771a7bSCy Schubert 	} else if(type == REG_DWORD) {
228*be771a7bSCy Schubert 		DWORD r;
229*be771a7bSCy Schubert 		memmove(&r, buf, sizeof(r));
230*be771a7bSCy Schubert 		result = r;
231*be771a7bSCy Schubert 	}
232*be771a7bSCy Schubert 	return result;
233*be771a7bSCy Schubert }
234*be771a7bSCy Schubert 
235*be771a7bSCy Schubert /** wait for unbound-anchor process to finish */
236*be771a7bSCy Schubert static void
237*be771a7bSCy Schubert waitforubanchor(PROCESS_INFORMATION* pinfo)
238*be771a7bSCy Schubert {
239*be771a7bSCy Schubert 	/* we have 5 seconds scheduled for it, usually it will be very fast,
240*be771a7bSCy Schubert 	 * with only a UDP message or two (100 msec or so), but the https
241*be771a7bSCy Schubert 	 * connections could take some time */
242*be771a7bSCy Schubert 	DWORD count = 7900;
243*be771a7bSCy Schubert 	DWORD ret = WAIT_TIMEOUT;
244*be771a7bSCy Schubert 	/* decrease timer every 1/10 second, we are still starting up */
245*be771a7bSCy Schubert 	while(ret == WAIT_TIMEOUT) {
246*be771a7bSCy Schubert 		ret = WaitForSingleObject(pinfo->hProcess, 100);
247*be771a7bSCy Schubert 		if(count > 4000) count -= 100;
248*be771a7bSCy Schubert 		else count--; /* go slow, it is taking long */
249*be771a7bSCy Schubert 		if(count > 3000)
250*be771a7bSCy Schubert 			report_status(SERVICE_START_PENDING, NO_ERROR, count);
251*be771a7bSCy Schubert 	}
252*be771a7bSCy Schubert 	verbose(VERB_ALGO, "unbound-anchor done");
253*be771a7bSCy Schubert 	if(ret != WAIT_OBJECT_0) {
254*be771a7bSCy Schubert 		return; /* did not end successfully */
255*be771a7bSCy Schubert 	}
256*be771a7bSCy Schubert 	if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
257*be771a7bSCy Schubert 		log_err("GetExitCodeProcess failed");
258*be771a7bSCy Schubert 		return;
259*be771a7bSCy Schubert 	}
260*be771a7bSCy Schubert 	verbose(VERB_ALGO, "unbound-anchor exit code is %d", (int)ret);
261*be771a7bSCy Schubert 	if(ret != 0) {
262*be771a7bSCy Schubert 		log_info("The root trust anchor has been updated.");
263*be771a7bSCy Schubert 	}
264*be771a7bSCy Schubert }
265*be771a7bSCy Schubert 
266*be771a7bSCy Schubert 
267*be771a7bSCy Schubert /**
268*be771a7bSCy Schubert  * Perform root anchor update if so configured, by calling that process
269*be771a7bSCy Schubert  */
270*be771a7bSCy Schubert static void
271*be771a7bSCy Schubert call_root_update(void)
272*be771a7bSCy Schubert {
273*be771a7bSCy Schubert 	char* rootanchor;
274*be771a7bSCy Schubert  	rootanchor = lookup_reg_str("Software\\Unbound", "RootAnchor");
275*be771a7bSCy Schubert 	if(rootanchor && strlen(rootanchor)>0) {
276*be771a7bSCy Schubert 		STARTUPINFO sinfo;
277*be771a7bSCy Schubert 		PROCESS_INFORMATION pinfo;
278*be771a7bSCy Schubert 		memset(&pinfo, 0, sizeof(pinfo));
279*be771a7bSCy Schubert 		memset(&sinfo, 0, sizeof(sinfo));
280*be771a7bSCy Schubert 		sinfo.cb = sizeof(sinfo);
281*be771a7bSCy Schubert 		verbose(VERB_ALGO, "rootanchor: %s", rootanchor);
282*be771a7bSCy Schubert 		report_status(SERVICE_START_PENDING, NO_ERROR, 8000);
283*be771a7bSCy Schubert 		if(!CreateProcess(NULL, rootanchor, NULL, NULL, 0,
284*be771a7bSCy Schubert 			CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
285*be771a7bSCy Schubert 			log_err("CreateProcess error for unbound-anchor.exe");
286*be771a7bSCy Schubert 		else {
287*be771a7bSCy Schubert 			waitforubanchor(&pinfo);
288*be771a7bSCy Schubert 			CloseHandle(pinfo.hProcess);
289*be771a7bSCy Schubert 			CloseHandle(pinfo.hThread);
290*be771a7bSCy Schubert 		}
291*be771a7bSCy Schubert 	}
292*be771a7bSCy Schubert 	free(rootanchor);
293*be771a7bSCy Schubert }
294*be771a7bSCy Schubert 
295*be771a7bSCy Schubert /**
296*be771a7bSCy Schubert  * Init service. Keeps calling status pending to tell service control
297*be771a7bSCy Schubert  * manager that this process is not hanging.
298*be771a7bSCy Schubert  * @param r: restart, true on restart
299*be771a7bSCy Schubert  * @param d: daemon returned here.
300*be771a7bSCy Schubert  * @param c: config file returned here.
301*be771a7bSCy Schubert  * @return false if failed.
302*be771a7bSCy Schubert  */
303*be771a7bSCy Schubert static int
304*be771a7bSCy Schubert service_init(int r, struct daemon** d, struct config_file** c)
305*be771a7bSCy Schubert {
306*be771a7bSCy Schubert 	struct config_file* cfg = NULL;
307*be771a7bSCy Schubert 	struct daemon* daemon = NULL;
308*be771a7bSCy Schubert 
309*be771a7bSCy Schubert 	if(!service_cfgfile) {
310*be771a7bSCy Schubert 		char* newf = lookup_reg_str("Software\\Unbound", "ConfigFile");
311*be771a7bSCy Schubert 		if(newf) service_cfgfile = newf;
312*be771a7bSCy Schubert 		else 	service_cfgfile = strdup(CONFIGFILE);
313*be771a7bSCy Schubert 		if(!service_cfgfile) fatal_exit("out of memory");
314*be771a7bSCy Schubert 	}
315*be771a7bSCy Schubert 
316*be771a7bSCy Schubert 	/* create daemon */
317*be771a7bSCy Schubert 	if(r) 	daemon = *d;
318*be771a7bSCy Schubert 	else 	daemon = daemon_init();
319*be771a7bSCy Schubert 	if(!daemon) return 0;
320*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2800);
321*be771a7bSCy Schubert 
322*be771a7bSCy Schubert 	/* read config */
323*be771a7bSCy Schubert 	cfg = config_create();
324*be771a7bSCy Schubert 	if(!cfg) return 0;
325*be771a7bSCy Schubert 	if(!config_read(cfg, service_cfgfile, daemon->chroot)) {
326*be771a7bSCy Schubert 		if(errno != ENOENT) {
327*be771a7bSCy Schubert 			log_err("error in config file");
328*be771a7bSCy Schubert 			return 0;
329*be771a7bSCy Schubert 		}
330*be771a7bSCy Schubert 		log_warn("could not open config file, using defaults");
331*be771a7bSCy Schubert 	}
332*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2600);
333*be771a7bSCy Schubert 
334*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - apply settings");
335*be771a7bSCy Schubert 	/* apply settings and init */
336*be771a7bSCy Schubert 	verbosity = cfg->verbosity + service_cmdline_verbose;
337*be771a7bSCy Schubert 	w_config_adjust_directory(cfg);
338*be771a7bSCy Schubert 	if(cfg->directory && cfg->directory[0]) {
339*be771a7bSCy Schubert 		char* dir = cfg->directory;
340*be771a7bSCy Schubert 		if(chdir(dir)) {
341*be771a7bSCy Schubert 			log_err("could not chdir to %s: %s",
342*be771a7bSCy Schubert 				dir, strerror(errno));
343*be771a7bSCy Schubert 			if(errno != ENOENT)
344*be771a7bSCy Schubert 				return 0;
345*be771a7bSCy Schubert 			log_warn("could not change directory - continuing");
346*be771a7bSCy Schubert 		} else
347*be771a7bSCy Schubert 			verbose(VERB_QUERY, "chdir to %s", dir);
348*be771a7bSCy Schubert 	}
349*be771a7bSCy Schubert 	log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
350*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2400);
351*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - apply cfg");
352*be771a7bSCy Schubert 	daemon_apply_cfg(daemon, cfg);
353*be771a7bSCy Schubert 
354*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300);
355*be771a7bSCy Schubert 	if(!r) {
356*be771a7bSCy Schubert 		if(!daemon_privileged(daemon))
357*be771a7bSCy Schubert 			fatal_exit("could not do privileged setup");
358*be771a7bSCy Schubert 	}
359*be771a7bSCy Schubert 	if(!(daemon->rc = daemon_remote_create(cfg))) {
360*be771a7bSCy Schubert 		log_err("could not set up remote-control");
361*be771a7bSCy Schubert 		daemon_delete(daemon);
362*be771a7bSCy Schubert 		config_delete(cfg);
363*be771a7bSCy Schubert 		return 0;
364*be771a7bSCy Schubert 	}
365*be771a7bSCy Schubert 	if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
366*be771a7bSCy Schubert 		if(!(daemon->listen_dot_sslctx = listen_sslctx_create(
367*be771a7bSCy Schubert 			cfg->ssl_service_key, cfg->ssl_service_pem, NULL,
368*be771a7bSCy Schubert 			cfg->tls_ciphers, cfg->tls_ciphersuites,
369*be771a7bSCy Schubert 			(cfg->tls_session_ticket_keys.first &&
370*be771a7bSCy Schubert 			cfg->tls_session_ticket_keys.first->str[0] != 0),
371*be771a7bSCy Schubert 			1, 0))) {
372*be771a7bSCy Schubert 			fatal_exit("could not set up listen SSL_CTX");
373*be771a7bSCy Schubert 		}
374*be771a7bSCy Schubert #ifdef HAVE_NGHTTP2_NGHTTP2_H
375*be771a7bSCy Schubert 		if(cfg_has_https(cfg)) {
376*be771a7bSCy Schubert 			if(!(daemon->listen_doh_sslctx = listen_sslctx_create(
377*be771a7bSCy Schubert 				cfg->ssl_service_key, cfg->ssl_service_pem, NULL,
378*be771a7bSCy Schubert 				cfg->tls_ciphers, cfg->tls_ciphersuites,
379*be771a7bSCy Schubert 				(cfg->tls_session_ticket_keys.first &&
380*be771a7bSCy Schubert 				cfg->tls_session_ticket_keys.first->str[0] != 0),
381*be771a7bSCy Schubert 				0, 1))) {
382*be771a7bSCy Schubert 				fatal_exit("could not set up listen doh SSL_CTX");
383*be771a7bSCy Schubert 			}
384*be771a7bSCy Schubert 		}
385*be771a7bSCy Schubert #endif
386*be771a7bSCy Schubert #ifdef HAVE_NGTCP2
387*be771a7bSCy Schubert 		if(cfg_has_quic(cfg)) {
388*be771a7bSCy Schubert 			if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
389*be771a7bSCy Schubert 				cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
390*be771a7bSCy Schubert 				fatal_exit("could not set up quic SSL_CTX");
391*be771a7bSCy Schubert 			}
392*be771a7bSCy Schubert 		}
393*be771a7bSCy Schubert #endif /* HAVE_NGTCP2 */
394*be771a7bSCy Schubert 	}
395*be771a7bSCy Schubert 	if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
396*be771a7bSCy Schubert 		cfg->tls_cert_bundle, cfg->tls_win_cert)))
397*be771a7bSCy Schubert 		fatal_exit("could not set up connect SSL_CTX");
398*be771a7bSCy Schubert 
399*be771a7bSCy Schubert 	/* open ports */
400*be771a7bSCy Schubert 	/* keep reporting that we are busy starting */
401*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2200);
402*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - open ports");
403*be771a7bSCy Schubert 	if(!daemon_open_shared_ports(daemon)) return 0;
404*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - ports opened");
405*be771a7bSCy Schubert 	if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2000);
406*be771a7bSCy Schubert 
407*be771a7bSCy Schubert 	*d = daemon;
408*be771a7bSCy Schubert 	*c = cfg;
409*be771a7bSCy Schubert 	return 1;
410*be771a7bSCy Schubert }
411*be771a7bSCy Schubert 
412*be771a7bSCy Schubert /**
413*be771a7bSCy Schubert  * Deinit the service
414*be771a7bSCy Schubert  */
415*be771a7bSCy Schubert static void
416*be771a7bSCy Schubert service_deinit(struct daemon* daemon, struct config_file* cfg)
417*be771a7bSCy Schubert {
418*be771a7bSCy Schubert 	daemon_cleanup(daemon);
419*be771a7bSCy Schubert 	config_delete(cfg);
420*be771a7bSCy Schubert 	daemon_delete(daemon);
421*be771a7bSCy Schubert }
422*be771a7bSCy Schubert 
423*be771a7bSCy Schubert #ifdef DOXYGEN
424*be771a7bSCy Schubert #define ATTR_UNUSED(x) x
425*be771a7bSCy Schubert #endif
426*be771a7bSCy Schubert /**
427*be771a7bSCy Schubert  * The main function for the service.
428*be771a7bSCy Schubert  * Called by the services API when starting unbound on windows in background.
429*be771a7bSCy Schubert  * Arguments could have been present in the string 'path'.
430*be771a7bSCy Schubert  * @param argc: nr args
431*be771a7bSCy Schubert  * @param argv: arg text.
432*be771a7bSCy Schubert  */
433*be771a7bSCy Schubert static void
434*be771a7bSCy Schubert service_main(DWORD ATTR_UNUSED(argc), LPTSTR* ATTR_UNUSED(argv))
435*be771a7bSCy Schubert {
436*be771a7bSCy Schubert 	struct config_file* cfg = NULL;
437*be771a7bSCy Schubert 	struct daemon* daemon = NULL;
438*be771a7bSCy Schubert 
439*be771a7bSCy Schubert 	service_status_handle = RegisterServiceCtrlHandler(SERVICE_NAME,
440*be771a7bSCy Schubert 		(LPHANDLER_FUNCTION)hdlr);
441*be771a7bSCy Schubert 	if(!service_status_handle) {
442*be771a7bSCy Schubert 		reportev("Could not RegisterServiceCtrlHandler");
443*be771a7bSCy Schubert 		return;
444*be771a7bSCy Schubert 	}
445*be771a7bSCy Schubert 
446*be771a7bSCy Schubert 	service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
447*be771a7bSCy Schubert 	service_status.dwServiceSpecificExitCode = 0;
448*be771a7bSCy Schubert 
449*be771a7bSCy Schubert 	/* see if we have root anchor update enabled */
450*be771a7bSCy Schubert 	call_root_update();
451*be771a7bSCy Schubert 
452*be771a7bSCy Schubert 	/* we are now starting up */
453*be771a7bSCy Schubert 	report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
454*be771a7bSCy Schubert 	if(!service_init(0, &daemon, &cfg)) {
455*be771a7bSCy Schubert 		reportev("Could not service_init");
456*be771a7bSCy Schubert 		report_status(SERVICE_STOPPED, NO_ERROR, 0);
457*be771a7bSCy Schubert 		return;
458*be771a7bSCy Schubert 	}
459*be771a7bSCy Schubert 
460*be771a7bSCy Schubert 	/* event that gets signalled when we want to quit; it
461*be771a7bSCy Schubert 	 * should get registered in the worker-0 waiting loop. */
462*be771a7bSCy Schubert 	service_stop_event = WSACreateEvent();
463*be771a7bSCy Schubert 	if(service_stop_event == WSA_INVALID_EVENT) {
464*be771a7bSCy Schubert 		log_err("WSACreateEvent: %s", wsa_strerror(WSAGetLastError()));
465*be771a7bSCy Schubert 		reportev("Could not WSACreateEvent");
466*be771a7bSCy Schubert 		report_status(SERVICE_STOPPED, NO_ERROR, 0);
467*be771a7bSCy Schubert 		return;
468*be771a7bSCy Schubert 	}
469*be771a7bSCy Schubert 	if(!WSAResetEvent(service_stop_event)) {
470*be771a7bSCy Schubert 		log_err("WSAResetEvent: %s", wsa_strerror(WSAGetLastError()));
471*be771a7bSCy Schubert 	}
472*be771a7bSCy Schubert 
473*be771a7bSCy Schubert 	/* SetServiceStatus SERVICE_RUNNING;*/
474*be771a7bSCy Schubert 	report_status(SERVICE_RUNNING, NO_ERROR, 0);
475*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - init complete");
476*be771a7bSCy Schubert 
477*be771a7bSCy Schubert 	/* daemon performs work */
478*be771a7bSCy Schubert 	while(!service_stop_shutdown) {
479*be771a7bSCy Schubert 		daemon_fork(daemon);
480*be771a7bSCy Schubert 		if(!service_stop_shutdown) {
481*be771a7bSCy Schubert 			daemon_cleanup(daemon);
482*be771a7bSCy Schubert 			config_delete(cfg); cfg=NULL;
483*be771a7bSCy Schubert 			if(!service_init(1, &daemon, &cfg)) {
484*be771a7bSCy Schubert 				reportev("Could not service_init");
485*be771a7bSCy Schubert 				report_status(SERVICE_STOPPED, NO_ERROR, 0);
486*be771a7bSCy Schubert 				return;
487*be771a7bSCy Schubert 			}
488*be771a7bSCy Schubert 		}
489*be771a7bSCy Schubert 	}
490*be771a7bSCy Schubert 
491*be771a7bSCy Schubert 	/* exit */
492*be771a7bSCy Schubert 	verbose(VERB_ALGO, "winservice - cleanup.");
493*be771a7bSCy Schubert 	report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
494*be771a7bSCy Schubert 	if(service_stop_event) (void)WSACloseEvent(service_stop_event);
495*be771a7bSCy Schubert 	service_deinit(daemon, cfg);
496*be771a7bSCy Schubert 	free(service_cfgfile);
497*be771a7bSCy Schubert 	verbose(VERB_QUERY, "winservice - full stop");
498*be771a7bSCy Schubert 	report_status(SERVICE_STOPPED, NO_ERROR, 0);
499*be771a7bSCy Schubert }
500*be771a7bSCy Schubert 
501*be771a7bSCy Schubert /** start the service */
502*be771a7bSCy Schubert static void
503*be771a7bSCy Schubert service_start(const char* cfgfile, int v, int c)
504*be771a7bSCy Schubert {
505*be771a7bSCy Schubert 	SERVICE_TABLE_ENTRY myservices[2] = {
506*be771a7bSCy Schubert 		{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main},
507*be771a7bSCy Schubert 		{NULL, NULL} };
508*be771a7bSCy Schubert 	verbosity=v;
509*be771a7bSCy Schubert 	if(verbosity >= VERB_QUERY) {
510*be771a7bSCy Schubert 		/* log to file about start sequence */
511*be771a7bSCy Schubert 		fclose(fopen("C:\\unbound.log", "w"));
512*be771a7bSCy Schubert 		log_init("C:\\unbound.log", 0, 0);
513*be771a7bSCy Schubert 		verbose(VERB_QUERY, "open logfile");
514*be771a7bSCy Schubert 	} else log_init(0, 1, 0); /* otherwise, use Application log */
515*be771a7bSCy Schubert 	if(c) {
516*be771a7bSCy Schubert 		service_cfgfile = strdup(cfgfile);
517*be771a7bSCy Schubert 		if(!service_cfgfile) fatal_exit("out of memory");
518*be771a7bSCy Schubert 	} else 	service_cfgfile = NULL;
519*be771a7bSCy Schubert 	service_cmdline_verbose = v;
520*be771a7bSCy Schubert 	/* this call returns when service has stopped. */
521*be771a7bSCy Schubert 	if(!StartServiceCtrlDispatcher(myservices)) {
522*be771a7bSCy Schubert 		reportev("Could not StartServiceCtrlDispatcher");
523*be771a7bSCy Schubert 	}
524*be771a7bSCy Schubert }
525*be771a7bSCy Schubert 
526*be771a7bSCy Schubert void
527*be771a7bSCy Schubert wsvc_command_option(const char* wopt, const char* cfgfile, int v, int c)
528*be771a7bSCy Schubert {
529*be771a7bSCy Schubert 	if(strcmp(wopt, "install") == 0)
530*be771a7bSCy Schubert 		wsvc_install(stdout, NULL);
531*be771a7bSCy Schubert 	else if(strcmp(wopt, "remove") == 0)
532*be771a7bSCy Schubert 		wsvc_remove(stdout);
533*be771a7bSCy Schubert 	else if(strcmp(wopt, "service") == 0)
534*be771a7bSCy Schubert 		service_start(cfgfile, v, c);
535*be771a7bSCy Schubert 	else if(strcmp(wopt, "start") == 0)
536*be771a7bSCy Schubert 		wsvc_rc_start(stdout);
537*be771a7bSCy Schubert 	else if(strcmp(wopt, "stop") == 0)
538*be771a7bSCy Schubert 		wsvc_rc_stop(stdout);
539*be771a7bSCy Schubert 	else fatal_exit("unknown option: %s", wopt);
540*be771a7bSCy Schubert 	exit(0);
541*be771a7bSCy Schubert }
542*be771a7bSCy Schubert 
543*be771a7bSCy Schubert void
544*be771a7bSCy Schubert worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void* arg)
545*be771a7bSCy Schubert {
546*be771a7bSCy Schubert         struct worker* worker = (struct worker*)arg;
547*be771a7bSCy Schubert         verbose(VERB_QUERY, "caught stop signal (wsaevent)");
548*be771a7bSCy Schubert         worker->need_to_exit = 1;
549*be771a7bSCy Schubert         comm_base_exit(worker->base);
550*be771a7bSCy Schubert }
551*be771a7bSCy Schubert 
552*be771a7bSCy Schubert /** wait for cron process to finish */
553*be771a7bSCy Schubert static void
554*be771a7bSCy Schubert waitforit(PROCESS_INFORMATION* pinfo)
555*be771a7bSCy Schubert {
556*be771a7bSCy Schubert 	DWORD ret = WaitForSingleObject(pinfo->hProcess, INFINITE);
557*be771a7bSCy Schubert 	verbose(VERB_ALGO, "cronaction done");
558*be771a7bSCy Schubert 	if(ret != WAIT_OBJECT_0) {
559*be771a7bSCy Schubert 		return; /* did not end successfully */
560*be771a7bSCy Schubert 	}
561*be771a7bSCy Schubert 	if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
562*be771a7bSCy Schubert 		log_err("GetExitCodeProcess failed");
563*be771a7bSCy Schubert 		return;
564*be771a7bSCy Schubert 	}
565*be771a7bSCy Schubert 	verbose(VERB_ALGO, "exit code is %d", (int)ret);
566*be771a7bSCy Schubert 	if(ret != 1) {
567*be771a7bSCy Schubert 		if(!WSASetEvent(service_stop_event))
568*be771a7bSCy Schubert 			log_err("Could not WSASetEvent: %s",
569*be771a7bSCy Schubert 			wsa_strerror(WSAGetLastError()));
570*be771a7bSCy Schubert 	}
571*be771a7bSCy Schubert }
572*be771a7bSCy Schubert 
573*be771a7bSCy Schubert /** Do the cron action and wait for result exit value */
574*be771a7bSCy Schubert static void*
575*be771a7bSCy Schubert win_do_cron(void* ATTR_UNUSED(arg))
576*be771a7bSCy Schubert {
577*be771a7bSCy Schubert 	int mynum=65;
578*be771a7bSCy Schubert 	char* cronaction;
579*be771a7bSCy Schubert 	log_thread_set(&mynum);
580*be771a7bSCy Schubert  	cronaction = lookup_reg_str("Software\\Unbound", "CronAction");
581*be771a7bSCy Schubert 	if(cronaction && strlen(cronaction)>0) {
582*be771a7bSCy Schubert 		STARTUPINFO sinfo;
583*be771a7bSCy Schubert 		PROCESS_INFORMATION pinfo;
584*be771a7bSCy Schubert 		memset(&pinfo, 0, sizeof(pinfo));
585*be771a7bSCy Schubert 		memset(&sinfo, 0, sizeof(sinfo));
586*be771a7bSCy Schubert 		sinfo.cb = sizeof(sinfo);
587*be771a7bSCy Schubert 		verbose(VERB_ALGO, "cronaction: %s", cronaction);
588*be771a7bSCy Schubert 		if(!CreateProcess(NULL, cronaction, NULL, NULL, 0,
589*be771a7bSCy Schubert 			CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
590*be771a7bSCy Schubert 			log_err("CreateProcess error");
591*be771a7bSCy Schubert 		else {
592*be771a7bSCy Schubert 			waitforit(&pinfo);
593*be771a7bSCy Schubert 			CloseHandle(pinfo.hProcess);
594*be771a7bSCy Schubert 			CloseHandle(pinfo.hThread);
595*be771a7bSCy Schubert 		}
596*be771a7bSCy Schubert 	}
597*be771a7bSCy Schubert 	free(cronaction);
598*be771a7bSCy Schubert 	/* stop self */
599*be771a7bSCy Schubert 	CloseHandle(cron_thread);
600*be771a7bSCy Schubert 	cron_thread = NULL;
601*be771a7bSCy Schubert 	return NULL;
602*be771a7bSCy Schubert }
603*be771a7bSCy Schubert 
604*be771a7bSCy Schubert /** Set the timer for cron for the next wake up */
605*be771a7bSCy Schubert static void
606*be771a7bSCy Schubert set_cron_timer(void)
607*be771a7bSCy Schubert {
608*be771a7bSCy Schubert 	struct timeval tv;
609*be771a7bSCy Schubert 	int crontime;
610*be771a7bSCy Schubert 	if(cron_was_quick == 0) {
611*be771a7bSCy Schubert 		cron_was_quick = 1;
612*be771a7bSCy Schubert 		crontime = 3600; /* first update some time after boot */
613*be771a7bSCy Schubert 	} else {
614*be771a7bSCy Schubert 		crontime = lookup_reg_int("Software\\Unbound", "CronTime");
615*be771a7bSCy Schubert 		if(crontime == 0) crontime = 60*60*24; /* 24 hours */
616*be771a7bSCy Schubert 	}
617*be771a7bSCy Schubert 	memset(&tv, 0, sizeof(tv));
618*be771a7bSCy Schubert 	tv.tv_sec = (time_t)crontime;
619*be771a7bSCy Schubert 	comm_timer_set(service_cron, &tv);
620*be771a7bSCy Schubert }
621*be771a7bSCy Schubert 
622*be771a7bSCy Schubert void
623*be771a7bSCy Schubert wsvc_cron_cb(void* arg)
624*be771a7bSCy Schubert {
625*be771a7bSCy Schubert 	struct worker* worker = (struct worker*)arg;
626*be771a7bSCy Schubert 	/* perform cronned operation */
627*be771a7bSCy Schubert 	verbose(VERB_ALGO, "cron timer callback");
628*be771a7bSCy Schubert 	if(cron_thread == NULL) {
629*be771a7bSCy Schubert 		/* create new thread to do it */
630*be771a7bSCy Schubert 		ub_thread_create(&cron_thread, win_do_cron, worker);
631*be771a7bSCy Schubert 	}
632*be771a7bSCy Schubert 	/* reschedule */
633*be771a7bSCy Schubert 	set_cron_timer();
634*be771a7bSCy Schubert }
635*be771a7bSCy Schubert 
636*be771a7bSCy Schubert void wsvc_setup_worker(struct worker* worker)
637*be771a7bSCy Schubert {
638*be771a7bSCy Schubert 	/* if not started with -w service, do nothing */
639*be771a7bSCy Schubert 	if(!service_stop_event)
640*be771a7bSCy Schubert 		return;
641*be771a7bSCy Schubert 	if(!(service_stop_ev = ub_winsock_register_wsaevent(
642*be771a7bSCy Schubert 		comm_base_internal(worker->base), service_stop_event,
643*be771a7bSCy Schubert 		&worker_win_stop_cb, worker))) {
644*be771a7bSCy Schubert 		fatal_exit("could not register wsaevent");
645*be771a7bSCy Schubert 		return;
646*be771a7bSCy Schubert 	}
647*be771a7bSCy Schubert 	if(!service_cron) {
648*be771a7bSCy Schubert 		service_cron = comm_timer_create(worker->base,
649*be771a7bSCy Schubert 			wsvc_cron_cb, worker);
650*be771a7bSCy Schubert 		if(!service_cron)
651*be771a7bSCy Schubert 			fatal_exit("could not create cron timer");
652*be771a7bSCy Schubert 		set_cron_timer();
653*be771a7bSCy Schubert 	}
654*be771a7bSCy Schubert }
655*be771a7bSCy Schubert 
656*be771a7bSCy Schubert void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
657*be771a7bSCy Schubert {
658*be771a7bSCy Schubert 	comm_timer_delete(service_cron);
659*be771a7bSCy Schubert 	service_cron = NULL;
660*be771a7bSCy Schubert }
661