--- /dev/null
+/*
+ * winrc/anchor-update.c - windows trust anchor update util
+ *
+ * Copyright (c) 2009, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file is made because contrib/update-anchor.sh does not work on
+ * windows (no shell).
+ */
+#include "config.h"
+#include "libunbound/unbound.h"
+
+/** usage */
+static void
+usage(void)
+{
+ printf("usage: { name-of-domain filename }+ \n");
+ printf("exit codes: 0 anchors updated, 1 no changes, 2 errors.\n");
+ exit(1);
+}
+
+/** fatal exit */
+static void fatal(const char* str)
+{
+ printf("fatal error: %s\n", str);
+ exit(2);
+}
+
+/** lookup data */
+static struct ub_result*
+do_lookup(struct ub_ctx* ctx, char* domain)
+{
+ struct ub_result* result = NULL;
+ int r;
+ r = ub_resolve(ctx, domain, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN,
+ &result);
+ if(r) {
+ printf("failed to lookup %s\n", ub_strerror(r));
+ fatal("ub_resolve failed");
+ }
+ if(!result->havedata && (result->rcode == LDNS_RCODE_SERVFAIL ||
+ result->rcode == LDNS_RCODE_REFUSED))
+ return NULL; /* probably no internet connection */
+ if(!result->havedata) fatal("result has no data");
+ if(!result->secure) fatal("result is not secure");
+ return result;
+}
+
+/** get answer into ldns rr list */
+static ldns_rr_list*
+result2answer(struct ub_result* result)
+{
+ ldns_pkt* p = NULL;
+ ldns_rr_list* a;
+ if(ldns_wire2pkt(&p, result->answer_packet, result->answer_len)
+ != LDNS_STATUS_OK)
+ return NULL;
+ a = ldns_pkt_answer(p);
+ ldns_pkt_set_answer(p, NULL);
+ ldns_pkt_free(p);
+ return a;
+}
+
+/** print result to file */
+static void
+do_print(struct ub_result* result, char* file)
+{
+ FILE* out;
+ ldns_rr_list* list = result2answer(result);
+ if(!list) fatal("result2answer failed");
+
+ out = fopen(file, "w");
+ if(!out) {
+ perror(file);
+ fatal("fopen failed");
+ }
+ ldns_rr_list_print(out, list);
+ fclose(out);
+ ldns_rr_list_deep_free(list);
+}
+
+/** update domain to file */
+static int
+do_update(char* domain, char* file)
+{
+ struct ub_ctx* ctx;
+ struct ub_result* result;
+ int r;
+ printf("updating %s to %s\n", domain, file);
+ ctx = ub_ctx_create();
+ if(!ctx) fatal("ub_ctx_create failed");
+
+ if((r=ub_ctx_add_ta_file(ctx, file))) {
+ printf("%s\n", ub_strerror(r));
+ fatal("ub_ctx_add_ta_file failed");
+ }
+
+ if(!(result=do_lookup(ctx, domain))) {
+ ub_ctx_delete(ctx);
+ return 1;
+ }
+ ub_ctx_delete(ctx);
+ do_print(result, file);
+ ub_resolve_free(result);
+ return 0;
+}
+
+/** anchor update main */
+int main(int argc, char** argv)
+{
+ int retcode = 1;
+ if(argc == 1) {
+ usage();
+ }
+ argc--;
+ argv++;
+ while(argc > 0) {
+ int r = do_update(argv[0], argv[1]);
+ if(r == 0) retcode = 0;
+
+ /* next */
+ argc-=2;
+ argv+=2;
+ }
+ return retcode;
+}
#include "util/winsock_event.h"
/** global service status */
-SERVICE_STATUS service_status;
+static SERVICE_STATUS service_status;
/** global service status handle */
-SERVICE_STATUS_HANDLE service_status_handle;
+static SERVICE_STATUS_HANDLE service_status_handle;
/** global service stop event */
-WSAEVENT service_stop_event = NULL;
+static WSAEVENT service_stop_event = NULL;
/** event struct for stop callbacks */
-struct event service_stop_ev;
+static struct event service_stop_ev;
+/** if stop even means shutdown or restart */
+static int service_stop_shutdown = 0;
/** config file to open. global communication to service_main() */
-char* service_cfgfile = CONFIGFILE;
+static char* service_cfgfile = CONFIGFILE;
/** commandline verbosity. global communication to service_main() */
-int service_cmdline_verbose = 0;
+static int service_cmdline_verbose = 0;
+/** the cron callback */
+static struct comm_timer* service_cron = NULL;
+/** the cron thread */
+static ub_thread_t cron_thread = NULL;
+/** if cron has already done its quick check */
+static int cron_was_quick = 0;
/**
* Report current service status to service control manager
{
if(ctrl == SERVICE_CONTROL_STOP) {
report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
+ service_stop_shutdown = 1;
/* send signal to stop */
if(!WSASetEvent(service_stop_event))
log_err("Could not WSASetEvent: %s",
return result;
}
+/**
+ * Obtain registry integer (if it exists).
+ * @param key: key string
+ * @param name: name of value to fetch.
+ * @return integer value (if it exists), or 0 on error.
+ */
+static int
+lookup_reg_int(const char* key, const char* name)
+{
+ HKEY hk = NULL;
+ DWORD type = 0;
+ BYTE buf[1024];
+ DWORD len = (DWORD)sizeof(buf);
+ LONG ret;
+ int result = 0;
+ ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
+ if(ret == ERROR_FILE_NOT_FOUND)
+ return 0; /* key does not exist */
+ else if(ret != ERROR_SUCCESS) {
+ reportev("RegOpenKeyEx failed");
+ return 0;
+ }
+ ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
+ if(RegCloseKey(hk))
+ reportev("RegCloseKey");
+ if(ret == ERROR_FILE_NOT_FOUND)
+ return 0; /* name does not exist */
+ else if(ret != ERROR_SUCCESS) {
+ reportev("RegQueryValueEx failed");
+ return 0;
+ }
+ if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
+ buf[sizeof(buf)-1] = 0;
+ buf[sizeof(buf)-2] = 0; /* for multi_sz */
+ result = atoi(buf);
+ } else if(type == REG_DWORD) {
+ result = *(DWORD*)buf;
+ }
+ return result;
+}
+
/**
* Init service. Keeps calling status pending to tell service control
* manager that this process is not hanging.
+ * @param r: restart, true on restart
* @param d: daemon returned here.
* @param c: config file returned here.
* @return false if failed.
*/
static int
-service_init(struct daemon** d, struct config_file** c)
+service_init(int r, struct daemon** d, struct config_file** c)
{
struct config_file* cfg = NULL;
struct daemon* daemon = NULL;
}
/* create daemon */
- daemon = daemon_init();
+ if(r) daemon = *d;
+ else daemon = daemon_init();
if(!daemon) return 0;
- report_status(SERVICE_START_PENDING, NO_ERROR, 2800);
+ if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2800);
/* read config */
cfg = config_create();
}
log_warn("could not open config file, using defaults");
}
- report_status(SERVICE_START_PENDING, NO_ERROR, 2600);
+ if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2600);
verbose(VERB_QUERY, "winservice - apply settings");
/* apply settings and init */
verbose(VERB_QUERY, "chdir to %s", cfg->directory);
}
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
- report_status(SERVICE_START_PENDING, NO_ERROR, 2400);
+ if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2400);
verbose(VERB_QUERY, "winservice - apply cfg");
daemon_apply_cfg(daemon, cfg);
/* open ports */
/* keep reporting that we are busy starting */
- report_status(SERVICE_START_PENDING, NO_ERROR, 2200);
+ if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2200);
verbose(VERB_QUERY, "winservice - open ports");
if(!daemon_open_shared_ports(daemon)) return 0;
verbose(VERB_QUERY, "winservice - ports opened");
- report_status(SERVICE_START_PENDING, NO_ERROR, 2000);
+ if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2000);
*d = daemon;
*c = cfg;
return 1;
}
+/**
+ * Deinit the service
+ */
+static void
+service_deinit(struct daemon* daemon, struct config_file* cfg)
+{
+ daemon_cleanup(daemon);
+ config_delete(cfg);
+ daemon_delete(daemon);
+}
+
/**
* The main function for the service.
* Called by the services API when starting unbound on windows in background.
/* we are now starting up */
report_status(SERVICE_START_PENDING, NO_ERROR, 3000);
- if(!service_init(&daemon, &cfg)) {
+ if(!service_init(0, &daemon, &cfg)) {
reportev("Could not service_init");
report_status(SERVICE_STOPPED, NO_ERROR, 0);
return;
verbose(VERB_QUERY, "winservice - init complete");
/* daemon performs work */
- daemon_fork(daemon);
+ while(!service_stop_shutdown) {
+ daemon_fork(daemon);
+ if(!service_stop_shutdown) {
+ daemon_cleanup(daemon);
+ config_delete(cfg); cfg=NULL;
+ if(!service_init(1, &daemon, &cfg)) {
+ reportev("Could not service_init");
+ report_status(SERVICE_STOPPED, NO_ERROR, 0);
+ return;
+ }
+ }
+ }
/* exit */
verbose(VERB_ALGO, "winservice - cleanup.");
report_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
- daemon_cleanup(daemon);
- config_delete(cfg);
- daemon_delete(daemon);
- (void)WSACloseEvent(service_stop_event);
+ service_deinit(daemon, cfg);
free(service_cfgfile);
+ if(service_stop_event) (void)WSACloseEvent(service_stop_event);
verbose(VERB_QUERY, "winservice - full stop");
report_status(SERVICE_STOPPED, NO_ERROR, 0);
}
SERVICE_TABLE_ENTRY myservices[2] = {
{SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main},
{NULL, NULL} };
+ v=4; /* DEBUG */
verbosity=v;
if(verbosity >= VERB_QUERY) {
/* log to file about start sequence */
comm_base_exit(worker->base);
}
+/** wait for cron process to finish */
+static void
+waitforit(PROCESS_INFORMATION* pinfo)
+{
+ DWORD ret = WaitForSingleObject(pinfo->hProcess, INFINITE);
+ verbose(VERB_ALGO, "cronaction done");
+ if(ret != WAIT_OBJECT_0) {
+ return; /* did not end successfully */
+ }
+ if(!GetExitCodeProcess(pinfo->hProcess, &ret)) {
+ log_err("GetExitCodeProcess failed");
+ return;
+ }
+ verbose(VERB_ALGO, "exit code is %d", (int)ret);
+ if(ret != 1) {
+ if(!WSASetEvent(service_stop_event))
+ log_err("Could not WSASetEvent: %s",
+ wsa_strerror(WSAGetLastError()));
+ }
+}
+
+/** Do the cron action and wait for result exit value */
+static void*
+win_do_cron(void* ATTR_UNUSED(arg))
+{
+ int mynum=65;
+ char* cronaction;
+ log_thread_set(&mynum);
+ cronaction = lookup_reg_str("Software\\Unbound", "CronAction");
+ if(cronaction) {
+ STARTUPINFO sinfo;
+ PROCESS_INFORMATION pinfo;
+ memset(&pinfo, 0, sizeof(pinfo));
+ memset(&sinfo, 0, sizeof(sinfo));
+ sinfo.cb = sizeof(sinfo);
+ verbose(VERB_ALGO, "cronaction: %s", cronaction);
+ if(!CreateProcess(NULL, cronaction, NULL, NULL, 0,
+ CREATE_NO_WINDOW, NULL, NULL, &sinfo, &pinfo))
+ log_err("CreateProcess error");
+ else {
+ waitforit(&pinfo);
+ CloseHandle(pinfo.hProcess);
+ CloseHandle(pinfo.hThread);
+ }
+ free(cronaction);
+ }
+ /* stop self */
+ CloseHandle(cron_thread);
+ cron_thread = NULL;
+ return NULL;
+}
+
+static void
+set_cron_timer()
+{
+ struct timeval tv;
+ int crontime;
+ if(cron_was_quick == 0) {
+ cron_was_quick = 1;
+ crontime = 10; /* first update 10 seconds after boot */
+ } else {
+ crontime = lookup_reg_int("Software\\Unbound", "CronTime");
+ if(crontime == 0) crontime = 60*60*24; /* 24 hours */
+ }
+ crontime = 10; /* DEBUG */
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = (time_t)crontime;
+ comm_timer_set(service_cron, &tv);
+}
+
+void
+wsvc_cron_cb(void* arg)
+{
+ struct worker* worker = (struct worker*)arg;
+ /* perform cronned operation */
+ verbose(VERB_ALGO, "cron timer callback");
+ if(cron_thread == NULL) {
+ /* create new thread to do it */
+ ub_thread_create(&cron_thread, win_do_cron, worker);
+ }
+ /* reschedule */
+ set_cron_timer();
+}
+
void wsvc_setup_worker(struct worker* worker)
{
/* if not started with -w service, do nothing */
fatal_exit("could not register wsaevent");
return;
}
+ if(!service_cron) {
+ service_cron = comm_timer_create(worker->base,
+ wsvc_cron_cb, worker);
+ if(!service_cron)
+ fatal_exit("could not create cron timer");
+ set_cron_timer();
+ }
}
+void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
+{
+ comm_timer_delete(service_cron);
+ service_cron = NULL;
+}