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