diff options
Diffstat (limited to '')
-rw-r--r-- | bin/named/win32/os.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c new file mode 100644 index 0000000..5503d95 --- /dev/null +++ b/bin/named/win32/os.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <io.h> +#include <process.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <syslog.h> + +#include <isc/ntpaths.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/string.h> +#include <isc/util.h> +#include <isc/win32os.h> + +#include <named/globals.h> +#include <named/log.h> +#include <named/main.h> +#include <named/ntservice.h> +#include <named/os.h> + +static char *lockfile = NULL; +static char *pidfile = NULL; +static int devnullfd = -1; +static int lockfilefd = -1; + +static BOOL Initialized = FALSE; + +static char *version_error = "named requires Windows 2000 Service Pack 2 or " + "later to run correctly"; + +void +named_paths_init(void) { + if (!Initialized) { + isc_ntpaths_init(); + } + + named_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH); + named_g_defaultpidfile = isc_ntpaths_get(NAMED_PID_PATH); + named_g_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH); + named_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH); + named_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH); + named_g_defaultbindkeys = isc_ntpaths_get(BIND_KEYS_PATH); + + Initialized = TRUE; +} + +/* + * Due to Knowledge base article Q263823 we need to make sure that + * Windows 2000 systems have Service Pack 2 or later installed and + * warn when it isn't. + */ +static void +version_check(const char *progname) { + if ((isc_win32os_versioncheck(4, 0, 0, 0) >= 0) && + (isc_win32os_versioncheck(5, 0, 0, 0) < 0)) + { + return; /* No problem with Version 4.0 */ + } + if (isc_win32os_versioncheck(5, 0, 2, 0) < 0) { + if (ntservice_isservice()) { + NTReportError(progname, version_error); + } else { + fprintf(stderr, "%s\n", version_error); + } + } +} + +static void +setup_syslog(const char *progname) { + int options; + + options = LOG_PID; +#ifdef LOG_NDELAY + options |= LOG_NDELAY; +#endif /* ifdef LOG_NDELAY */ + + openlog(progname, options, LOG_DAEMON); +} + +void +named_os_init(const char *progname) { + named_paths_init(); + setup_syslog(progname); + /* + * XXXMPA. We may need to split ntservice_init() in two and + * just mark as running in named_os_started(). If we do that + * this is where the first part of ntservice_init() should be + * called from. + * + * XXX970 Remove comment if no problems by 9.7.0. + * + * ntservice_init(); + */ + version_check(progname); + /* + * If running in a Cygwin environment, clear the SEM_NOGPFAULTERRORBOX + * bit in the process error mode to prevent Cygwin from concealing + * non-abort() crashes, giving Windows Error Reporting a chance to + * handle such crashes. This is done to ensure all crashes triggered + * by system tests can be detected. + */ + if (getenv("CYGWIN") != NULL) { + SetErrorMode(GetErrorMode() & ~SEM_NOGPFAULTERRORBOX); + } +} + +void +named_os_daemonize(void) { + /* + * Try to set stdin, stdout, and stderr to /dev/null, but press + * on even if it fails. + */ + if (devnullfd != -1) { + if (devnullfd != _fileno(stdin)) { + close(_fileno(stdin)); + (void)_dup2(devnullfd, _fileno(stdin)); + } + if (devnullfd != _fileno(stdout)) { + close(_fileno(stdout)); + (void)_dup2(devnullfd, _fileno(stdout)); + } + if (devnullfd != _fileno(stderr)) { + close(_fileno(stderr)); + (void)_dup2(devnullfd, _fileno(stderr)); + } + } +} + +void +named_os_opendevnull(void) { + devnullfd = open("NUL", O_RDWR, 0); +} + +void +named_os_closedevnull(void) { + if (devnullfd != _fileno(stdin) && devnullfd != _fileno(stdout) && + devnullfd != _fileno(stderr)) + { + close(devnullfd); + devnullfd = -1; + } +} + +void +named_os_chroot(const char *root) { + if (root != NULL) { + named_main_earlyfatal("chroot(): isn't supported by Win32 API"); + } +} + +void +named_os_inituserinfo(const char *username) {} + +void +named_os_changeuser(void) {} + +unsigned int +ns_os_uid(void) { + return (0); +} + +void +named_os_adjustnofile(void) {} + +void +named_os_minprivs(void) {} + +static int +safe_open(const char *filename, int mode, bool append) { + int fd; + struct stat sb; + + if (stat(filename, &sb) == -1) { + if (errno != ENOENT) { + return (-1); + } + } else if ((sb.st_mode & S_IFREG) == 0) { + return (-1); + } + + if (append) { + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, mode); + } else { + (void)unlink(filename); + fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, mode); + } + return (fd); +} + +static void +cleanup_pidfile(void) { + if (pidfile != NULL) { + (void)unlink(pidfile); + free(pidfile); + } + pidfile = NULL; +} + +static void +cleanup_lockfile(void) { + if (lockfilefd != -1) { + close(lockfilefd); + lockfilefd = -1; + } + + if (lockfile != NULL) { + int n = unlink(lockfile); + if (n == -1 && errno != ENOENT) { + named_main_earlywarning("unlink '%s': failed", + lockfile); + } + free(lockfile); + lockfile = NULL; + } +} + +FILE * +named_os_openfile(const char *filename, int mode, bool switch_user) { + char strbuf[ISC_STRERRORSIZE]; + FILE *fp; + int fd; + + UNUSED(switch_user); + fd = safe_open(filename, mode, false); + if (fd < 0) { + strerror_s(strbuf, sizeof(strbuf), errno); + named_main_earlywarning("could not open file '%s': %s", + filename, strbuf); + return (NULL); + } + + fp = fdopen(fd, "w"); + if (fp == NULL) { + strerror_s(strbuf, sizeof(strbuf), errno); + named_main_earlywarning("could not fdopen() file '%s': %s", + filename, strbuf); + close(fd); + } + + return (fp); +} + +void +named_os_writepidfile(const char *filename, bool first_time) { + FILE *pidlockfile; + pid_t pid; + char strbuf[ISC_STRERRORSIZE]; + void (*report)(const char *, ...); + + /* + * The caller must ensure any required synchronization. + */ + + report = first_time ? named_main_earlyfatal : named_main_earlywarning; + + cleanup_pidfile(); + + if (filename == NULL) { + return; + } + + pidfile = strdup(filename); + if (pidfile == NULL) { + strerror_s(strbuf, sizeof(strbuf), errno); + (*report)("couldn't strdup() '%s': %s", filename, strbuf); + return; + } + + pidlockfile = named_os_openfile( + filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, false); + if (pidlockfile == NULL) { + free(pidfile); + pidfile = NULL; + return; + } + + pid = getpid(); + + if (fprintf(pidlockfile, "%ld\n", (long)pid) < 0) { + (*report)("fprintf() to pid file '%s' failed", filename); + (void)fclose(pidlockfile); + cleanup_pidfile(); + return; + } + if (fflush(pidlockfile) == EOF) { + (*report)("fflush() to pid file '%s' failed", filename); + (void)fclose(pidlockfile); + cleanup_pidfile(); + return; + } + (void)fclose(pidlockfile); +} + +bool +named_os_issingleton(const char *filename) { + char strbuf[ISC_STRERRORSIZE]; + OVERLAPPED o; + + if (lockfilefd != -1) { + return (true); + } + + if (strcasecmp(filename, "none") == 0) { + return (true); + } + + lockfile = strdup(filename); + if (lockfile == NULL) { + strerror_s(strbuf, sizeof(strbuf), errno); + named_main_earlyfatal("couldn't allocate memory for '%s': %s", + filename, strbuf); + } + + /* + * named_os_openfile() uses safeopen() which removes any existing + * files. We can't use that here. + */ + lockfilefd = open(filename, O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (lockfilefd == -1) { + cleanup_lockfile(); + return (false); + } + + memset(&o, 0, sizeof(o)); + /* Expect ERROR_LOCK_VIOLATION if already locked */ + if (!LockFileEx((HANDLE)_get_osfhandle(lockfilefd), + LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, + 0, 1, &o)) + { + cleanup_lockfile(); + return (false); + } + + return (true); +} + +void +named_os_shutdown(void) { + closelog(); + cleanup_pidfile(); + + if (lockfilefd != -1) { + (void)UnlockFile((HANDLE)_get_osfhandle(lockfilefd), 0, 0, 0, + 1); + } + cleanup_lockfile(); + + ntservice_shutdown(); /* This MUST be the last thing done */ +} + +isc_result_t +named_os_gethostname(char *buf, size_t len) { + int n; + + n = gethostname(buf, (int)len); + return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE); +} + +void +named_os_shutdownmsg(char *command, isc_buffer_t *text) { + UNUSED(command); + UNUSED(text); +} + +void +named_os_tzset(void) { +#ifdef HAVE_TZSET + tzset(); +#endif /* ifdef HAVE_TZSET */ +} + +void +named_os_started(void) { + ntservice_init(); +} + +static char unamebuf[BUFSIZ]; +static const char *unamep = NULL; + +static void +getuname(void) { + DWORD fvilen; + char *fvi; + VS_FIXEDFILEINFO *ffi; + UINT ffilen; + SYSTEM_INFO sysinfo; + char *arch; + + fvi = NULL; + fvilen = GetFileVersionInfoSize("kernel32.dll", 0); + if (fvilen == 0) { + goto err; + } + fvi = (char *)malloc(fvilen); + if (fvi == NULL) { + goto err; + } + memset(fvi, 0, fvilen); + if (GetFileVersionInfo("kernel32.dll", 0, fvilen, fvi) == 0) { + goto err; + } + ffi = NULL; + ffilen = 0; + if ((VerQueryValue(fvi, "\\", &ffi, &ffilen) == 0) || (ffi == NULL) || + (ffilen == 0)) + { + goto err; + } + memset(&sysinfo, 0, sizeof(sysinfo)); + GetSystemInfo(&sysinfo); + switch (sysinfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + arch = "x86"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + arch = "arm"; + break; + case PROCESSOR_ARCHITECTURE_IA64: + arch = "ia64"; + break; + case PROCESSOR_ARCHITECTURE_AMD64: + arch = "x64"; + break; + default: + arch = "unknown architecture"; + break; + } + + snprintf(unamebuf, sizeof(unamebuf), + "Windows %d %d build %d %d for %s\n", + (ffi->dwProductVersionMS >> 16) & 0xffff, + ffi->dwProductVersionMS & 0xffff, + (ffi->dwProductVersionLS >> 16) & 0xffff, + ffi->dwProductVersionLS & 0xffff, arch); + +err: + if (fvi != NULL) { + free(fvi); + } + unamep = unamebuf; +} + +/* + * GetVersionEx() returns 6.2 (aka Windows 8.1) since it was obsoleted + * so we had to switch to the recommended way to get the Windows version. + */ +const char * +named_os_uname(void) { + if (unamep == NULL) { + getuname(); + } + return (unamep); +} |