From 45d6379135504814ab723b57f0eb8be23393a51d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 09:24:22 +0200 Subject: Adding upstream version 1:9.16.44. Signed-off-by: Daniel Baumann --- lib/isc/win32/.dir-locals.el | 35 + lib/isc/win32/DLLMain.c | 52 + lib/isc/win32/Makefile.in | 36 + lib/isc/win32/condition.c | 255 ++ lib/isc/win32/dir.c | 314 +++ lib/isc/win32/errno.c | 21 + lib/isc/win32/errno2result.c | 118 + lib/isc/win32/errno2result.h | 35 + lib/isc/win32/file.c | 1003 ++++++++ lib/isc/win32/fsaccess.c | 404 +++ lib/isc/win32/include/.clang-format | 1 + lib/isc/win32/include/Makefile.in | 19 + lib/isc/win32/include/isc/Makefile.in | 32 + lib/isc/win32/include/isc/align.h | 15 + lib/isc/win32/include/isc/bind_registry.h | 42 + lib/isc/win32/include/isc/bindevt.h | 83 + lib/isc/win32/include/isc/condition.h | 60 + lib/isc/win32/include/isc/dir.h | 73 + lib/isc/win32/include/isc/ipv6.h | 130 + lib/isc/win32/include/isc/mutex.h | 47 + lib/isc/win32/include/isc/net.h | 402 +++ lib/isc/win32/include/isc/netdb.h | 48 + lib/isc/win32/include/isc/ntgroups.h | 28 + lib/isc/win32/include/isc/ntpaths.h | 66 + lib/isc/win32/include/isc/offset.h | 26 + lib/isc/win32/include/isc/once.h | 40 + lib/isc/win32/include/isc/platform.h.in | 132 + lib/isc/win32/include/isc/stat.h | 58 + lib/isc/win32/include/isc/stdatomic.h | 595 +++++ lib/isc/win32/include/isc/stdtime.h | 62 + lib/isc/win32/include/isc/syslog.h | 39 + lib/isc/win32/include/isc/thread.h | 103 + lib/isc/win32/include/isc/time.h | 468 ++++ lib/isc/win32/include/isc/win32os.h | 41 + lib/isc/win32/interfaceiter.c | 550 ++++ lib/isc/win32/ipv6.c | 18 + lib/isc/win32/libgen.h | 22 + lib/isc/win32/libisc.def.exclude | 42 + lib/isc/win32/libisc.def.in | 805 ++++++ lib/isc/win32/libisc.vcxproj.filters.in | 671 +++++ lib/isc/win32/libisc.vcxproj.in | 483 ++++ lib/isc/win32/libisc.vcxproj.user | 3 + lib/isc/win32/meminfo.c | 26 + lib/isc/win32/net.c | 301 +++ lib/isc/win32/netdb.h | 182 ++ lib/isc/win32/ntgroups.c | 215 ++ lib/isc/win32/ntpaths.c | 151 ++ lib/isc/win32/once.c | 43 + lib/isc/win32/os.c | 41 + lib/isc/win32/pk11_api.c | 706 +++++ lib/isc/win32/resource.c | 66 + lib/isc/win32/socket.c | 3965 +++++++++++++++++++++++++++++ lib/isc/win32/stdio.c | 158 ++ lib/isc/win32/stdtime.c | 43 + lib/isc/win32/syslog.c | 171 ++ lib/isc/win32/syslog.h | 70 + lib/isc/win32/thread.c | 82 + lib/isc/win32/time.c | 651 +++++ lib/isc/win32/unistd.h | 55 + lib/isc/win32/version.c | 18 + lib/isc/win32/win32os.c | 113 + 61 files changed, 14534 insertions(+) create mode 100644 lib/isc/win32/.dir-locals.el create mode 100644 lib/isc/win32/DLLMain.c create mode 100644 lib/isc/win32/Makefile.in create mode 100644 lib/isc/win32/condition.c create mode 100644 lib/isc/win32/dir.c create mode 100644 lib/isc/win32/errno.c create mode 100644 lib/isc/win32/errno2result.c create mode 100644 lib/isc/win32/errno2result.h create mode 100644 lib/isc/win32/file.c create mode 100644 lib/isc/win32/fsaccess.c create mode 120000 lib/isc/win32/include/.clang-format create mode 100644 lib/isc/win32/include/Makefile.in create mode 100644 lib/isc/win32/include/isc/Makefile.in create mode 100644 lib/isc/win32/include/isc/align.h create mode 100644 lib/isc/win32/include/isc/bind_registry.h create mode 100644 lib/isc/win32/include/isc/bindevt.h create mode 100644 lib/isc/win32/include/isc/condition.h create mode 100644 lib/isc/win32/include/isc/dir.h create mode 100644 lib/isc/win32/include/isc/ipv6.h create mode 100644 lib/isc/win32/include/isc/mutex.h create mode 100644 lib/isc/win32/include/isc/net.h create mode 100644 lib/isc/win32/include/isc/netdb.h create mode 100644 lib/isc/win32/include/isc/ntgroups.h create mode 100644 lib/isc/win32/include/isc/ntpaths.h create mode 100644 lib/isc/win32/include/isc/offset.h create mode 100644 lib/isc/win32/include/isc/once.h create mode 100644 lib/isc/win32/include/isc/platform.h.in create mode 100644 lib/isc/win32/include/isc/stat.h create mode 100644 lib/isc/win32/include/isc/stdatomic.h create mode 100644 lib/isc/win32/include/isc/stdtime.h create mode 100644 lib/isc/win32/include/isc/syslog.h create mode 100644 lib/isc/win32/include/isc/thread.h create mode 100644 lib/isc/win32/include/isc/time.h create mode 100644 lib/isc/win32/include/isc/win32os.h create mode 100644 lib/isc/win32/interfaceiter.c create mode 100644 lib/isc/win32/ipv6.c create mode 100644 lib/isc/win32/libgen.h create mode 100644 lib/isc/win32/libisc.def.exclude create mode 100644 lib/isc/win32/libisc.def.in create mode 100644 lib/isc/win32/libisc.vcxproj.filters.in create mode 100644 lib/isc/win32/libisc.vcxproj.in create mode 100644 lib/isc/win32/libisc.vcxproj.user create mode 100644 lib/isc/win32/meminfo.c create mode 100644 lib/isc/win32/net.c create mode 100644 lib/isc/win32/netdb.h create mode 100644 lib/isc/win32/ntgroups.c create mode 100644 lib/isc/win32/ntpaths.c create mode 100644 lib/isc/win32/once.c create mode 100644 lib/isc/win32/os.c create mode 100644 lib/isc/win32/pk11_api.c create mode 100644 lib/isc/win32/resource.c create mode 100644 lib/isc/win32/socket.c create mode 100644 lib/isc/win32/stdio.c create mode 100644 lib/isc/win32/stdtime.c create mode 100644 lib/isc/win32/syslog.c create mode 100644 lib/isc/win32/syslog.h create mode 100644 lib/isc/win32/thread.c create mode 100644 lib/isc/win32/time.c create mode 100644 lib/isc/win32/unistd.h create mode 100644 lib/isc/win32/version.c create mode 100644 lib/isc/win32/win32os.c (limited to 'lib/isc/win32') diff --git a/lib/isc/win32/.dir-locals.el b/lib/isc/win32/.dir-locals.el new file mode 100644 index 0000000..b16a1be --- /dev/null +++ b/lib/isc/win32/.dir-locals.el @@ -0,0 +1,35 @@ +;;; Directory Local Variables +;;; For more information see (info "(emacs) Directory Variables") + +((c-mode . + ((eval . + (set (make-local-variable 'directory-of-current-dir-locals-file) + (file-name-directory (locate-dominating-file default-directory ".dir-locals.el")) + ) + ) + (eval . + (set (make-local-variable 'include-directories) + (list + (expand-file-name + (concat directory-of-current-dir-locals-file "../../../")) + (expand-file-name + (concat directory-of-current-dir-locals-file "include")) + (expand-file-name + (concat directory-of-current-dir-locals-file "../include")) + (expand-file-name + (concat directory-of-current-dir-locals-file "../")) + (expand-file-name + (concat directory-of-current-dir-locals-file "./")) + (expand-file-name + (concat directory-of-current-dir-locals-file "../../dns/include")) + (expand-file-name "/usr/local/opt/openssl@1.1/include") + (expand-file-name "/usr/local/opt/libxml2/include/libxml2") + (expand-file-name "/usr/local/include") + ) + ) + ) + + (eval setq flycheck-clang-include-path include-directories) + (eval setq flycheck-cpp-include-path include-directories) + ) + )) diff --git a/lib/isc/win32/DLLMain.c b/lib/isc/win32/DLLMain.c new file mode 100644 index 0000000..abaaf40 --- /dev/null +++ b/lib/isc/win32/DLLMain.c @@ -0,0 +1,52 @@ +/* + * 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 +#include + +#include +#include + +#include "lib_p.h" + +/* + * Called when we enter the DLL + */ +__declspec(dllexport) BOOL WINAPI + DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { + switch (fdwReason) { + /* + * The DLL is loading due to process initialization or a call to + * LoadLibrary. + */ + case DLL_PROCESS_ATTACH: + isc__initialize(); + break; + + /* + * The DLL is unloading from a process due to process + * termination or a call to FreeLibrary. + */ + case DLL_PROCESS_DETACH: + isc__shutdown(); + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + + default: + break; + } + return (TRUE); +} diff --git a/lib/isc/win32/Makefile.in b/lib/isc/win32/Makefile.in new file mode 100644 index 0000000..e8377ff --- /dev/null +++ b/lib/isc/win32/Makefile.in @@ -0,0 +1,36 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +CINCLUDES = -I${srcdir}/.. \ + -I./include \ + -I${srcdir}/include \ + -I${srcdir}/../include +CDEFINES = +CWARNINGS = + +# Alphabetically +OBJS = condition.@O@ dir.@O@ errno.@O@ file.@O@ \ + meminfo.@O@ fsaccess.@O@ \ + once.@O@ stdtime.@O@ thread.@O@ time.@O@ pk11_api.@O@ + +# Alphabetically +SRCS = condition.c dir.c errno.c file.c \ + meminfo.c once.c fsaccess.c \ + stdtime.c thread.c time.c pk11_api.c + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/win32/condition.c b/lib/isc/win32/condition.c new file mode 100644 index 0000000..ca11bce --- /dev/null +++ b/lib/isc/win32/condition.c @@ -0,0 +1,255 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LSIGNAL 0 +#define LBROADCAST 1 + +void +isc_condition_init(isc_condition_t *cond) { + HANDLE h; + + REQUIRE(cond != NULL); + + cond->waiters = 0; + /* + * This handle is shared across all threads + */ + h = CreateEvent(NULL, FALSE, FALSE, NULL); + if (h == NULL) { + char strbuf[ISC_STRERRORSIZE]; + DWORD err = GetLastError(); + strerror_r(err, strbuf, sizeof(strbuf)); + isc_error_fatal(__FILE__, __LINE__, "CreateEvent failed: %s", + strbuf); + } + cond->events[LSIGNAL] = h; + + /* + * The threadlist will hold the actual events needed + * for the wait condition + */ + ISC_LIST_INIT(cond->threadlist); +} + +/* + * Add the thread to the threadlist along with the required events + */ +static isc_result_t +register_thread(unsigned long thrd, isc_condition_t *gblcond, + isc_condition_thread_t **localcond) { + HANDLE hc; + isc_condition_thread_t *newthread; + + REQUIRE(localcond != NULL && *localcond == NULL); + + newthread = malloc(sizeof(isc_condition_thread_t)); + if (newthread == NULL) { + return (ISC_R_NOMEMORY); + } + + /* + * Create the thread-specific handle + */ + hc = CreateEvent(NULL, FALSE, FALSE, NULL); + if (hc == NULL) { + free(newthread); + return (ISC_R_UNEXPECTED); + } + + /* + * Add the thread ID and handles to list of threads for broadcast + */ + newthread->handle[LSIGNAL] = gblcond->events[LSIGNAL]; + newthread->handle[LBROADCAST] = hc; + newthread->th = thrd; + + /* + * The thread is holding the manager lock so this is safe + */ + ISC_LINK_INIT(newthread, link); + ISC_LIST_APPEND(gblcond->threadlist, newthread, link); + *localcond = newthread; + return (ISC_R_SUCCESS); +} + +static isc_result_t +find_thread_condition(uintptr_t thrd, isc_condition_t *cond, + isc_condition_thread_t **threadcondp) { + isc_condition_thread_t *threadcond; + + REQUIRE(threadcondp != NULL && *threadcondp == NULL); + + /* + * Look for the thread ID. + */ + for (threadcond = ISC_LIST_HEAD(cond->threadlist); threadcond != NULL; + threadcond = ISC_LIST_NEXT(threadcond, link)) + { + if (threadcond->th == thrd) { + *threadcondp = threadcond; + return (ISC_R_SUCCESS); + } + } + + /* + * Not found, so add it. + */ + return (register_thread(thrd, cond, threadcondp)); +} + +isc_result_t +isc_condition_signal(isc_condition_t *cond) { + /* + * Unlike pthreads, the caller MUST hold the lock associated with + * the condition variable when calling us. + */ + REQUIRE(cond != NULL); + + if (!SetEvent(cond->events[LSIGNAL])) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_condition_broadcast(isc_condition_t *cond) { + isc_condition_thread_t *threadcond; + bool failed = false; + + /* + * Unlike pthreads, the caller MUST hold the lock associated with + * the condition variable when calling us. + */ + REQUIRE(cond != NULL); + + /* + * Notify every thread registered for this + */ + for (threadcond = ISC_LIST_HEAD(cond->threadlist); threadcond != NULL; + threadcond = ISC_LIST_NEXT(threadcond, link)) + { + if (!SetEvent(threadcond->handle[LBROADCAST])) { + failed = true; + } + } + + if (failed) { + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_condition_destroy(isc_condition_t *cond) { + isc_condition_thread_t *next, *threadcond; + + REQUIRE(cond != NULL); + REQUIRE(cond->waiters == 0); + + (void)CloseHandle(cond->events[LSIGNAL]); + + /* + * Delete the threadlist + */ + threadcond = ISC_LIST_HEAD(cond->threadlist); + + while (threadcond != NULL) { + next = ISC_LIST_NEXT(threadcond, link); + DEQUEUE(cond->threadlist, threadcond, link); + (void)CloseHandle(threadcond->handle[LBROADCAST]); + free(threadcond); + threadcond = next; + } + + return (ISC_R_SUCCESS); +} + +/* + * This is always called when the mutex (lock) is held, but because + * we are waiting we need to release it and reacquire it as soon as the wait + * is over. This allows other threads to make use of the object guarded + * by the mutex but it should never try to delete it as long as the + * number of waiters > 0. Always reacquire the mutex regardless of the + * result of the wait. Note that EnterCriticalSection will wait to acquire + * the mutex. + */ +static isc_result_t +wait(isc_condition_t *cond, isc_mutex_t *mutex, DWORD milliseconds) { + DWORD result; + isc_result_t tresult; + isc_condition_thread_t *threadcond = NULL; + + /* + * Get the thread events needed for the wait + */ + tresult = find_thread_condition(isc_thread_self(), cond, &threadcond); + if (tresult != ISC_R_SUCCESS) { + return (tresult); + } + + cond->waiters++; + LeaveCriticalSection(mutex); + result = WaitForMultipleObjects(2, threadcond->handle, FALSE, + milliseconds); + EnterCriticalSection(mutex); + cond->waiters--; + if (result == WAIT_FAILED) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + if (result == WAIT_TIMEOUT) { + return (ISC_R_TIMEDOUT); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_condition_wait(isc_condition_t *cond, isc_mutex_t *mutex) { + return (wait(cond, mutex, INFINITE)); +} + +isc_result_t +isc_condition_waituntil(isc_condition_t *cond, isc_mutex_t *mutex, + isc_time_t *t) { + DWORD milliseconds; + uint64_t microseconds; + isc_time_t now; + + if (isc_time_now(&now) != ISC_R_SUCCESS) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + + microseconds = isc_time_microdiff(t, &now); + if (microseconds > 0xFFFFFFFFi64 * 1000) { + milliseconds = 0xFFFFFFFF; + } else { + milliseconds = (DWORD)(microseconds / 1000); + } + + return (wait(cond, mutex, milliseconds)); +} diff --git a/lib/isc/win32/dir.c b/lib/isc/win32/dir.c new file mode 100644 index 0000000..9a512da --- /dev/null +++ b/lib/isc/win32/dir.c @@ -0,0 +1,314 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "errno2result.h" + +#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') +#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) + +static isc_result_t +start_directory(isc_dir_t *p); + +void +isc_dir_init(isc_dir_t *dir) { + REQUIRE(dir != NULL); + + dir->dirname[0] = '\0'; + + dir->entry.name[0] = '\0'; + dir->entry.length = 0; + memset(&(dir->entry.find_data), 0, sizeof(dir->entry.find_data)); + + dir->entry_filled = false; + dir->search_handle = INVALID_HANDLE_VALUE; + + dir->magic = ISC_DIR_MAGIC; +} + +/* + * Allocate workspace and open directory stream. If either one fails, + * NULL will be returned. + */ +isc_result_t +isc_dir_open(isc_dir_t *dir, const char *dirname) { + char *p; + isc_result_t result; + + REQUIRE(dirname != NULL); + REQUIRE(VALID_DIR(dir) && dir->search_handle == INVALID_HANDLE_VALUE); + + /* + * Copy directory name. Need to have enough space for the name, + * a possible path separator, the wildcard, and the final NUL. + */ + if (strlen(dirname) + 3 > sizeof(dir->dirname)) { + /* XXXDCL ? */ + return (ISC_R_NOSPACE); + } + strlcpy(dir->dirname, dirname, sizeof(dir->dirname)); + + /* + * Append path separator, if needed, and "*". + */ + p = dir->dirname + strlen(dir->dirname); + if (dir->dirname < p && *(p - 1) != '\\' && *(p - 1) != ':') { + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* + * Open stream. + */ + result = start_directory(dir); + + return (result); +} + +/* + * Return previously retrieved file or get next one. Unix's dirent has + * separate open and read functions, but the Win32 and DOS interfaces open + * the dir stream and reads the first file in one operation. + */ +isc_result_t +isc_dir_read(isc_dir_t *dir) { + REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); + + if (dir->entry_filled) { + /* + * start_directory() already filled in the first entry. + */ + dir->entry_filled = false; + } else { + /* + * Fetch next file in directory. + */ + if (FindNextFile(dir->search_handle, &dir->entry.find_data) == + FALSE) + { + /* + * Either the last file has been processed or + * an error has occurred. The former is not + * really an error, but the latter is. + */ + if (GetLastError() == ERROR_NO_MORE_FILES) { + return (ISC_R_NOMORE); + } else { + return (ISC_R_UNEXPECTED); + } + } + } + + /* + * Make sure that the space for the name is long enough. + */ + strlcpy(dir->entry.name, dir->entry.find_data.cFileName, + sizeof(dir->entry.name)); + dir->entry.length = strlen(dir->entry.name); + + return (ISC_R_SUCCESS); +} + +/* + * Close directory stream. + */ +void +isc_dir_close(isc_dir_t *dir) { + REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); + + FindClose(dir->search_handle); + dir->search_handle = INVALID_HANDLE_VALUE; +} + +/* + * Reposition directory stream at start. + */ +isc_result_t +isc_dir_reset(isc_dir_t *dir) { + isc_result_t result; + + REQUIRE(VALID_DIR(dir) && dir->search_handle != INVALID_HANDLE_VALUE); + REQUIRE(dir->dirname != NULL); + + /* + * NT cannot reposition the seek pointer to the beginning of the + * the directory stream, but rather the directory needs to be + * closed and reopened. The latter might fail. + */ + + isc_dir_close(dir); + + result = start_directory(dir); + + return (result); +} + +/* + * Initialize isc_dir_t structure with new directory. The function + * returns 0 on failure and nonzero on success. + * + * Note: + * - Be sure to close previous stream before opening new one + */ +static isc_result_t +start_directory(isc_dir_t *dir) { + REQUIRE(VALID_DIR(dir)); + REQUIRE(dir->search_handle == INVALID_HANDLE_VALUE); + + dir->entry_filled = false; + + /* + * Open stream and retrieve first file. + */ + dir->search_handle = FindFirstFile(dir->dirname, &dir->entry.find_data); + + if (dir->search_handle == INVALID_HANDLE_VALUE) { + /* + * Something went wrong but we don't know what. GetLastError() + * could give us more information about the error, but the + * MSDN documentation is frustratingly thin about what + * possible errors could have resulted. (Score one for + * the Unix manual pages.) So there is just this lame error + * instead of being able to differentiate ISC_R_NOTFOUND + * from ISC_R_UNEXPECTED. + */ + return (ISC_R_FAILURE); + } + + /* + * Make sure that the space for the name is long enough. + */ + INSIST(sizeof(dir->entry.name) > + strlen(dir->entry.find_data.cFileName)); + + /* + * Fill in the data for the first entry of the directory. + */ + strlcpy(dir->entry.name, dir->entry.find_data.cFileName, + sizeof(dir->entry.name)); + dir->entry.length = strlen(dir->entry.name); + + dir->entry_filled = true; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_dir_chdir(const char *dirname) { + /* + * Change the current directory to 'dirname'. + */ + + REQUIRE(dirname != NULL); + + if (chdir(dirname) < 0) { + return (isc__errno2result(errno)); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_dir_chroot(const char *dirname) { + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +isc_dir_createunique(char *templet) { + isc_result_t result; + char *x; + char *p; + int i; + int pid; + + REQUIRE(templet != NULL); + + /* + * mkdtemp is not portable, so this emulates it. + */ + + pid = getpid(); + + /* + * Replace trailing Xs with the process-id, zero-filled. + */ + for (x = templet + strlen(templet) - 1; *x == 'X' && x >= templet; + x--, pid /= 10) + { + *x = pid % 10 + '0'; + } + + x++; /* Set x to start of ex-Xs. */ + + do { + i = mkdir(templet); + if (i == 0) { + i = chmod(templet, 0700); + } + + if (i == 0 || errno != EEXIST) { + break; + } + + /* + * The BSD algorithm. + */ + p = x; + while (*p != '\0') { + if (isdigit((unsigned char)*p)) { + *p = 'a'; + } else if (*p != 'z') { + ++*p; + } else { + /* + * Reset character and move to next. + */ + *p++ = 'a'; + continue; + } + + break; + } + + if (*p == '\0') { + /* + * Tried all combinations. errno should already + * be EEXIST, but ensure it is anyway for + * isc__errno2result(). + */ + errno = EEXIST; + break; + } + } while (1); + + if (i == -1) { + result = isc__errno2result(errno); + } else { + result = ISC_R_SUCCESS; + } + + return (result); +} diff --git a/lib/isc/win32/errno.c b/lib/isc/win32/errno.c new file mode 100644 index 0000000..fd6899f --- /dev/null +++ b/lib/isc/win32/errno.c @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/*! \file */ + +#include "errno2result.h" + +isc_result_t +isc_errno_toresult(int err) { + return (isc__errno2resultx(err, false, 0, 0)); +} diff --git a/lib/isc/win32/errno2result.c b/lib/isc/win32/errno2result.c new file mode 100644 index 0000000..593c27b --- /dev/null +++ b/lib/isc/win32/errno2result.c @@ -0,0 +1,118 @@ +/* + * 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 "errno2result.h" +#include +#include + +#include +#include +#include +#include + +/* + * Convert a POSIX errno value into an isc_result_t. The + * list of supported errno values is not complete; new users + * of this function should add any expected errors that are + * not already there. + */ +isc_result_t +isc__errno2resultx(int posixerrno, bool dolog, const char *file, int line) { + char strbuf[ISC_STRERRORSIZE]; + + switch (posixerrno) { + case ENOTDIR: + case WSAELOOP: + case WSAEINVAL: + case EINVAL: /* XXX sometimes this is not for files */ + case ENAMETOOLONG: + case WSAENAMETOOLONG: + case EBADF: + case WSAEBADF: + return (ISC_R_INVALIDFILE); + case ENOENT: + return (ISC_R_FILENOTFOUND); + case EACCES: + case WSAEACCES: + case EPERM: + return (ISC_R_NOPERM); + case EEXIST: + return (ISC_R_FILEEXISTS); + case EIO: + return (ISC_R_IOERROR); + case ENOMEM: + return (ISC_R_NOMEMORY); +#ifdef EOVERFLOW + case EOVERFLOW: + return (ISC_R_RANGE); +#endif /* ifdef EOVERFLOW */ + case ENFILE: + case EMFILE: + case WSAEMFILE: + return (ISC_R_TOOMANYOPENFILES); + case ENOSPC: + return (ISC_R_DISCFULL); + case ERROR_CANCELLED: + return (ISC_R_CANCELED); + case ERROR_CONNECTION_REFUSED: + case WSAECONNREFUSED: + return (ISC_R_CONNREFUSED); + case WSAENOTCONN: + case ERROR_CONNECTION_INVALID: + return (ISC_R_NOTCONNECTED); + case ERROR_HOST_UNREACHABLE: + case WSAEHOSTUNREACH: + return (ISC_R_HOSTUNREACH); + case ERROR_NETWORK_UNREACHABLE: + case WSAENETUNREACH: + return (ISC_R_NETUNREACH); + case ERROR_NO_NETWORK: + return (ISC_R_NETUNREACH); + case ERROR_PORT_UNREACHABLE: + return (ISC_R_HOSTUNREACH); + case ERROR_SEM_TIMEOUT: + return (ISC_R_TIMEDOUT); + case WSAECONNRESET: + case WSAENETRESET: + case WSAECONNABORTED: + case WSAEDISCON: + case ERROR_OPERATION_ABORTED: + case ERROR_CONNECTION_ABORTED: + case ERROR_REQUEST_ABORTED: + return (ISC_R_CONNECTIONRESET); + case WSAEADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case ERROR_NETNAME_DELETED: + case WSAENETDOWN: + return (ISC_R_NETUNREACH); + case WSAEHOSTDOWN: + return (ISC_R_HOSTUNREACH); + case WSAENOBUFS: + return (ISC_R_NORESOURCES); + default: + if (dolog) { + strerror_r(posixerrno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(file, line, + "unable to convert errno " + "to isc_result: %d: %s", + posixerrno, strbuf); + } + /* + * XXXDCL would be nice if perhaps this function could + * return the system's error string, so the caller + * might have something more descriptive than "unexpected + * error" to log with. + */ + return (ISC_R_UNEXPECTED); + } +} diff --git a/lib/isc/win32/errno2result.h b/lib/isc/win32/errno2result.h new file mode 100644 index 0000000..ce2d72e --- /dev/null +++ b/lib/isc/win32/errno2result.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef UNIX_ERRNO2RESULT_H +#define UNIX_ERRNO2RESULT_H 1 + +/* XXXDCL this should be moved to lib/isc/include/isc/errno2result.h. */ + +#include /* Provides errno. */ +#include + +#include +#include + +ISC_LANG_BEGINDECLS + +#define isc__errno2result(posixerrno) \ + isc__errno2resultx(posixerrno, true, __FILE__, __LINE__) + +isc_result_t +isc__errno2resultx(int posixerrno, bool dolog, const char *file, int line); + +ISC_LANG_ENDDECLS + +#endif /* UNIX_ERRNO2RESULT_H */ diff --git a/lib/isc/win32/file.c b/lib/isc/win32/file.c new file mode 100644 index 0000000..32f6a19 --- /dev/null +++ b/lib/isc/win32/file.c @@ -0,0 +1,1003 @@ +/* + * 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. + */ + +#undef rename +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "errno2result.h" + +static const char alphnum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv" + "wxyz0123456789"; + +/* + * Emulate UNIX mkstemp, which returns an open FD to the new file + * + */ +static int +gettemp(char *path, bool binary, int *doopen) { + char *start, *trv; + struct stat sbuf; + int flags = O_CREAT | O_EXCL | O_RDWR; + + if (binary) { + flags |= _O_BINARY; + } + + trv = strrchr(path, 'X'); + trv++; + /* extra X's get set to 0's */ + while (*--trv == 'X') { + uint32_t which = isc_random_uniform(sizeof(alphnum) - 1); + *trv = alphnum[which]; + } + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) { + break; + } + if (*trv == '\\') { + *trv = '\0'; + if (stat(path, &sbuf)) { + return (0); + } + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return (0); + } + *trv = '\\'; + break; + } + } + + for (;;) { + if (doopen) { + if ((*doopen = open(path, flags, + _S_IREAD | _S_IWRITE)) >= 0) + { + return (1); + } + if (errno != EEXIST) { + return (0); + } + } else if (stat(path, &sbuf)) { + return (errno == ENOENT ? 1 : 0); + } + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) { + return (0); + } + if (*trv == 'z') { + *trv++ = 'a'; + } else { + if (isdigit((unsigned char)*trv)) { + *trv = 'a'; + } else { + ++*trv; + } + break; + } + } + } + /*NOTREACHED*/ +} + +static int +mkstemp(char *path, bool binary) { + int fd; + + return (gettemp(path, binary, &fd) ? fd : -1); +} + +/* + * XXXDCL As the API for accessing file statistics undoubtedly gets expanded, + * it might be good to provide a mechanism that allows for the results + * of a previous stat() to be used again without having to do another stat, + * such as perl's mechanism of using "_" in place of a file name to indicate + * that the results of the last stat should be used. But then you get into + * annoying MP issues. BTW, Win32 has stat(). + */ +static isc_result_t +file_stats(const char *file, struct stat *stats) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(file != NULL); + REQUIRE(stats != NULL); + + if (stat(file, stats) != 0) { + result = isc__errno2result(errno); + } + + return (result); +} + +static isc_result_t +fd_stats(int fd, struct stat *stats) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(stats != NULL); + + if (fstat(fd, stats) != 0) { + result = isc__errno2result(errno); + } + + return (result); +} + +isc_result_t +isc_file_getsizefd(int fd, off_t *size) { + isc_result_t result; + struct stat stats; + + REQUIRE(size != NULL); + + result = fd_stats(fd, &stats); + + if (result == ISC_R_SUCCESS) { + *size = stats.st_size; + } + return (result); +} + +isc_result_t +isc_file_mode(const char *file, mode_t *modep) { + isc_result_t result; + struct stat stats; + + REQUIRE(modep != NULL); + + result = file_stats(file, &stats); + if (result == ISC_R_SUCCESS) { + *modep = (stats.st_mode & 07777); + } + return (result); +} + +/* + * isc_file_safemovefile is needed to be defined here to ensure that + * any file with the new name is renamed to a backup name and then the + * rename is done. If all goes well then the backup can be deleted, + * otherwise it gets renamed back. + */ + +int +isc_file_safemovefile(const char *oldname, const char *newname) { + BOOL filestatus; + char buf[512]; + struct stat sbuf; + BOOL exists = FALSE; + int tmpfd; + + /* + * Make sure we have something to do + */ + if (stat(oldname, &sbuf) != 0) { + errno = ENOENT; + return (-1); + } + + /* + * Rename to a backup the new file if it still exists + */ + if (stat(newname, &sbuf) == 0) { + exists = TRUE; + strlcpy(buf, newname, sizeof(buf)); + strlcat(buf, ".XXXXX", sizeof(buf)); + tmpfd = mkstemp(buf, true); + if (tmpfd > 0) { + _close(tmpfd); + } + (void)DeleteFile(buf); + _chmod(newname, _S_IREAD | _S_IWRITE); + + filestatus = MoveFile(newname, buf); + } + /* Now rename the file to the new name + */ + _chmod(oldname, _S_IREAD | _S_IWRITE); + + filestatus = MoveFile(oldname, newname); + if (filestatus == 0) { + /* + * Try to rename the backup back to the original name + * if the backup got created + */ + if (exists == TRUE) { + filestatus = MoveFile(buf, newname); + if (filestatus == 0) { + errno = EACCES; + } + } + return (-1); + } + + /* + * Delete the backup file if it got created + */ + if (exists == TRUE) { + (void)DeleteFile(buf); + } + return (0); +} + +isc_result_t +isc_file_getmodtime(const char *file, isc_time_t *time) { + int fh; + + REQUIRE(file != NULL); + REQUIRE(time != NULL); + + if ((fh = open(file, _O_RDONLY | _O_BINARY)) < 0) { + return (isc__errno2result(errno)); + } + + if (!GetFileTime((HANDLE)_get_osfhandle(fh), NULL, NULL, + &time->absolute)) + { + close(fh); + errno = EINVAL; + return (isc__errno2result(errno)); + } + close(fh); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_getsize(const char *file, off_t *size) { + isc_result_t result; + struct stat stats; + + REQUIRE(file != NULL); + REQUIRE(size != NULL); + + result = file_stats(file, &stats); + + if (result == ISC_R_SUCCESS) { + *size = stats.st_size; + } + + return (result); +} + +isc_result_t +isc_file_settime(const char *file, isc_time_t *time) { + int fh; + + REQUIRE(file != NULL && time != NULL); + + if ((fh = open(file, _O_RDWR | _O_BINARY)) < 0) { + return (isc__errno2result(errno)); + } + + /* + * Set the date via the filedate system call and return. Failing + * this call implies the new file times are not supported by the + * underlying file system. + */ + if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &time->absolute, + &time->absolute)) + { + close(fh); + errno = EINVAL; + return (isc__errno2result(errno)); + } + + close(fh); + return (ISC_R_SUCCESS); +} + +#undef TEMPLATE +#define TEMPLATE "XXXXXXXXXX.tmp" /* 14 characters. */ + +isc_result_t +isc_file_mktemplate(const char *path, char *buf, size_t buflen) { + return (isc_file_template(path, TEMPLATE, buf, buflen)); +} + +isc_result_t +isc_file_template(const char *path, const char *templet, char *buf, + size_t buflen) { + char *s; + + REQUIRE(templet != NULL); + REQUIRE(buf != NULL); + + if (path == NULL) { + path = ""; + } + + s = strrchr(templet, '\\'); + if (s != NULL) { + templet = s + 1; + } + + s = strrchr(path, '\\'); + + if (s != NULL) { + size_t prefixlen = s - path + 1; + if ((prefixlen + strlen(templet) + 1) > buflen) { + return (ISC_R_NOSPACE); + } + + /* Copy 'prefixlen' bytes and NUL terminate. */ + strlcpy(buf, path, ISC_MIN(prefixlen + 1, buflen)); + strlcat(buf, templet, buflen); + } else { + if ((strlen(templet) + 1) > buflen) { + return (ISC_R_NOSPACE); + } + + strlcpy(buf, templet, buflen); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_renameunique(const char *file, char *templet) { + int fd; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(file != NULL); + REQUIRE(templet != NULL); + + fd = mkstemp(templet, true); + if (fd == -1) { + result = isc__errno2result(errno); + } else { + close(fd); + } + + if (result == ISC_R_SUCCESS) { + int res; + res = isc_file_safemovefile(file, templet); + if (res != 0) { + result = isc__errno2result(errno); + (void)unlink(templet); + } + } + return (result); +} + +static isc_result_t +openuniquemode(char *templet, int mode, bool binary, FILE **fp) { + int fd; + FILE *f; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(templet != NULL); + REQUIRE(fp != NULL && *fp == NULL); + + /* + * Win32 does not have mkstemp. Using emulation above. + */ + fd = mkstemp(templet, binary); + + if (fd == -1) { + result = isc__errno2result(errno); + } + if (result == ISC_R_SUCCESS) { +#if 1 + UNUSED(mode); +#else /* if 1 */ + (void)fchmod(fd, mode); +#endif /* if 1 */ + f = fdopen(fd, binary ? "wb+" : "w+"); + if (f == NULL) { + result = isc__errno2result(errno); + (void)remove(templet); + (void)close(fd); + } else { + *fp = f; + } + } + + return (result); +} + +isc_result_t +isc_file_openuniqueprivate(char *templet, FILE **fp) { + int mode = _S_IREAD | _S_IWRITE; + return (openuniquemode(templet, mode, false, fp)); +} + +isc_result_t +isc_file_openunique(char *templet, FILE **fp) { + int mode = _S_IREAD | _S_IWRITE; + return (openuniquemode(templet, mode, false, fp)); +} + +isc_result_t +isc_file_openuniquemode(char *templet, int mode, FILE **fp) { + return (openuniquemode(templet, mode, false, fp)); +} + +isc_result_t +isc_file_bopenuniqueprivate(char *templet, FILE **fp) { + int mode = _S_IREAD | _S_IWRITE; + return (openuniquemode(templet, mode, true, fp)); +} + +isc_result_t +isc_file_bopenunique(char *templet, FILE **fp) { + int mode = _S_IREAD | _S_IWRITE; + return (openuniquemode(templet, mode, true, fp)); +} + +isc_result_t +isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) { + return (openuniquemode(templet, mode, true, fp)); +} + +isc_result_t +isc_file_remove(const char *filename) { + int r; + + REQUIRE(filename != NULL); + + r = unlink(filename); + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_file_rename(const char *oldname, const char *newname) { + int r; + + REQUIRE(oldname != NULL); + REQUIRE(newname != NULL); + + r = isc_file_safemovefile(oldname, newname); + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +bool +isc_file_exists(const char *pathname) { + struct stat stats; + + REQUIRE(pathname != NULL); + + return (file_stats(pathname, &stats) == ISC_R_SUCCESS); +} + +isc_result_t +isc_file_isplainfile(const char *filename) { + /* + * This function returns success if filename is a plain file. + */ + struct stat filestat; + memset(&filestat, 0, sizeof(struct stat)); + + if ((stat(filename, &filestat)) == -1) { + return (isc__errno2result(errno)); + } + + if (!S_ISREG(filestat.st_mode)) { + return (ISC_R_INVALIDFILE); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_isplainfilefd(int fd) { + /* + * This function returns success if filename is a plain file. + */ + struct stat filestat; + memset(&filestat, 0, sizeof(struct stat)); + + if ((fstat(fd, &filestat)) == -1) { + return (isc__errno2result(errno)); + } + + if (!S_ISREG(filestat.st_mode)) { + return (ISC_R_INVALIDFILE); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_isdirectory(const char *filename) { + /* + * This function returns success if filename is a directory. + */ + struct stat filestat; + memset(&filestat, 0, sizeof(struct stat)); + + if ((stat(filename, &filestat)) == -1) { + return (isc__errno2result(errno)); + } + + if (!S_ISDIR(filestat.st_mode)) { + return (ISC_R_INVALIDFILE); + } + + return (ISC_R_SUCCESS); +} + +bool +isc_file_isabsolute(const char *filename) { + REQUIRE(filename != NULL); + /* + * Look for c:\path\... style, c:/path/... or \\computer\shar\path... + * the UNC style file specs + */ + if ((filename[0] == '\\') && (filename[1] == '\\')) { + return (true); + } + if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '\\') { + return (true); + } + if (isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/') { + return (true); + } + return (false); +} + +bool +isc_file_iscurrentdir(const char *filename) { + REQUIRE(filename != NULL); + return (filename[0] == '.' && filename[1] == '\0'); +} + +bool +isc_file_ischdiridempotent(const char *filename) { + REQUIRE(filename != NULL); + + if (isc_file_isabsolute(filename)) { + return (true); + } + if (filename[0] == '\\') { + return (true); + } + if (filename[0] == '/') { + return (true); + } + if (isc_file_iscurrentdir(filename)) { + return (true); + } + return (false); +} + +const char * +isc_file_basename(const char *filename) { + char *s; + + REQUIRE(filename != NULL); + + s = strrchr(filename, '\\'); + if (s == NULL) { + return (filename); + } + return (s + 1); +} + +isc_result_t +isc_file_progname(const char *filename, char *progname, size_t namelen) { + const char *s; + const char *p; + size_t len; + + REQUIRE(filename != NULL); + REQUIRE(progname != NULL); + + /* + * Strip the path from the name + */ + s = isc_file_basename(filename); + if (s == NULL) { + return (ISC_R_NOSPACE); + } + + /* + * Strip any and all suffixes + */ + p = strchr(s, '.'); + if (p == NULL) { + if (namelen <= strlen(s)) { + return (ISC_R_NOSPACE); + } + + strlcpy(progname, s, namelen); + return (ISC_R_SUCCESS); + } + + /* + * Copy the result to the buffer + */ + len = p - s; + if (len >= namelen) { + return (ISC_R_NOSPACE); + } + + /* Copy up to 'len' bytes and NUL terminate. */ + strlcpy(progname, s, ISC_MIN(len + 1, namelen)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { + char *ptrname; + DWORD retval; + + REQUIRE(filename != NULL); + REQUIRE(path != NULL); + + retval = GetFullPathName(filename, (DWORD)pathlen, path, &ptrname); + + /* Something went wrong in getting the path */ + if (retval == 0) { + return (ISC_R_NOTFOUND); + } + /* Caller needs to provide a larger buffer to contain the string */ + if (retval >= pathlen) { + return (ISC_R_NOSPACE); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_truncate(const char *filename, isc_offset_t size) { + int fh; + + REQUIRE(filename != NULL && size >= 0); + + if ((fh = open(filename, _O_RDWR | _O_BINARY)) < 0) { + return (isc__errno2result(errno)); + } + + if (_chsize(fh, size) != 0) { + close(fh); + return (isc__errno2result(errno)); + } + close(fh); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_safecreate(const char *filename, FILE **fp) { + isc_result_t result; + int flags; + struct stat sb; + FILE *f; + int fd; + + REQUIRE(filename != NULL); + REQUIRE(fp != NULL && *fp == NULL); + + result = file_stats(filename, &sb); + if (result == ISC_R_SUCCESS) { + if ((sb.st_mode & S_IFREG) == 0) { + return (ISC_R_INVALIDFILE); + } + flags = O_WRONLY | O_TRUNC; + } else if (result == ISC_R_FILENOTFOUND) { + flags = O_WRONLY | O_CREAT | O_EXCL; + } else { + return (result); + } + + fd = open(filename, flags, S_IRUSR | S_IWUSR); + if (fd == -1) { + return (isc__errno2result(errno)); + } + + f = fdopen(fd, "w"); + if (f == NULL) { + result = isc__errno2result(errno); + close(fd); + return (result); + } + + *fp = f; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_splitpath(isc_mem_t *mctx, const char *path, char **dirname, + char const **basename) { + char *dir; + const char *file, *slash; + char *backslash; + + slash = strrchr(path, '/'); + + backslash = strrchr(path, '\\'); + if ((slash != NULL && backslash != NULL && backslash > slash) || + (slash == NULL && backslash != NULL)) + { + slash = backslash; + } + + if (slash == path) { + file = ++slash; + dir = isc_mem_strdup(mctx, "/"); + } else if (slash != NULL) { + file = ++slash; + dir = isc_mem_allocate(mctx, slash - path); + strlcpy(dir, path, slash - path); + } else { + file = path; + dir = isc_mem_strdup(mctx, "."); + } + + if (dir == NULL) { + return (ISC_R_NOMEMORY); + } + + if (*file == '\0') { + isc_mem_free(mctx, dir); + return (ISC_R_INVALIDFILE); + } + + *dirname = dir; + *basename = file; + + return (ISC_R_SUCCESS); +} + +void * +isc_file_mmap(void *addr, size_t len, int prot, int flags, int fd, + off_t offset) { + void *buf; + ssize_t ret; + off_t end; + + UNUSED(addr); + UNUSED(prot); + UNUSED(flags); + + end = lseek(fd, 0, SEEK_END); + lseek(fd, offset, SEEK_SET); + if (end - offset < (off_t)len) { + len = end - offset; + } + + buf = malloc(len); + if (buf == NULL) { + return (NULL); + } + + ret = read(fd, buf, (unsigned int)len); + if (ret != (ssize_t)len) { + free(buf); + buf = NULL; + } + + return (buf); +} + +int +isc_file_munmap(void *addr, size_t len) { + UNUSED(len); + free(addr); + return (0); +} + +#define DISALLOW "\\/:ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +static isc_result_t +digest2hex(unsigned char *digest, unsigned int digestlen, char *hash, + size_t hashlen) { + unsigned int i; + int ret; + for (i = 0; i < digestlen; i++) { + size_t left = hashlen - i * 2; + ret = snprintf(hash + i * 2, left, "%02x", digest[i]); + if (ret < 0 || (size_t)ret >= left) { + return (ISC_R_NOSPACE); + } + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_sanitize(const char *dir, const char *base, const char *ext, + char *path, size_t length) { + char buf[PATH_MAX]; + unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned int digestlen; + char hash[ISC_MAX_MD_SIZE * 2 + 1]; + size_t l = 0; + isc_result_t err; + + REQUIRE(base != NULL); + REQUIRE(path != NULL); + + l = strlen(base) + 1; + + /* + * allow room for a full sha256 hash (64 chars + * plus null terminator) + */ + if (l < 65) { + l = 65; + } + + if (dir != NULL) { + l += strlen(dir) + 1; + } + if (ext != NULL) { + l += strlen(ext) + 1; + } + + if (l > length || l > PATH_MAX) { + return (ISC_R_NOSPACE); + } + + /* Check whether the full-length SHA256 hash filename exists */ + err = isc_md(ISC_MD_SHA256, (const unsigned char *)base, strlen(base), + digest, &digestlen); + if (err != ISC_R_SUCCESS) { + return (err); + } + + err = digest2hex(digest, digestlen, hash, sizeof(hash)); + if (err != ISC_R_SUCCESS) { + return (err); + } + + snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", + dir != NULL ? "/" : "", hash, ext != NULL ? "." : "", + ext != NULL ? ext : ""); + if (isc_file_exists(buf)) { + strlcpy(path, buf, length); + return (ISC_R_SUCCESS); + } + + /* Check for a truncated SHA256 hash filename */ + hash[16] = '\0'; + snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", + dir != NULL ? "/" : "", hash, ext != NULL ? "." : "", + ext != NULL ? ext : ""); + if (isc_file_exists(buf)) { + strlcpy(path, buf, length); + return (ISC_R_SUCCESS); + } + + /* + * If neither hash filename already exists, then we'll use + * the original base name if it has no disallowed characters, + * or the truncated hash name if it does. + */ + if (strpbrk(base, DISALLOW) != NULL) { + strlcpy(path, buf, length); + return (ISC_R_SUCCESS); + } + + snprintf(buf, sizeof(buf), "%s%s%s%s%s", dir != NULL ? dir : "", + dir != NULL ? "/" : "", base, ext != NULL ? "." : "", + ext != NULL ? ext : ""); + strlcpy(path, buf, length); + return (ISC_R_SUCCESS); +} + +/* + * Based on http://blog.aaronballman.com/2011/08/how-to-check-access-rights/ + */ +bool +isc_file_isdirwritable(const char *path) { + DWORD length = 0; + HANDLE hToken = NULL; + PSECURITY_DESCRIPTOR security = NULL; + bool answer = false; + + if (isc_file_isdirectory(path) != ISC_R_SUCCESS) { + return (answer); + } + + /* + * Figure out buffer size. GetFileSecurity() should not succeed. + */ + if (GetFileSecurity(path, + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + NULL, 0, &length)) + { + return (answer); + } + + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return (answer); + } + + security = malloc(length); + if (security == NULL) { + return (answer); + } + + /* + * GetFileSecurity() should succeed. + */ + if (!GetFileSecurity(path, + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION, + security, length, &length)) + { + return (answer); + } + + if (OpenProcessToken(GetCurrentProcess(), + TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | + STANDARD_RIGHTS_READ, + &hToken)) + { + HANDLE hImpersonatedToken = NULL; + + if (DuplicateToken(hToken, SecurityImpersonation, + &hImpersonatedToken)) + { + GENERIC_MAPPING mapping; + PRIVILEGE_SET privileges = { 0 }; + DWORD grantedAccess = 0; + DWORD privilegesLength = sizeof(privileges); + BOOL result = FALSE; + DWORD genericAccessRights = GENERIC_WRITE; + + mapping.GenericRead = FILE_GENERIC_READ; + mapping.GenericWrite = FILE_GENERIC_WRITE; + mapping.GenericExecute = FILE_GENERIC_EXECUTE; + mapping.GenericAll = FILE_ALL_ACCESS; + + MapGenericMask(&genericAccessRights, &mapping); + if (AccessCheck(security, hImpersonatedToken, + genericAccessRights, &mapping, + &privileges, &privilegesLength, + &grantedAccess, &result)) + { + answer = result; + } + CloseHandle(hImpersonatedToken); + } + CloseHandle(hToken); + } + free(security); + return (answer); +} diff --git a/lib/isc/win32/fsaccess.c b/lib/isc/win32/fsaccess.c new file mode 100644 index 0000000..909f9e5 --- /dev/null +++ b/lib/isc/win32/fsaccess.c @@ -0,0 +1,404 @@ +/* + * 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. + */ + +/* + * Note that Win32 does not have the concept of files having access + * and ownership bits. The FAT File system only has a readonly flag + * for everyone and that's all. NTFS uses ACL's which is a totally + * different concept of controlling access. + * + * This code needs to be revisited to set up proper access control for + * NTFS file systems. Nothing can be done for FAT file systems. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "errno2result.h" + +/* + * The OS-independent part of the API is in lib/isc. + */ +#include "../fsaccess.c" + +/* Store the user account name locally */ +static char username[255] = "\0"; +static DWORD namelen = 0; + +/* + * In order to set or retrieve access information, we need to obtain + * the File System type. These could be UNC-type shares. + */ + +BOOL +is_ntfs(const char *file) { + char drive[255]; + char FSType[20]; + char tmpbuf[256]; + char *machinename; + char *sharename; + char filename[1024]; + char *last; + + REQUIRE(filename != NULL); + + if (isc_file_absolutepath(file, filename, sizeof(filename)) != + ISC_R_SUCCESS) + { + return (FALSE); + } + + /* + * Look for c:\path\... style, c:/path/... or \\computer\shar\path... + * the UNC style file specs + */ + if (isalpha(filename[0]) && filename[1] == ':' && + (filename[2] == '\\' || filename[2] == '/')) + { + /* Copy 'c:\' or 'c:/' and NUL terminate. */ + strlcpy(drive, filename, ISC_MIN(3 + 1, sizeof(drive))); + } else if ((filename[0] == '\\') && (filename[1] == '\\')) { + /* Find the machine and share name and rebuild the UNC */ + strlcpy(tmpbuf, filename, sizeof(tmpbuf)); + machinename = strtok_r(tmpbuf, "\\", &last); + sharename = strtok_r(NULL, "\\", &last); + strlcpy(drive, "\\\\", sizeof(drive)); + strlcat(drive, machinename, sizeof(drive)); + strlcat(drive, "\\", sizeof(drive)); + strlcat(drive, sharename, sizeof(drive)); + strlcat(drive, "\\", sizeof(drive)); + } else { /* Not determinable */ + return (FALSE); + } + + GetVolumeInformation(drive, NULL, 0, NULL, 0, NULL, FSType, + sizeof(FSType)); + if (strcmp(FSType, "NTFS") == 0) { + return (TRUE); + } else { + return (FALSE); + } +} + +/* + * If it's not NTFS, we assume that it is FAT and proceed + * with almost nothing to do. Only the write flag can be set or + * cleared. + */ +isc_result_t +FAT_fsaccess_set(const char *path, isc_fsaccess_t access) { + int mode; + isc_fsaccess_t bits; + + /* + * Done with checking bad bits. Set mode_t. + */ + mode = 0; + +#define SET_AND_CLEAR1(modebit) \ + if ((access & bits) != 0) { \ + mode |= modebit; \ + access &= ~bits; \ + } +#define SET_AND_CLEAR(user, group, other) \ + SET_AND_CLEAR1(user); \ + bits <<= STEP; \ + SET_AND_CLEAR1(group); \ + bits <<= STEP; \ + SET_AND_CLEAR1(other); + + bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY; + + SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH); + + bits = ISC_FSACCESS_WRITE | ISC_FSACCESS_CREATECHILD | + ISC_FSACCESS_DELETECHILD; + + SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH); + + INSIST(access == 0); + + if (_chmod(path, mode) < 0) { + return (isc__errno2result(errno)); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +NTFS_Access_Control(const char *filename, const char *user, int access, + bool isdir) { + SECURITY_DESCRIPTOR sd; + BYTE aclBuffer[1024]; + PACL pacl = (PACL)&aclBuffer; + BYTE sidBuffer[100]; + PSID psid = (PSID)&sidBuffer; + DWORD sidBufferSize = sizeof(sidBuffer); + BYTE adminSidBuffer[100]; + PSID padminsid = (PSID)&adminSidBuffer; + DWORD adminSidBufferSize = sizeof(adminSidBuffer); + BYTE otherSidBuffer[100]; + PSID pothersid = (PSID)&otherSidBuffer; + DWORD otherSidBufferSize = sizeof(otherSidBuffer); + char domainBuffer[100]; + DWORD domainBufferSize = sizeof(domainBuffer); + SID_NAME_USE snu; + DWORD NTFSbits; + int caccess; + + /* Initialize an ACL */ + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + return (ISC_R_NOPERM); + } + if (!InitializeAcl(pacl, sizeof(aclBuffer), ACL_REVISION)) { + return (ISC_R_NOPERM); + } + if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, + &domainBufferSize, &snu)) + { + return (ISC_R_NOPERM); + } + domainBufferSize = sizeof(domainBuffer); + if (!LookupAccountName(0, "Administrators", padminsid, + &adminSidBufferSize, domainBuffer, + &domainBufferSize, &snu)) + { + (void)GetLastError(); + return (ISC_R_NOPERM); + } + domainBufferSize = sizeof(domainBuffer); + if (!LookupAccountName(0, "Everyone", pothersid, &otherSidBufferSize, + domainBuffer, &domainBufferSize, &snu)) + { + (void)GetLastError(); + return (ISC_R_NOPERM); + } + + caccess = access; + /* Owner check */ + + NTFSbits = 0; + if ((caccess & ISC_FSACCESS_READ) != 0) { + NTFSbits |= FILE_GENERIC_READ; + } + if ((caccess & ISC_FSACCESS_WRITE) != 0) { + NTFSbits |= FILE_GENERIC_WRITE; + } + if ((caccess & ISC_FSACCESS_EXECUTE) != 0) { + NTFSbits |= FILE_GENERIC_EXECUTE; + } + + /* For directories check the directory-specific bits */ + if (isdir) { + if ((caccess & ISC_FSACCESS_CREATECHILD) != 0) { + NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; + } + if ((caccess & ISC_FSACCESS_DELETECHILD) != 0) { + NTFSbits |= FILE_DELETE_CHILD; + } + if ((caccess & ISC_FSACCESS_LISTDIRECTORY) != 0) { + NTFSbits |= FILE_LIST_DIRECTORY; + } + if ((caccess & ISC_FSACCESS_ACCESSCHILD) != 0) { + NTFSbits |= FILE_TRAVERSE; + } + } + + if (NTFSbits == + (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)) + { + NTFSbits |= FILE_ALL_ACCESS; + } + /* + * Owner and Administrator also get STANDARD_RIGHTS_ALL + * to ensure that they have full control + */ + + NTFSbits |= STANDARD_RIGHTS_ALL; + + /* Add the ACE to the ACL */ + if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, psid)) { + return (ISC_R_NOPERM); + } + if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, padminsid)) { + return (ISC_R_NOPERM); + } + + /* + * Group is ignored since we can be in multiple groups or no group + * and its meaning is not clear on Win32 + */ + + caccess = caccess >> STEP; + + /* + * Other check. We translate this to be the same as Everyone + */ + + caccess = caccess >> STEP; + + NTFSbits = 0; + if ((caccess & ISC_FSACCESS_READ) != 0) { + NTFSbits |= FILE_GENERIC_READ; + } + if ((caccess & ISC_FSACCESS_WRITE) != 0) { + NTFSbits |= FILE_GENERIC_WRITE; + } + if ((caccess & ISC_FSACCESS_EXECUTE) != 0) { + NTFSbits |= FILE_GENERIC_EXECUTE; + } + + /* For directories check the directory-specific bits */ + if (isdir == TRUE) { + if ((caccess & ISC_FSACCESS_CREATECHILD) != 0) { + NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; + } + if ((caccess & ISC_FSACCESS_DELETECHILD) != 0) { + NTFSbits |= FILE_DELETE_CHILD; + } + if ((caccess & ISC_FSACCESS_LISTDIRECTORY) != 0) { + NTFSbits |= FILE_LIST_DIRECTORY; + } + if ((caccess & ISC_FSACCESS_ACCESSCHILD) != 0) { + NTFSbits |= FILE_TRAVERSE; + } + } + /* Add the ACE to the ACL */ + if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, pothersid)) { + return (ISC_R_NOPERM); + } + + if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { + return (ISC_R_NOPERM); + } + if (!SetFileSecurity(filename, DACL_SECURITY_INFORMATION, &sd)) { + return (ISC_R_NOPERM); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +NTFS_fsaccess_set(const char *path, isc_fsaccess_t access, bool isdir) { + /* + * For NTFS we first need to get the name of the account under + * which BIND is running + */ + if (namelen == 0) { + namelen = sizeof(username); + if (GetUserName(username, &namelen) == 0) { + return (ISC_R_FAILURE); + } + } + return (NTFS_Access_Control(path, username, access, isdir)); +} + +isc_result_t +isc_fsaccess_set(const char *path, isc_fsaccess_t access) { + struct stat statb; + bool is_dir = false; + isc_result_t result; + + if (stat(path, &statb) != 0) { + return (isc__errno2result(errno)); + } + + if ((statb.st_mode & S_IFDIR) != 0) { + is_dir = true; + } else if ((statb.st_mode & S_IFREG) == 0) { + return (ISC_R_INVALIDFILE); + } + + result = check_bad_bits(access, is_dir); + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* + * Determine if this is a FAT or NTFS disk and + * call the appropriate function to set the permissions + */ + if (is_ntfs(path)) { + return (NTFS_fsaccess_set(path, access, is_dir)); + } else { + return (FAT_fsaccess_set(path, access)); + } +} + +isc_result_t +isc_fsaccess_changeowner(const char *filename, const char *user) { + SECURITY_DESCRIPTOR psd; + BYTE sidBuffer[500]; + BYTE groupBuffer[500]; + PSID psid = (PSID)&sidBuffer; + DWORD sidBufferSize = sizeof(sidBuffer); + char domainBuffer[100]; + DWORD domainBufferSize = sizeof(domainBuffer); + SID_NAME_USE snu; + PSID pSidGroup = (PSID)&groupBuffer; + DWORD groupBufferSize = sizeof(groupBuffer); + + /* + * Determine if this is a FAT or NTFS disk and + * call the appropriate function to set the ownership + * FAT disks do not have ownership attributes so it's + * a noop. + */ + if (is_ntfs(filename) == FALSE) { + return (ISC_R_SUCCESS); + } + + if (!InitializeSecurityDescriptor(&psd, SECURITY_DESCRIPTOR_REVISION)) { + return (ISC_R_NOPERM); + } + + if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, + &domainBufferSize, &snu)) + { + return (ISC_R_NOPERM); + } + + /* Make sure administrators can get to it */ + domainBufferSize = sizeof(domainBuffer); + if (!LookupAccountName(0, "Administrators", pSidGroup, &groupBufferSize, + domainBuffer, &domainBufferSize, &snu)) + { + return (ISC_R_NOPERM); + } + + if (!SetSecurityDescriptorOwner(&psd, psid, FALSE)) { + return (ISC_R_NOPERM); + } + + if (!SetSecurityDescriptorGroup(&psd, pSidGroup, FALSE)) { + return (ISC_R_NOPERM); + } + + if (!SetFileSecurity(filename, + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION, + &psd)) + { + return (ISC_R_NOPERM); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/win32/include/.clang-format b/lib/isc/win32/include/.clang-format new file mode 120000 index 0000000..e919bba --- /dev/null +++ b/lib/isc/win32/include/.clang-format @@ -0,0 +1 @@ +../../../../.clang-format.headers \ No newline at end of file diff --git a/lib/isc/win32/include/Makefile.in b/lib/isc/win32/include/Makefile.in new file mode 100644 index 0000000..60a0a67 --- /dev/null +++ b/lib/isc/win32/include/Makefile.in @@ -0,0 +1,19 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +SUBDIRS = isc +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/win32/include/isc/Makefile.in b/lib/isc/win32/include/isc/Makefile.in new file mode 100644 index 0000000..7718caa --- /dev/null +++ b/lib/isc/win32/include/isc/Makefile.in @@ -0,0 +1,32 @@ +# 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. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +HEADERS = align.h dir.h mutex.h net.h netdb.h once.h \ + stat.h stdtime.h thread.h time.h + +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}\isc + +install:: installdirs + for i in $(HEADERS); do \ + $(INSTALL_DATA) $(srcdir)\$$i $(includedir)\isc || exit 1; \ + done diff --git a/lib/isc/win32/include/isc/align.h b/lib/isc/win32/include/isc/align.h new file mode 100644 index 0000000..6e98665 --- /dev/null +++ b/lib/isc/win32/include/isc/align.h @@ -0,0 +1,15 @@ +/* + * 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. + */ + +#pragma once +#define alignas(x) __declspec(align(x)) diff --git a/lib/isc/win32/include/isc/bind_registry.h b/lib/isc/win32/include/isc/bind_registry.h new file mode 100644 index 0000000..6c997d6 --- /dev/null +++ b/lib/isc/win32/include/isc/bind_registry.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef ISC_BINDREGISTRY_H +#define ISC_BINDREGISTRY_H + +/* + * BIND makes use of the following Registry keys in various places, especially + * during startup and installation + */ + +#define BIND_SUBKEY "Software\\ISC\\BIND" +#define BIND_SESSION "CurrentSession" +#define BIND_SESSION_SUBKEY "Software\\ISC\\BIND\\CurrentSession" +#define BIND_UNINSTALL_SUBKEY \ + "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\ISC BIND" + +#define EVENTLOG_APP_SUBKEY \ + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application" +#define BIND_MESSAGE_SUBKEY \ + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\named" +#define BIND_MESSAGE_NAME "named" + +#define BIND_SERVICE_SUBKEY "SYSTEM\\CurrentControlSet\\Services\\named" + +#define BIND_CONFIGFILE 0 +#define BIND_DEBUGLEVEL 1 +#define BIND_QUERYLOG 2 +#define BIND_FOREGROUND 3 +#define BIND_PORT 4 + +#endif /* ISC_BINDREGISTRY_H */ diff --git a/lib/isc/win32/include/isc/bindevt.h b/lib/isc/win32/include/isc/bindevt.h new file mode 100644 index 0000000..406efe3 --- /dev/null +++ b/lib/isc/win32/include/isc/bindevt.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef ISC_BINDEVT_H +#define ISC_BINDEVT_H 1 + +/* + * This is used for the event log for both logging the messages and + * later on by the event viewer when looking at the events + */ + +/* + * Values are 32 bit values laid out as follows: + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +---+-+-+-----------------------+-------------------------------+ + * |Sev|C|R| Facility | Code | + * +---+-+-+-----------------------+-------------------------------+ + * + * where + * + * Sev - is the severity code + * + * 00 - Success + * 01 - Informational + * 10 - Warning + * 11 - Error + * + * C - is the Customer code flag + * + * R - is a reserved bit + * + * Facility - is the facility code + * + * Code - is the facility's status code + * + * + * Define the facility codes + */ + +/* + * Define the severity codes + */ + +/* + * MessageId: BIND_ERR_MSG + * + * MessageText: + * + * %1 + */ +#define BIND_ERR_MSG ((DWORD)0xC0000001L) + +/* + * MessageId: BIND_WARN_MSG + * + * MessageText: + * + * %1 + */ +#define BIND_WARN_MSG ((DWORD)0x80000002L) + +/* + * MessageId: BIND_INFO_MSG + * + * MessageText: + * + * %1 + */ +#define BIND_INFO_MSG ((DWORD)0x40000003L) + +#endif /* ISC_BINDEVT_H */ diff --git a/lib/isc/win32/include/isc/condition.h b/lib/isc/win32/include/isc/condition.h new file mode 100644 index 0000000..738419c --- /dev/null +++ b/lib/isc/win32/include/isc/condition.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef ISC_CONDITION_H +#define ISC_CONDITION_H 1 + +#include + +#include +#include +#include +#include + +typedef struct isc_condition_thread isc_condition_thread_t; + +struct isc_condition_thread { + uintptr_t th; + HANDLE handle[2]; + ISC_LINK(isc_condition_thread_t) link; +}; + +typedef struct isc_condition { + HANDLE events[2]; + unsigned int waiters; + ISC_LIST(isc_condition_thread_t) threadlist; +} isc_condition_t; + +ISC_LANG_BEGINDECLS + +void +isc_condition_init(isc_condition_t *); + +isc_result_t +isc_condition_wait(isc_condition_t *, isc_mutex_t *); + +isc_result_t +isc_condition_signal(isc_condition_t *); + +isc_result_t +isc_condition_broadcast(isc_condition_t *); + +isc_result_t +isc_condition_destroy(isc_condition_t *); + +isc_result_t +isc_condition_waituntil(isc_condition_t *, isc_mutex_t *, isc_time_t *); + +ISC_LANG_ENDDECLS + +#endif /* ISC_CONDITION_H */ diff --git a/lib/isc/win32/include/isc/dir.h b/lib/isc/win32/include/isc/dir.h new file mode 100644 index 0000000..f0050dd --- /dev/null +++ b/lib/isc/win32/include/isc/dir.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef ISC_DIR_H +#define ISC_DIR_H 1 + +#include +#include +#include + +#include +#include +#include + +typedef struct { + char name[NAME_MAX]; + unsigned int length; + WIN32_FIND_DATA find_data; +} isc_direntry_t; + +typedef struct { + unsigned int magic; + char dirname[PATH_MAX]; + isc_direntry_t entry; + bool entry_filled; + HANDLE search_handle; +} isc_dir_t; + +ISC_LANG_BEGINDECLS + +void +isc_dir_init(isc_dir_t *dir); + +isc_result_t +isc_dir_open(isc_dir_t *dir, const char *dirname); + +isc_result_t +isc_dir_read(isc_dir_t *dir); + +isc_result_t +isc_dir_reset(isc_dir_t *dir); + +void +isc_dir_close(isc_dir_t *dir); + +isc_result_t +isc_dir_chdir(const char *dirname); + +isc_result_t +isc_dir_chroot(const char *dirname); + +isc_result_t +isc_dir_createunique(char *templet); +/* + * Use a templet (such as from isc_file_mktemplate()) to create a uniquely + * named, empty directory. The templet string is modified in place. + * If result == ISC_R_SUCCESS, it is the name of the directory that was + * created. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_DIR_H */ diff --git a/lib/isc/win32/include/isc/ipv6.h b/lib/isc/win32/include/isc/ipv6.h new file mode 100644 index 0000000..4ab567a --- /dev/null +++ b/lib/isc/win32/include/isc/ipv6.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#ifndef ISC_IPV6_H +#define ISC_IPV6_H 1 + +/***** +***** Module Info +*****/ + +/* + * IPv6 definitions for systems which do not support IPv6. + * + * MP: + * No impact. + * + * Reliability: + * No anticipated impact. + * + * Resources: + * N/A. + * + * Security: + * No anticipated impact. + * + * Standards: + * RFC2553. + */ + +#if _MSC_VER < 1300 +#define in6_addr in_addr6 +#endif /* if _MSC_VER < 1300 */ + +#ifndef IN6ADDR_ANY_INIT +#define IN6ADDR_ANY_INIT \ + { \ + { \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ + } \ + } +#endif /* ifndef IN6ADDR_ANY_INIT */ +#ifndef IN6ADDR_LOOPBACK_INIT +#define IN6ADDR_LOOPBACK_INIT \ + { \ + { \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 \ + } \ + } +#endif /* ifndef IN6ADDR_LOOPBACK_INIT */ +#ifndef IN6ADDR_V4MAPPED_INIT +#define IN6ADDR_V4MAPPED_INIT \ + { \ + { \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0 \ + } \ + } +#endif /* ifndef IN6ADDR_V4MAPPED_INIT */ + +LIBISC_EXTERNAL_DATA extern const struct in6_addr isc_in6addr_any; +LIBISC_EXTERNAL_DATA extern const struct in6_addr isc_in6addr_loopback; + +/* + * Unspecified + */ +#ifndef IN6_IS_ADDR_UNSPECIFIED +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + (*((u_long *)((a)->s6_addr)) == 0 && \ + *((u_long *)((a)->s6_addr) + 1) == 0 && \ + *((u_long *)((a)->s6_addr) + 2) == 0 && \ + *((u_long *)((a)->s6_addr) + 3) == 0) +#endif /* ifndef IN6_IS_ADDR_UNSPECIFIED */ + +/* + * Loopback + */ +#ifndef IN6_IS_ADDR_LOOPBACK +#define IN6_IS_ADDR_LOOPBACK(a) \ + (*((u_long *)((a)->s6_addr)) == 0 && \ + *((u_long *)((a)->s6_addr) + 1) == 0 && \ + *((u_long *)((a)->s6_addr) + 2) == 0 && \ + *((u_long *)((a)->s6_addr) + 3) == htonl(1)) +#endif /* ifndef IN6_IS_ADDR_LOOPBACK */ + +/* + * IPv4 compatible + */ +#define IN6_IS_ADDR_V4COMPAT(a) \ + (*((u_long *)((a)->s6_addr)) == 0 && \ + *((u_long *)((a)->s6_addr) + 1) == 0 && \ + *((u_long *)((a)->s6_addr) + 2) == 0 && \ + *((u_long *)((a)->s6_addr) + 3) != 0 && \ + *((u_long *)((a)->s6_addr) + 3) != htonl(1)) + +/* + * Mapped + */ +#define IN6_IS_ADDR_V4MAPPED(a) \ + (*((u_long *)((a)->s6_addr)) == 0 && \ + *((u_long *)((a)->s6_addr) + 1) == 0 && \ + *((u_long *)((a)->s6_addr) + 2) == htonl(0x0000ffff)) + +/* + * Multicast + */ +#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xffU) + +/* + * Unicast link / site local. + */ +#ifndef IN6_IS_ADDR_LINKLOCAL +#define IN6_IS_ADDR_LINKLOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#endif /* ifndef IN6_IS_ADDR_LINKLOCAL */ + +#ifndef IN6_IS_ADDR_SITELOCAL +#define IN6_IS_ADDR_SITELOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) +#endif /* ifndef IN6_IS_ADDR_SITELOCAL */ + +#endif /* ISC_IPV6_H */ diff --git a/lib/isc/win32/include/isc/mutex.h b/lib/isc/win32/include/isc/mutex.h new file mode 100644 index 0000000..3fcc538 --- /dev/null +++ b/lib/isc/win32/include/isc/mutex.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef ISC_MUTEX_H +#define ISC_MUTEX_H 1 + +#include + +#include +#include + +typedef CRITICAL_SECTION isc_mutex_t; + +/* + * This definition is here since some versions of WINBASE.H + * omits it for some reason. + */ +#if (_WIN32_WINNT < 0x0400) +WINBASEAPI BOOL WINAPI +TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); +#endif /* _WIN32_WINNT < 0x0400 */ + +#define isc_mutex_init(mp) InitializeCriticalSection((mp)) +#define isc_mutex_lock(mp) (EnterCriticalSection((mp)), ISC_R_SUCCESS) +#define isc_mutex_unlock(mp) (LeaveCriticalSection((mp)), ISC_R_SUCCESS) +#define isc_mutex_trylock(mp) \ + (TryEnterCriticalSection((mp)) ? ISC_R_SUCCESS : ISC_R_LOCKBUSY) +#define isc_mutex_destroy(mp) (DeleteCriticalSection((mp))) + +/* + * This is a placeholder for now since we are not keeping any mutex stats + */ +#define isc_mutex_stats(fp) \ + do { \ + } while (0) + +#endif /* ISC_MUTEX_H */ diff --git a/lib/isc/win32/include/isc/net.h b/lib/isc/win32/include/isc/net.h new file mode 100644 index 0000000..391614e --- /dev/null +++ b/lib/isc/win32/include/isc/net.h @@ -0,0 +1,402 @@ +/* + * 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. + */ + +#ifndef ISC_NET_H +#define ISC_NET_H 1 + +/***** +***** Module Info +*****/ + +/* + * Basic Networking Types + * + * This module is responsible for defining the following basic networking + * types: + * + * struct in_addr + * struct in6_addr + * struct in6_pktinfo + * struct sockaddr + * struct sockaddr_in + * struct sockaddr_in6 + * in_port_t + * + * It ensures that the AF_ and PF_ macros are defined. + * + * It declares ntoh[sl]() and hton[sl](). + * + * It declares inet_ntop(), and inet_pton(). + * + * It ensures that INADDR_ANY, IN6ADDR_ANY_INIT, in6addr_any, and + * in6addr_loopback are available. + * + * It ensures that IN_MULTICAST() is available to check for multicast + * addresses. + * + * MP: + * No impact. + * + * Reliability: + * No anticipated impact. + * + * Resources: + * N/A. + * + * Security: + * No anticipated impact. + * + * Standards: + * BSD Socket API + * RFC2553 + */ + +/*** + *** Imports. + ***/ +#include + +#include + +/* + * Because of some sort of problem in the MS header files, this cannot + * be simple "#include ", because winsock2.h tries to include + * windows.h, which then generates an error out of mswsock.h. _You_ + * figure it out. + */ +#ifndef _WINSOCKAPI_ +#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ +#endif /* ifndef _WINSOCKAPI_ */ + +#include +#include + +#include +#include +#include + +#include + +/* + * This is here because named client, interfacemgr.c, etc. use the name as + * a variable + */ +#undef interface + +#ifndef INADDR_ANY +#define INADDR_ANY 0x00000000UL +#endif /* ifndef INADDR_ANY */ + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001UL +#endif /* ifndef INADDR_LOOPBACK */ + +#if _MSC_VER < 1300 +#define in6addr_any isc_in6addr_any +#define in6addr_loopback isc_in6addr_loopback +#endif /* if _MSC_VER < 1300 */ + +/* + * Ensure type in_port_t is defined. + */ +typedef uint16_t in_port_t; + +/* + * If this system does not have MSG_TRUNC (as returned from recvmsg()) + * ISC_PLATFORM_RECVOVERFLOW will be defined. This will enable the MSG_TRUNC + * faking code in socket.c. + */ +#ifndef MSG_TRUNC +#define ISC_PLATFORM_RECVOVERFLOW +#endif /* ifndef MSG_TRUNC */ + +#define ISC__IPADDR(x) ((uint32_t)htonl((uint32_t)(x))) + +#define ISC_IPADDR_ISMULTICAST(i) \ + (((uint32_t)(i)&ISC__IPADDR(0xf0000000)) == ISC__IPADDR(0xe0000000)) + +#define ISC_IPADDR_ISEXPERIMENTAL(i) \ + (((uint32_t)(i)&ISC__IPADDR(0xf0000000)) == ISC__IPADDR(0xf0000000)) + +/* + * Fix the FD_SET and FD_CLR Macros to properly cast + */ +#undef FD_CLR +#define FD_CLR(fd, set) \ + do { \ + u_int __i; \ + for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \ + if (((fd_set FAR *)(set))->fd_array[__i] == \ + (SOCKET)fd) { \ + while (__i < \ + ((fd_set FAR *)(set))->fd_count - 1) \ + { \ + ((fd_set FAR *)(set))->fd_array[__i] = \ + ((fd_set FAR *)(set)) \ + ->fd_array[__i + 1]; \ + __i++; \ + } \ + ((fd_set FAR *)(set))->fd_count--; \ + break; \ + } \ + } \ + } while (0) + +#undef FD_SET +#define FD_SET(fd, set) \ + do { \ + u_int __i; \ + for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \ + if (((fd_set FAR *)(set))->fd_array[__i] == \ + (SOCKET)(fd)) \ + { \ + break; \ + } \ + } \ + if (__i == ((fd_set FAR *)(set))->fd_count) { \ + if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \ + ((fd_set FAR *)(set))->fd_array[__i] = \ + (SOCKET)(fd); \ + ((fd_set FAR *)(set))->fd_count++; \ + } \ + } \ + } while (0) + +/* + * Windows Sockets errors redefined as regular Berkeley error constants. + * These are usually commented out in Windows NT to avoid conflicts with + * errno.h. Use the WSA constants instead. + */ + +#include + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif /* ifndef EWOULDBLOCK */ +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif /* ifndef EINPROGRESS */ +#ifndef EALREADY +#define EALREADY WSAEALREADY +#endif /* ifndef EALREADY */ +#ifndef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#endif /* ifndef ENOTSOCK */ +#ifndef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#endif /* ifndef EDESTADDRREQ */ +#ifndef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#endif /* ifndef EMSGSIZE */ +#ifndef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#endif /* ifndef EPROTOTYPE */ +#ifndef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#endif /* ifndef ENOPROTOOPT */ +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif /* ifndef EPROTONOSUPPORT */ +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif /* ifndef ESOCKTNOSUPPORT */ +#ifndef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#endif /* ifndef EOPNOTSUPP */ +#ifndef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif /* ifndef EPFNOSUPPORT */ +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif /* ifndef EAFNOSUPPORT */ +#ifndef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#endif /* ifndef EADDRINUSE */ +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif /* ifndef EADDRNOTAVAIL */ +#ifndef ENETDOWN +#define ENETDOWN WSAENETDOWN +#endif /* ifndef ENETDOWN */ +#ifndef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#endif /* ifndef ENETUNREACH */ +#ifndef ENETRESET +#define ENETRESET WSAENETRESET +#endif /* ifndef ENETRESET */ +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif /* ifndef ECONNABORTED */ +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif /* ifndef ECONNRESET */ +#ifndef ENOBUFS +#define ENOBUFS WSAENOBUFS +#endif /* ifndef ENOBUFS */ +#ifndef EISCONN +#define EISCONN WSAEISCONN +#endif /* ifndef EISCONN */ +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif /* ifndef ENOTCONN */ +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif /* ifndef ESHUTDOWN */ +#ifndef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#endif /* ifndef ETOOMANYREFS */ +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif /* ifndef ETIMEDOUT */ +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif /* ifndef ECONNREFUSED */ +#ifndef ELOOP +#define ELOOP WSAELOOP +#endif /* ifndef ELOOP */ +#ifndef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#endif /* ifndef EHOSTDOWN */ +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif /* ifndef EHOSTUNREACH */ +#ifndef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#endif /* ifndef EPROCLIM */ +#ifndef EUSERS +#define EUSERS WSAEUSERS +#endif /* ifndef EUSERS */ +#ifndef EDQUOT +#define EDQUOT WSAEDQUOT +#endif /* ifndef EDQUOT */ +#ifndef ESTALE +#define ESTALE WSAESTALE +#endif /* ifndef ESTALE */ +#ifndef EREMOTE +#define EREMOTE WSAEREMOTE +#endif /* ifndef EREMOTE */ + +/*** + *** Functions. + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_net_probeipv4(void); +/* + * Check if the system's kernel supports IPv4. + * + * Returns: + * + * ISC_R_SUCCESS IPv4 is supported. + * ISC_R_NOTFOUND IPv4 is not supported. + * ISC_R_DISABLED IPv4 is disabled. + * ISC_R_UNEXPECTED + */ + +isc_result_t +isc_net_probeipv6(void); +/* + * Check if the system's kernel supports IPv6. + * + * Returns: + * + * ISC_R_SUCCESS IPv6 is supported. + * ISC_R_NOTFOUND IPv6 is not supported. + * ISC_R_DISABLED IPv6 is disabled. + * ISC_R_UNEXPECTED + */ + +isc_result_t +isc_net_probeunix(void); +/* + * Check if UNIX domain sockets are supported. + * + * Returns: + * + * ISC_R_SUCCESS + * ISC_R_NOTFOUND + */ + +#define ISC_NET_DSCPRECVV4 0x01 /* Can receive sent DSCP value IPv4 */ +#define ISC_NET_DSCPRECVV6 0x02 /* Can receive sent DSCP value IPv6 */ +#define ISC_NET_DSCPSETV4 0x04 /* Can set DSCP on socket IPv4 */ +#define ISC_NET_DSCPSETV6 0x08 /* Can set DSCP on socket IPv6 */ +#define ISC_NET_DSCPPKTV4 0x10 /* Can set DSCP on per packet IPv4 */ +#define ISC_NET_DSCPPKTV6 0x20 /* Can set DSCP on per packet IPv6 */ +#define ISC_NET_DSCPALL 0x3f /* All valid flags */ + +unsigned int +isc_net_probedscp(void); +/*%< + * Probe the level of DSCP support. + */ + +isc_result_t +isc_net_probe_ipv6only(void); +/* + * Check if the system's kernel supports the IPV6_V6ONLY socket option. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported for both TCP and UDP. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + +isc_result_t +isc_net_probe_ipv6pktinfo(void); +/* + * Check if the system's kernel supports the IPV6_(RECV)PKTINFO socket option + * for UDP sockets. + * + * Returns: + * + * ISC_R_SUCCESS the option is supported. + * ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * ISC_R_UNEXPECTED + */ + +void +isc_net_disableipv4(void); + +void +isc_net_disableipv6(void); + +void +isc_net_enableipv4(void); + +void +isc_net_enableipv6(void); + +isc_result_t +isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high); +/*%< + * Returns system's default range of ephemeral UDP ports, if defined. + * If the range is not available or unknown, ISC_NET_PORTRANGELOW and + * ISC_NET_PORTRANGEHIGH will be returned. + * + * Requires: + * + *\li 'low' and 'high' must be non NULL. + * + * Returns: + * + *\li *low and *high will be the ports specifying the low and high ends of + * the range. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_NET_H */ diff --git a/lib/isc/win32/include/isc/netdb.h b/lib/isc/win32/include/isc/netdb.h new file mode 100644 index 0000000..e37ecb5 --- /dev/null +++ b/lib/isc/win32/include/isc/netdb.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ISC_NETDB_H +#define ISC_NETDB_H 1 + +/***** +***** Module Info +*****/ + +/* + * Portable netdb.h support. + * + * This module is responsible for defining the getby APIs. + * + * MP: + * No impact. + * + * Reliability: + * No anticipated impact. + * + * Resources: + * N/A. + * + * Security: + * No anticipated impact. + * + * Standards: + * BSD API + */ + +/*** + *** Imports. + ***/ + +#include + +#endif /* ISC_NETDB_H */ diff --git a/lib/isc/win32/include/isc/ntgroups.h b/lib/isc/win32/include/isc/ntgroups.h new file mode 100644 index 0000000..496f84a --- /dev/null +++ b/lib/isc/win32/include/isc/ntgroups.h @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#ifndef ISC_NTGROUPS_H +#define ISC_NTGROUPS_H 1 + +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_ntsecurity_getaccountgroups(char *name, char **Groups, + unsigned int maxgroups, unsigned int *total); + +ISC_LANG_ENDDECLS + +#endif /* ISC_NTGROUPS_H */ diff --git a/lib/isc/win32/include/isc/ntpaths.h b/lib/isc/win32/include/isc/ntpaths.h new file mode 100644 index 0000000..37c915d --- /dev/null +++ b/lib/isc/win32/include/isc/ntpaths.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +/* + * Windows-specific path definitions + * These routines are used to set up and return system-specific path + * information about the files enumerated in NtPaths + */ + +#ifndef ISC_NTPATHS_H +#define ISC_NTPATHS_H + +#include + +/* + * Index of paths needed + */ +enum NtPaths { + NAMED_CONF_PATH, + RESOLV_CONF_PATH, + RNDC_CONF_PATH, + NAMED_PID_PATH, + NAMED_LOCK_PATH, + LOCAL_STATE_DIR, + SYS_CONF_DIR, + RNDC_KEY_PATH, + SESSION_KEY_PATH, + BIND_KEYS_PATH +}; + +/* + * Define macros to get the path of the config files + */ +#define NAMED_CONFFILE isc_ntpaths_get(NAMED_CONF_PATH) +#define RNDC_CONFFILE isc_ntpaths_get(RNDC_CONF_PATH) +#define RNDC_KEYFILE isc_ntpaths_get(RNDC_KEY_PATH) +#define SESSION_KEYFILE isc_ntpaths_get(SESSION_KEY_PATH) +#define RESOLV_CONF isc_ntpaths_get(RESOLV_CONF_PATH) + +/* + * Information about where the files are on disk + */ +#define NAMED_LOCALSTATEDIR "/dns/bin" +#define NAMED_SYSCONFDIR "/dns/etc" + +ISC_LANG_BEGINDECLS + +void +isc_ntpaths_init(void); + +char * +isc_ntpaths_get(int); + +ISC_LANG_ENDDECLS + +#endif /* ISC_NTPATHS_H */ diff --git a/lib/isc/win32/include/isc/offset.h b/lib/isc/win32/include/isc/offset.h new file mode 100644 index 0000000..f09cfdb --- /dev/null +++ b/lib/isc/win32/include/isc/offset.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#ifndef ISC_OFFSET_H +#define ISC_OFFSET_H 1 + +/* + * File offsets are operating-system dependent. + */ +#include /* Required for CHAR_BIT. */ + +#include + +typedef _off_t isc_offset_t; + +#endif /* ISC_OFFSET_H */ diff --git a/lib/isc/win32/include/isc/once.h b/lib/isc/win32/include/isc/once.h new file mode 100644 index 0000000..d2d841f --- /dev/null +++ b/lib/isc/win32/include/isc/once.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ISC_ONCE_H +#define ISC_ONCE_H 1 + +#include +#include + +ISC_LANG_BEGINDECLS + +typedef struct { + int status; + int counter; +} isc_once_t; + +#define ISC_ONCE_INIT_NEEDED 0 +#define ISC_ONCE_INIT_DONE 1 + +#define ISC_ONCE_INIT \ + { \ + ISC_ONCE_INIT_NEEDED, 1 \ + } + +isc_result_t +isc_once_do(isc_once_t *controller, void (*function)(void)); + +ISC_LANG_ENDDECLS + +#endif /* ISC_ONCE_H */ diff --git a/lib/isc/win32/include/isc/platform.h.in b/lib/isc/win32/include/isc/platform.h.in new file mode 100644 index 0000000..b3fad9c --- /dev/null +++ b/lib/isc/win32/include/isc/platform.h.in @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#ifndef ISC_PLATFORM_H +#define ISC_PLATFORM_H 1 + +/***** + ***** Platform-dependent defines. + *****/ + +#if _MSC_VER > 1400 +#define HAVE_TLS 1 +#define HAVE___DECLSPEC_THREAD 1 +#endif + +/* + * Some compatibility cludges + */ + +#if defined(_WIN32) || defined(_WIN64) +/* We are on Windows */ +# define strtok_r strtok_s + +#define ISC_STRERRORSIZE 128 + +#ifndef strtoull +#define strtoull _strtoui64 +#endif + +#include +#if _MSC_VER < 1914 +typedef uint32_t socklen_t; +#endif + +#endif + +#define __builtin_unreachable() __assume(0) + +/* + * Remove __attribute__ ((foo)) on Windows + */ + +#define __attribute__(attribute) /* do nothing */ + +/* + * Limits + */ + +#ifndef NAME_MAX +#define NAME_MAX _MAX_FNAME +#endif + +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif + +/*** + *** Network. + ***/ + +#undef MSG_TRUNC + +typedef uint16_t sa_family_t; + +/* + * Define if the platform has . + */ +#undef ISC_PLATFORM_HAVESYSUNH + +/* + * Defines for the noreturn attribute. + */ +#define ISC_PLATFORM_NORETURN_PRE __declspec(noreturn) +#define ISC_PLATFORM_NORETURN_POST + +/* + * Set up a macro for importing and exporting from the DLL + */ + +#ifdef LIBISC_EXPORTS +#define LIBISC_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBISC_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBDNS_EXPORTS +#define LIBDNS_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBDNS_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBISCCC_EXPORTS +#define LIBISCCC_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBISCCC_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBISCCFG_EXPORTS +#define LIBISCCFG_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBISCCFG_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBNS_EXPORTS +#define LIBNS_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBNS_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBBIND9_EXPORTS +#define LIBBIND9_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBBIND9_EXTERNAL_DATA __declspec(dllimport) +#endif + +#ifdef LIBTESTS_EXPORTS +#define LIBTESTS_EXTERNAL_DATA __declspec(dllexport) +#else +#define LIBTESTS_EXTERNAL_DATA __declspec(dllimport) +#endif + +#endif /* ISC_PLATFORM_H */ diff --git a/lib/isc/win32/include/isc/stat.h b/lib/isc/win32/include/isc/stat.h new file mode 100644 index 0000000..63577f9 --- /dev/null +++ b/lib/isc/win32/include/isc/stat.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef ISC_STAT_H +#define ISC_STAT_H 1 + +#include + +/* + * Windows doesn't typedef this. + */ +typedef unsigned short mode_t; + +/* open() under unix allows setting of read/write permissions + * at the owner, group and other levels. These don't exist in NT + * We'll just map them all to the NT equivalent + */ + +#define S_IREAD _S_IREAD /* read permission, owner */ +#define S_IWRITE _S_IWRITE /* write permission, owner */ +#define S_IRUSR _S_IREAD /* Owner read permission */ +#define S_IWUSR _S_IWRITE /* Owner write permission */ +#define S_IRGRP _S_IREAD /* Group read permission */ +#define S_IWGRP _S_IWRITE /* Group write permission */ +#define S_IROTH _S_IREAD /* Other read permission */ +#define S_IWOTH _S_IWRITE /* Other write permission */ + +#ifndef S_IFMT +#define S_IFMT _S_IFMT +#endif /* ifndef S_IFMT */ +#ifndef S_IFDIR +#define S_IFDIR _S_IFDIR +#endif /* ifndef S_IFDIR */ +#ifndef S_IFCHR +#define S_IFCHR _S_IFCHR +#endif /* ifndef S_IFCHR */ +#ifndef S_IFREG +#define S_IFREG _S_IFREG +#endif /* ifndef S_IFREG */ + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif /* ifndef S_ISDIR */ +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) +#endif /* ifndef S_ISREG */ + +#endif /* ISC_STAT_H */ diff --git a/lib/isc/win32/include/isc/stdatomic.h b/lib/isc/win32/include/isc/stdatomic.h new file mode 100644 index 0000000..dd5293f --- /dev/null +++ b/lib/isc/win32/include/isc/stdatomic.h @@ -0,0 +1,595 @@ +/* + * 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. + */ + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include + +#pragma warning(disable : 4133) +#pragma warning(disable : 4090) + +#define InterlockedExchangeAdd8 _InterlockedExchangeAdd8 +#define InterlockedCompareExchange8 _InterlockedCompareExchange8 + +#pragma intrinsic(_InterlockedCompareExchange8, _InterlockedExchangeAdd8) + +#include + +#ifndef __ATOMIC_RELAXED +#define __ATOMIC_RELAXED 0 +#endif /* ifndef __ATOMIC_RELAXED */ +#ifndef __ATOMIC_CONSUME +#define __ATOMIC_CONSUME 1 +#endif /* ifndef __ATOMIC_CONSUME */ +#ifndef __ATOMIC_ACQUIRE +#define __ATOMIC_ACQUIRE 2 +#endif /* ifndef __ATOMIC_ACQUIRE */ +#ifndef __ATOMIC_RELEASE +#define __ATOMIC_RELEASE 3 +#endif /* ifndef __ATOMIC_RELEASE */ +#ifndef __ATOMIC_ACQ_REL +#define __ATOMIC_ACQ_REL 4 +#endif /* ifndef __ATOMIC_ACQ_REL */ +#ifndef __ATOMIC_SEQ_CST +#define __ATOMIC_SEQ_CST 5 +#endif /* ifndef __ATOMIC_SEQ_CST */ + +enum memory_order { + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +}; + +typedef enum memory_order memory_order; + +/* + * If you add a type with different sizeof() length, + * you need to implement atomic__explicitNN macros. + */ + +typedef bool volatile atomic_bool; +typedef char volatile atomic_char; +typedef signed char volatile atomic_schar; +typedef unsigned char volatile atomic_uchar; +typedef short volatile atomic_short; +typedef unsigned short volatile atomic_ushort; +typedef int volatile atomic_int; +typedef unsigned int volatile atomic_uint; +typedef long volatile atomic_long; +typedef unsigned long volatile atomic_ulong; +typedef long long volatile atomic_llong; +typedef unsigned long long volatile atomic_ullong; +typedef char16_t volatile atomic_char16_t; +typedef char32_t volatile atomic_char32_t; +typedef wchar_t volatile atomic_wchar_t; +typedef int_least8_t volatile atomic_int_least8_t; +typedef uint_least8_t volatile atomic_uint_least8_t; +typedef int_least16_t volatile atomic_int_least16_t; +typedef uint_least16_t volatile atomic_uint_least16_t; +typedef int_least32_t volatile atomic_int_least32_t; +typedef uint_least32_t volatile atomic_uint_least32_t; +typedef int_least64_t volatile atomic_int_least64_t; +typedef uint_least64_t volatile atomic_uint_least64_t; +typedef int_fast8_t volatile atomic_int_fast8_t; +typedef uint_fast8_t volatile atomic_uint_fast8_t; +typedef int_fast16_t volatile atomic_int_fast16_t; +typedef uint_fast16_t volatile atomic_uint_fast16_t; +typedef int_fast32_t volatile atomic_int_fast32_t; +typedef uint_fast32_t volatile atomic_uint_fast32_t; +typedef int_fast64_t volatile atomic_int_fast64_t; +typedef uint_fast64_t volatile atomic_uint_fast64_t; +typedef intptr_t volatile atomic_intptr_t; +typedef uintptr_t volatile atomic_uintptr_t; +typedef size_t volatile atomic_size_t; +typedef ptrdiff_t volatile atomic_ptrdiff_t; +typedef intmax_t volatile atomic_intmax_t; +typedef uintmax_t volatile atomic_uintmax_t; + +#define atomic_init(obj, desired) (*(obj) = (desired)) + +#define atomic_store_explicit8(obj, desired, order) \ + (void)InterlockedExchange8((atomic_int_fast8_t *)obj, desired) + +#define atomic_store_explicit16(obj, desired, order) \ + (order == memory_order_relaxed \ + ? (void)InterlockedExchangeNoFence16((atomic_short *)obj, \ + desired) \ + : (order == memory_order_acquire \ + ? (void)InterlockedExchangeAcquire16( \ + (atomic_short *)obj, desired) \ + : (void)InterlockedExchange16((atomic_short *)obj, \ + desired))) + +#define atomic_store_explicit32(obj, desired, order) \ + (order == memory_order_relaxed \ + ? (void)InterlockedExchangeNoFence( \ + (atomic_int_fast32_t *)obj, desired) \ + : (order == memory_order_acquire \ + ? (void)InterlockedExchangeAcquire( \ + (atomic_int_fast32_t *)obj, desired) \ + : (void)InterlockedExchange( \ + (atomic_int_fast32_t *)obj, desired))) + +#ifdef _WIN64 +#define atomic_store_explicit64(obj, desired, order) \ + (order == memory_order_relaxed \ + ? (void)InterlockedExchangeNoFence64( \ + (atomic_int_fast64_t *)obj, desired) \ + : (order == memory_order_acquire \ + ? (void)InterlockedExchangeAcquire64( \ + (atomic_int_fast64_t *)obj, desired) \ + : (void)InterlockedExchange64( \ + (atomic_int_fast64_t *)obj, desired))) +#else /* ifdef _WIN64 */ +#define atomic_store_explicit64(obj, desired, order) \ + (void)InterlockedExchange64((atomic_int_fast64_t *)obj, desired) +#endif /* ifdef _WIN64 */ + +static inline void +atomic_store_abort() { + UNREACHABLE(); +} + +#define atomic_store_explicit(obj, desired, order) \ + (sizeof(*(obj)) == 8 \ + ? atomic_store_explicit64(obj, desired, order) \ + : (sizeof(*(obj)) == 4 \ + ? atomic_store_explicit32(obj, desired, order) \ + : (sizeof(*(obj)) == 2 \ + ? atomic_store_explicit16(obj, desired, \ + order) \ + : (sizeof(*(obj)) == 1 \ + ? atomic_store_explicit8( \ + obj, desired, \ + order) \ + : atomic_store_abort())))) + +#define atomic_store(obj, desired) \ + atomic_store_explicit(obj, desired, memory_order_seq_cst) + +#define atomic_load_explicit8(obj, order) \ + (int8_t) InterlockedOr8((atomic_int_fast8_t *)obj, 0) + +#define atomic_load_explicit16(obj, order) \ + (short)InterlockedOr16((atomic_short *)obj, 0) + +#define atomic_load_explicit32(obj, order) \ + (order == memory_order_relaxed \ + ? (int32_t)InterlockedOrNoFence((atomic_int_fast32_t *)obj, \ + 0) \ + : (order == memory_order_acquire \ + ? (int32_t)InterlockedOrAcquire( \ + (atomic_int_fast32_t *)obj, 0) \ + : (order == memory_order_release \ + ? (int32_t)InterlockedOrRelease( \ + (atomic_int_fast32_t *)obj, 0) \ + : (int32_t)InterlockedOr( \ + (atomic_int_fast32_t *)obj, \ + 0)))) + +#ifdef _WIN64 +#define atomic_load_explicit64(obj, order) \ + (order == memory_order_relaxed \ + ? InterlockedOr64NoFence((atomic_int_fast64_t *)obj, 0) \ + : (order == memory_order_acquire \ + ? InterlockedOr64Acquire( \ + (atomic_int_fast64_t *)obj, 0) \ + : (order == memory_order_release \ + ? InterlockedOr64Release( \ + (atomic_int_fast64_t *)obj, 0) \ + : InterlockedOr64( \ + (atomic_int_fast64_t *)obj, \ + 0)))) +#else /* ifdef _WIN64 */ +#define atomic_load_explicit64(obj, order) \ + InterlockedOr64((atomic_int_fast64_t *)obj, 0) +#endif /* ifdef _WIN64 */ + +static inline int8_t +atomic_load_abort() { + UNREACHABLE(); +} + +#define atomic_load_explicit(obj, order) \ + (((sizeof(*(obj)) == 8) \ + ? atomic_load_explicit64(obj, order) \ + : ((sizeof(*(obj)) == 4) \ + ? atomic_load_explicit32(obj, order) \ + : ((sizeof(*(obj)) == 2) \ + ? atomic_load_explicit16(obj, order) \ + : ((sizeof(*(obj)) == 1) \ + ? atomic_load_explicit8( \ + obj, order) \ + : atomic_load_abort())))) & \ + ((sizeof(*(obj)) == 8) \ + ? 0xffffffffffffffffULL \ + : ((sizeof(*(obj)) == 4) \ + ? 0xffffffffULL \ + : ((sizeof(*(obj)) == 2) \ + ? 0xffffULL \ + : ((sizeof(*(obj)) == 1) \ + ? 0xffULL \ + : atomic_load_abort()))))) + +#define atomic_load(obj) atomic_load_explicit(obj, memory_order_seq_cst) + +#define atomic_fetch_add_explicit8(obj, arg, order) \ + InterlockedExchangeAdd8((atomic_int_fast8_t *)obj, arg) + +#define atomic_fetch_add_explicit16(obj, arg, order) \ + InterlockedExchangeAdd16((atomic_short *)obj, arg) + +#define atomic_fetch_add_explicit32(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedExchangeAddNoFence((atomic_int_fast32_t *)obj, \ + arg) \ + : (order == memory_order_acquire \ + ? InterlockedExchangeAddAcquire( \ + (atomic_int_fast32_t *)obj, arg) \ + : (order == memory_order_release \ + ? InterlockedExchangeAddRelease( \ + (atomic_int_fast32_t *)obj, \ + arg) \ + : InterlockedExchangeAdd( \ + (atomic_int_fast32_t *)obj, \ + arg)))) + +#ifdef _WIN64 +#define atomic_fetch_add_explicit64(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedExchangeAddNoFence64((atomic_int_fast64_t *)obj, \ + arg) \ + : (order == memory_order_acquire \ + ? InterlockedExchangeAddAcquire64( \ + (atomic_int_fast64_t *)obj, arg) \ + : (order == memory_order_release \ + ? InterlockedExchangeAddRelease64( \ + (atomic_int_fast64_t *)obj, \ + arg) \ + : InterlockedExchangeAdd64( \ + (atomic_int_fast64_t *)obj, \ + arg)))) +#else /* ifdef _WIN64 */ +#define atomic_fetch_add_explicit64(obj, arg, order) \ + InterlockedExchangeAdd64((atomic_int_fast64_t *)obj, arg) +#endif /* ifdef _WIN64 */ + +static inline int8_t +atomic_add_abort() { + UNREACHABLE(); +} + +#define atomic_fetch_add_explicit(obj, arg, order) \ + (sizeof(*(obj)) == 8 \ + ? atomic_fetch_add_explicit64(obj, arg, order) \ + : (sizeof(*(obj)) == 4 \ + ? atomic_fetch_add_explicit32(obj, arg, order) \ + : (sizeof(*(obj)) == 2 \ + ? atomic_fetch_add_explicit16(obj, arg, \ + order) \ + : (sizeof(*(obj)) == 1 \ + ? atomic_fetch_add_explicit8( \ + obj, arg, order) \ + : atomic_add_abort())))) + +#define atomic_fetch_add(obj, arg) \ + atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_sub_explicit(obj, arg, order) \ + atomic_fetch_add_explicit(obj, -arg, order) + +#define atomic_fetch_sub(obj, arg) \ + atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_and_explicit8(obj, arg, order) \ + InterlockedAnd8((atomic_int_fast8_t *)obj, arg) + +#define atomic_fetch_and_explicit16(obj, arg, order) \ + InterlockedAnd16((atomic_short *)obj, arg) + +#define atomic_fetch_and_explicit32(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedAndNoFence((atomic_int_fast32_t *)obj, arg) \ + : (order == memory_order_acquire \ + ? InterlockedAndAcquire( \ + (atomic_int_fast32_t *)obj, arg) \ + : (order == memory_order_release \ + ? InterlockedAndRelease( \ + (atomic_int_fast32_t *)obj, \ + arg) \ + : InterlockedAnd( \ + (atomic_int_fast32_t *)obj, \ + arg)))) + +#ifdef _WIN64 +#define atomic_fetch_and_explicit64(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedAnd64NoFence((atomic_int_fast64_t *)obj, arg) \ + : (order == memory_order_acquire \ + ? InterlockedAnd64Acquire( \ + (atomic_int_fast64_t *)obj, arg) \ + : (order == memory_order_release \ + ? InterlockedAnd64Release( \ + (atomic_int_fast64_t *)obj, \ + arg) \ + : InterlockedAnd64( \ + (atomic_int_fast64_t *)obj, \ + arg)))) +#else /* ifdef _WIN64 */ +#define atomic_fetch_and_explicit64(obj, arg, order) \ + InterlockedAnd64((atomic_int_fast64_t *)obj, arg) +#endif /* ifdef _WIN64 */ + +static inline int8_t +atomic_and_abort() { + UNREACHABLE(); +} + +#define atomic_fetch_and_explicit(obj, arg, order) \ + (sizeof(*(obj)) == 8 \ + ? atomic_fetch_and_explicit64(obj, arg, order) \ + : (sizeof(*(obj)) == 4 \ + ? atomic_fetch_and_explicit32(obj, arg, order) \ + : (sizeof(*(obj)) == 2 \ + ? atomic_fetch_and_explicit16(obj, arg, \ + order) \ + : (sizeof(*(obj)) == 1 \ + ? atomic_fetch_and_explicit8( \ + obj, arg, order) \ + : atomic_and_abort())))) + +#define atomic_fetch_and(obj, arg) \ + atomic_fetch_and_explicit(obj, arg, memory_order_seq_cst) + +#define atomic_fetch_or_explicit8(obj, arg, order) \ + InterlockedOr8((atomic_int_fast8_t *)obj, arg) + +#define atomic_fetch_or_explicit16(obj, arg, order) \ + InterlockedOr16((atomic_short *)obj, arg) + +#define atomic_fetch_or_explicit32(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedOrNoFence((atomic_int_fast32_t *)obj, arg) \ + : (order == memory_order_acquire \ + ? InterlockedOrAcquire((atomic_int_fast32_t *)obj, \ + arg) \ + : (order == memory_order_release \ + ? InterlockedOrRelease( \ + (atomic_int_fast32_t *)obj, \ + arg) \ + : InterlockedOr( \ + (atomic_int_fast32_t *)obj, \ + arg)))) + +#ifdef _WIN64 +#define atomic_fetch_or_explicit64(obj, arg, order) \ + (order == memory_order_relaxed \ + ? InterlockedOr64NoFence((atomic_int_fast64_t *)obj, arg) \ + : (order == memory_order_acquire \ + ? InterlockedOr64Acquire( \ + (atomic_int_fast64_t *)obj, arg) \ + : (order == memory_order_release \ + ? InterlockedOr64Release( \ + (atomic_int_fast64_t *)obj, \ + arg) \ + : InterlockedOr64( \ + (atomic_int_fast64_t *)obj, \ + arg)))) +#else /* ifdef _WIN64 */ +#define atomic_fetch_or_explicit64(obj, arg, order) \ + InterlockedOr64((atomic_int_fast64_t *)obj, arg) +#endif /* ifdef _WIN64 */ + +static inline int8_t +atomic_or_abort() { + UNREACHABLE(); +} + +#define atomic_fetch_or_explicit(obj, arg, order) \ + (sizeof(*(obj)) == 8 \ + ? atomic_fetch_or_explicit64(obj, arg, order) \ + : (sizeof(*(obj)) == 4 \ + ? atomic_fetch_or_explicit32(obj, arg, order) \ + : (sizeof(*(obj)) == 2 \ + ? atomic_fetch_or_explicit16(obj, arg, \ + order) \ + : (sizeof(*(obj)) == 1 \ + ? atomic_fetch_or_explicit8( \ + obj, arg, order) \ + : atomic_or_abort())))) + +#define atomic_fetch_or(obj, arg) \ + atomic_fetch_or_explicit(obj, arg, memory_order_seq_cst) + +static inline bool +atomic_compare_exchange_strong_explicit8(atomic_int_fast8_t *obj, + int8_t *expected, int8_t desired, + memory_order succ, memory_order fail) { + bool __r; + int8_t __v; + + UNUSED(succ); + UNUSED(fail); + + __v = InterlockedCompareExchange8((atomic_int_fast8_t *)obj, desired, + *expected); + __r = (*(expected) == __v); + if (!__r) { + *(expected) = __v; + } + return (__r); +} + +static inline bool +atomic_compare_exchange_strong_explicit16(atomic_short *obj, short *expected, + short desired, memory_order succ, + memory_order fail) { + bool __r; + short __v; + + UNUSED(succ); + UNUSED(fail); + + __v = InterlockedCompareExchange16((atomic_short *)obj, desired, + *expected); + __r = (*(expected) == __v); + if (!__r) { + *(expected) = __v; + } + return (__r); +} + +static inline bool +atomic_compare_exchange_strong_explicit32(atomic_int_fast32_t *obj, + int32_t *expected, int32_t desired, + memory_order succ, + memory_order fail) { + bool __r; + int32_t __v; + + UNUSED(succ); + UNUSED(fail); + + switch (succ) { + case memory_order_relaxed: + __v = InterlockedCompareExchangeNoFence( + (atomic_int_fast32_t *)obj, desired, *expected); + break; + case memory_order_acquire: + __v = InterlockedCompareExchangeAcquire( + (atomic_int_fast32_t *)obj, desired, *expected); + break; + case memory_order_release: + __v = InterlockedCompareExchangeRelease( + (atomic_int_fast32_t *)obj, desired, *expected); + break; + default: + __v = InterlockedCompareExchange((atomic_int_fast32_t *)obj, + desired, *expected); + break; + } + __r = (*(expected) == __v); + if (!__r) { + *(expected) = __v; + } + return (__r); +} + +static inline bool +atomic_compare_exchange_strong_explicit64(atomic_int_fast64_t *obj, + int64_t *expected, int64_t desired, + memory_order succ, + memory_order fail) { + bool __r; + int64_t __v; + + UNUSED(succ); + UNUSED(fail); + +#ifdef _WIN64 + switch (succ) { + case memory_order_relaxed: + __v = InterlockedCompareExchangeNoFence64( + (atomic_int_fast64_t *)obj, desired, *expected); + break; + case memory_order_acquire: + __v = InterlockedCompareExchangeAcquire64( + (atomic_int_fast64_t *)obj, desired, *expected); + break; + case memory_order_release: + __v = InterlockedCompareExchangeRelease64( + (atomic_int_fast64_t *)obj, desired, *expected); + break; + default: + __v = InterlockedCompareExchange64((atomic_int_fast64_t *)obj, + desired, *expected); + break; + } +#else /* ifdef _WIN64 */ + __v = InterlockedCompareExchange64((atomic_int_fast64_t *)obj, desired, + *expected); +#endif /* ifdef _WIN64 */ + __r = (*(expected) == __v); + if (!__r) { + *(expected) = __v; + } + return (__r); +} + +static inline bool +atomic_compare_exchange_abort() { + UNREACHABLE(); +} + +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, \ + fail) \ + (sizeof(*(obj)) == 8 \ + ? atomic_compare_exchange_strong_explicit64( \ + obj, expected, desired, succ, fail) \ + : (sizeof(*(obj)) == 4 \ + ? atomic_compare_exchange_strong_explicit32( \ + obj, expected, desired, succ, fail) \ + : (sizeof(*(obj)) == 2 \ + ? atomic_compare_exchange_strong_explicit16( \ + obj, expected, desired, succ, \ + fail) \ + : (sizeof(*(obj)) == 1 \ + ? atomic_compare_exchange_strong_explicit8( \ + obj, expected, \ + desired, succ, \ + fail) \ + : atomic_compare_exchange_abort())))) + +#define atomic_compare_exchange_strong(obj, expected, desired) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, \ + memory_order_seq_cst, \ + memory_order_seq_cst) + +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, \ + fail) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, \ + fail) + +#define atomic_compare_exchange_weak(obj, expected, desired) \ + atomic_compare_exchange_weak_explicit(obj, expected, desired, \ + memory_order_seq_cst, \ + memory_order_seq_cst) + +static inline bool +atomic_exchange_abort() { + UNREACHABLE(); +} + +#define atomic_exchange_explicit(obj, desired, order) \ + (sizeof(*(obj)) == 8 \ + ? InterlockedExchange64(obj, desired) \ + : (sizeof(*(obj)) == 4 \ + ? InterlockedExchange(obj, desired) \ + : (sizeof(*(obj)) == 2 \ + ? InterlockedExchange16(obj, desired) \ + : (sizeof(*(obj)) == 1 \ + ? InterlockedExchange8( \ + obj, desired) \ + : atomic_exchange_abort())))) + +#define atomic_exchange(obj, desired) \ + atomic_exchange_explicit(obj, desired, memory_order_seq_cst) diff --git a/lib/isc/win32/include/isc/stdtime.h b/lib/isc/win32/include/isc/stdtime.h new file mode 100644 index 0000000..12e990c --- /dev/null +++ b/lib/isc/win32/include/isc/stdtime.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef ISC_STDTIME_H +#define ISC_STDTIME_H 1 + +#include +#include + +#include + +/* + * It's public information that 'isc_stdtime_t' is an unsigned integral type. + * Applications that want maximum portability should not assume anything + * about its size. + */ +typedef uint32_t isc_stdtime_t; + +ISC_LANG_BEGINDECLS + +void +isc_stdtime_get(isc_stdtime_t *t); +/* + * Set 't' to the number of seconds since 00:00:00 UTC, January 1, 1970. + * + * Requires: + * + * 't' is a valid pointer. + */ + +void +isc_stdtime_tostring(isc_stdtime_t t, char *out, size_t outlen); +/* + * Convert 't' into a null-terminated string of the form + * "Wed Jun 30 21:49:08 1993". Store the string in the 'out' + * buffer. + * + * Requires: + * + * 't' is a valid time. + * 'out' is a valid pointer. + * 'outlen' is at least 26. + */ + +#define isc_stdtime_convert32(t, t32p) (*(t32p) = t) +/* + * Convert the standard time to its 32-bit version. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_STDTIME_H */ diff --git a/lib/isc/win32/include/isc/syslog.h b/lib/isc/win32/include/isc/syslog.h new file mode 100644 index 0000000..2638ad2 --- /dev/null +++ b/lib/isc/win32/include/isc/syslog.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef ISC_SYSLOG_H +#define ISC_SYSLOG_H 1 + +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_syslog_facilityfromstring(const char *str, int *facilityp); +/* + * Convert 'str' to the appropriate syslog facility constant. + * + * Requires: + * + * 'str' is not NULL + * 'facilityp' is not NULL + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOTFOUND + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SYSLOG_H */ diff --git a/lib/isc/win32/include/isc/thread.h b/lib/isc/win32/include/isc/thread.h new file mode 100644 index 0000000..5e30c63 --- /dev/null +++ b/lib/isc/win32/include/isc/thread.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#ifndef ISC_THREAD_H +#define ISC_THREAD_H 1 + +#include +#include + +#include +#include + +extern __declspec(thread) size_t isc_tid_v; + +/* + * Inlines to help with wait return checking + */ + +/* check handle for NULL and INVALID_HANDLE */ +inline BOOL +IsValidHandle(HANDLE hHandle) { + return ((hHandle != NULL) && (hHandle != INVALID_HANDLE_VALUE)); +} + +/* validate wait return codes... */ +inline BOOL +WaitSucceeded(DWORD dwWaitResult, DWORD dwHandleCount) { + return ((dwWaitResult >= WAIT_OBJECT_0) && + (dwWaitResult < WAIT_OBJECT_0 + dwHandleCount)); +} + +inline BOOL +WaitAbandoned(DWORD dwWaitResult, DWORD dwHandleCount) { + return ((dwWaitResult >= WAIT_ABANDONED_0) && + (dwWaitResult < WAIT_ABANDONED_0 + dwHandleCount)); +} + +inline BOOL +WaitTimeout(DWORD dwWaitResult) { + return (dwWaitResult == WAIT_TIMEOUT); +} + +inline BOOL +WaitFailed(DWORD dwWaitResult) { + return (dwWaitResult == WAIT_FAILED); +} + +/* compute object indices for waits... */ +inline DWORD +WaitSucceededIndex(DWORD dwWaitResult) { + return (dwWaitResult - WAIT_OBJECT_0); +} + +inline DWORD +WaitAbandonedIndex(DWORD dwWaitResult) { + return (dwWaitResult - WAIT_ABANDONED_0); +} + +typedef HANDLE isc_thread_t; +typedef DWORD isc_threadresult_t; +typedef void *isc_threadarg_t; +typedef isc_threadresult_t(WINAPI *isc_threadfunc_t)(isc_threadarg_t); + +#define isc_thread_self (uintptr_t) GetCurrentThreadId + +ISC_LANG_BEGINDECLS + +void +isc_thread_create(isc_threadfunc_t, isc_threadarg_t, isc_thread_t *); + +void +isc_thread_join(isc_thread_t, isc_threadresult_t *); + +void +isc_thread_setconcurrency(unsigned int level); + +void +isc_thread_setname(isc_thread_t, const char *); + +isc_result_t +isc_thread_setaffinity(int cpu); + +#define isc_thread_yield() Sleep(0) + +#if HAVE___DECLSPEC_THREAD +#define ISC_THREAD_LOCAL static __declspec(thread) +#else /* if HAVE___DECLSPEC_THREAD */ +#error "Thread-local storage support is required!" +#endif /* if HAVE___DECLSPEC_THREAD */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_THREAD_H */ diff --git a/lib/isc/win32/include/isc/time.h b/lib/isc/win32/include/isc/time.h new file mode 100644 index 0000000..baed464 --- /dev/null +++ b/lib/isc/win32/include/isc/time.h @@ -0,0 +1,468 @@ +/* + * 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. + */ + +#ifndef ISC_TIME_H +#define ISC_TIME_H 1 + +#include +#include +#include +#include +#include + +#include +#include + +/*** + *** POSIX Shims + ***/ + +struct tm * +gmtime_r(const time_t *clock, struct tm *result); + +struct tm * +localtime_r(const time_t *clock, struct tm *result); + +int +nanosleep(const struct timespec *req, struct timespec *rem); + +typedef uint32_t useconds_t; + +int +usleep(useconds_t usec); + +/*** + *** Intervals + ***/ + +/* + * The contents of this structure are private, and MUST NOT be accessed + * directly by callers. + * + * The contents are exposed only to allow callers to avoid dynamic allocation. + */ +struct isc_interval { + int64_t interval; +}; + +LIBISC_EXTERNAL_DATA extern const isc_interval_t *const isc_interval_zero; + +/* + * ISC_FORMATHTTPTIMESTAMP_SIZE needs to be 30 in C locale and potentially + * more for other locales to handle longer national abbreviations when + * expanding strftime's %a and %b. + */ +#define ISC_FORMATHTTPTIMESTAMP_SIZE 50 + +ISC_LANG_BEGINDECLS + +void +isc_interval_set(isc_interval_t *i, unsigned int seconds, + unsigned int nanoseconds); +/* + * Set 'i' to a value representing an interval of 'seconds' seconds and + * 'nanoseconds' nanoseconds, suitable for use in isc_time_add() and + * isc_time_subtract(). + * + * Requires: + * + * 't' is a valid pointer. + * nanoseconds < 1000000000. + */ + +bool +isc_interval_iszero(const isc_interval_t *i); +/* + * Returns true iff. 'i' is the zero interval. + * + * Requires: + * + * 'i' is a valid pointer. + */ + +/*** + *** Absolute Times + ***/ + +/* + * The contents of this structure are private, and MUST NOT be accessed + * directly by callers. + * + * The contents are exposed only to allow callers to avoid dynamic allocation. + */ + +struct isc_time { + FILETIME absolute; +}; + +LIBISC_EXTERNAL_DATA extern const isc_time_t *const isc_time_epoch; + +void +isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds); +/*%< + * Set 't' to a value which represents the given number of seconds and + * nanoseconds since 00:00:00 January 1, 1970, UTC. + * + * Requires: + *\li 't' is a valid pointer. + *\li nanoseconds < 1000000000. + */ + +void +isc_time_settoepoch(isc_time_t *t); +/* + * Set 't' to the time of the epoch. + * + * Notes: + * The date of the epoch is platform-dependent. + * + * Requires: + * + * 't' is a valid pointer. + */ + +bool +isc_time_isepoch(const isc_time_t *t); +/* + * Returns true iff. 't' is the epoch ("time zero"). + * + * Requires: + * + * 't' is a valid pointer. + */ + +isc_result_t +isc_time_now(isc_time_t *t); +/* + * Set 't' to the current absolute time. + * + * Requires: + * + * 't' is a valid pointer. + * + * Returns: + * + * Success + * Unexpected error + * Getting the time from the system failed. + * Out of range + * The time from the system is too large to be represented + * in the current definition of isc_time_t. + */ + +isc_result_t +isc_time_now_hires(isc_time_t *t); +/*%< + * Set 't' to the current absolute time. Uses higher resolution clocks + * recommended when microsecond accuracy is required. + * + * Requires: + * + *\li 't' is a valid pointer. + * + * Returns: + * + *\li Success + *\li Unexpected error + * Getting the time from the system failed. + *\li Out of range + * The time from the system is too large to be represented + * in the current definition of isc_time_t. + */ + +isc_result_t +isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i); +/* + * Set *t to the current absolute time + i. + * + * Note: + * This call is equivalent to: + * + * isc_time_now(t); + * isc_time_add(t, i, t); + * + * Requires: + * + * 't' and 'i' are valid pointers. + * + * Returns: + * + * Success + * Unexpected error + * Getting the time from the system failed. + * Out of range + * The interval added to the time from the system is too large to + * be represented in the current definition of isc_time_t. + */ + +int +isc_time_compare(const isc_time_t *t1, const isc_time_t *t2); +/* + * Compare the times referenced by 't1' and 't2' + * + * Requires: + * + * 't1' and 't2' are valid pointers. + * + * Returns: + * + * -1 t1 < t2 (comparing times, not pointers) + * 0 t1 = t2 + * 1 t1 > t2 + */ + +isc_result_t +isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result); +/* + * Add 'i' to 't', storing the result in 'result'. + * + * Requires: + * + * 't', 'i', and 'result' are valid pointers. + * + * Returns: + * Success + * Out of range + * The interval added to the time is too large to + * be represented in the current definition of isc_time_t. + */ + +isc_result_t +isc_time_subtract(const isc_time_t *t, const isc_interval_t *i, + isc_time_t *result); +/* + * Subtract 'i' from 't', storing the result in 'result'. + * + * Requires: + * + * 't', 'i', and 'result' are valid pointers. + * + * Returns: + * Success + * Out of range + * The interval is larger than the time since the epoch. + */ + +uint64_t +isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2); +/* + * Find the difference in milliseconds between time t1 and time t2. + * t2 is the subtrahend of t1; ie, difference = t1 - t2. + * + * Requires: + * + * 't1' and 't2' are valid pointers. + * + * Returns: + * The difference of t1 - t2, or 0 if t1 <= t2. + */ + +isc_result_t +isc_time_parsehttptimestamp(char *input, isc_time_t *t); +/*%< + * Parse the time in 'input' into the isc_time_t pointed to by 't', + * expecting a format like "Mon, 30 Aug 2000 04:06:47 GMT" + * + * Requires: + *\li 'buf' and 't' are not NULL. + */ + +uint32_t +isc_time_nanoseconds(const isc_time_t *t); +/* + * Return the number of nanoseconds stored in a time structure. + * + * Notes: + * This is the number of nanoseconds in excess of the number + * of seconds since the epoch; it will always be less than one + * full second. + * + * Requires: + * 't' is a valid pointer. + * + * Ensures: + * The returned value is less than 1*10^9. + */ + +void +isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len); +/* + * Format the time 't' into the buffer 'buf' of length 'len', + * using a format like "30-Aug-2000 04:06:47.997" and the local time zone. + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + * 'len' > 0 + * 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len); +/* + * Format the time 't' into the buffer 'buf' of length 'len', + * using a format like "Mon, 30 Aug 2000 04:06:47 GMT" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + * 'len' > 0 + * 'buf' points to an array of at least len chars + * + */ + +isc_result_t +isc_time_parsehttptimestamp(char *input, isc_time_t *t); +/*%< + * Parse the time in 'input' into the isc_time_t pointed to by 't', + * expecting a format like "Mon, 30 Aug 2000 04:06:47 GMT" + * + * Requires: + *\li 'buf' and 't' are not NULL. + */ + +void +isc_time_formatISO8601L(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.sss" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatISO8601Lus(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.ssssss" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ssZ" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.sssZ" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the ISO8601 format: "yyyy-mm-ddThh:mm:ss.ssssssZ" + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +void +isc_time_formatshorttimestamp(const isc_time_t *t, char *buf, unsigned int len); +/*%< + * Format the time 't' into the buffer 'buf' of length 'len', + * using the format "yyyymmddhhmmsssss" useful for file timestamping. + * If the text does not fit in the buffer, the result is indeterminate, + * but is always guaranteed to be null terminated. + * + * Requires: + *\li 'len' > 0 + *\li 'buf' points to an array of at least len chars + * + */ + +uint32_t +isc_time_seconds(const isc_time_t *t); +/*%< + * Return the number of seconds since the epoch stored in a time structure. + * + * Requires: + * + *\li 't' is a valid pointer. + */ + +isc_result_t +isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp); +/*%< + * Ensure the number of seconds in an isc_time_t is representable by a time_t. + * + * Notes: + *\li The number of seconds stored in an isc_time_t might be larger + * than the number of seconds a time_t is able to handle. Since + * time_t is mostly opaque according to the ANSI/ISO standard + * (essentially, all you can be sure of is that it is an arithmetic type, + * not even necessarily integral), it can be tricky to ensure that + * the isc_time_t is in the range a time_t can handle. Use this + * function in place of isc_time_seconds() any time you need to set a + * time_t from an isc_time_t. + * + * Requires: + *\li 't' is a valid pointer. + * + * Returns: + *\li Success + *\li Out of range + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TIME_H */ diff --git a/lib/isc/win32/include/isc/win32os.h b/lib/isc/win32/include/isc/win32os.h new file mode 100644 index 0000000..22def42 --- /dev/null +++ b/lib/isc/win32/include/isc/win32os.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef ISC_WIN32OS_H +#define ISC_WIN32OS_H 1 + +#include + +ISC_LANG_BEGINDECLS + +/* + * Return the number of CPUs available on the system, or 1 if this cannot + * be determined. + */ + +int +isc_win32os_versioncheck(unsigned int major, unsigned int minor, + unsigned int updatemajor, unsigned int updateminor); + +/* + * Checks the current version of the operating system with the + * supplied version information. + * Returns: + * -1 if less than the version information supplied + * 0 if equal to all of the version information supplied + * +1 if greater than the version information supplied + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_WIN32OS_H */ diff --git a/lib/isc/win32/interfaceiter.c b/lib/isc/win32/interfaceiter.c new file mode 100644 index 0000000..db82e20 --- /dev/null +++ b/lib/isc/win32/interfaceiter.c @@ -0,0 +1,550 @@ +/* + * 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. + */ + +/* + * Note that this code will need to be revisited to support IPv6 Interfaces. + * For now we just iterate through IPv4 interfaces. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void +InitSockets(void); + +/* Common utility functions */ + +/* + * Extract the network address part from a "struct sockaddr". + * + * The address family is given explicitly + * instead of using src->sa_family, because the latter does not work + * for copying a network mask obtained by SIOCGIFNETMASK (it does + * not have a valid address family). + */ + +#define IFITER_MAGIC 0x49464954U /* IFIT. */ +#define VALID_IFITER(t) ((t) != NULL && (t)->magic == IFITER_MAGIC) + +struct isc_interfaceiter { + unsigned int magic; /* Magic number. */ + isc_mem_t *mctx; + SOCKET socket; + INTERFACE_INFO IFData; /* Current Interface Info. */ + int numIF; /* Current Interface count. */ + int v4IF; /* Number of IPv4 Interfaces */ + INTERFACE_INFO *buf4; /* Buffer for WSAIoctl data. */ + unsigned int buf4size; /* Bytes allocated. */ + INTERFACE_INFO *pos4; /* Current offset in IF List */ + SOCKET_ADDRESS_LIST *buf6; /* Buffer for WSAIoctl data. */ + unsigned int buf6size; /* Bytes allocated. */ + unsigned int pos6; /* Which entry to process. */ + bool v6loop; /* See IPv6 loop address. */ + bool pos6zero; /* Done pos6 == 0. */ + isc_interface_t current; /* Current interface data. */ + isc_result_t result; /* Last result code. */ +}; + +/* + * Size of buffer for SIO_GET_INTERFACE_LIST, in number of interfaces. + * We assume no sane system will have more than than 1K of IP addresses on + * all of its adapters. + */ +#define IFCONF_SIZE_INITIAL 16 +#define IFCONF_SIZE_INCREMENT 64 +#define IFCONF_SIZE_MAX 1040 + +static void +get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src) { + dst->family = family; + switch (family) { + case AF_INET: + memmove(&dst->type.in, &((struct sockaddr_in *)src)->sin_addr, + sizeof(struct in_addr)); + break; + case AF_INET6: + memmove(&dst->type.in6, + &((struct sockaddr_in6 *)src)->sin6_addr, + sizeof(struct in6_addr)); + dst->zone = ((struct sockaddr_in6 *)src)->sin6_scope_id; + break; + default: + UNREACHABLE(); + } +} + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { + char strbuf[ISC_STRERRORSIZE]; + isc_interfaceiter_t *iter; + isc_result_t result; + int error; + unsigned long bytesReturned = 0; + + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + + InitSockets(); + + iter->mctx = mctx; + iter->buf4 = NULL; + iter->buf6 = NULL; + iter->pos4 = NULL; + iter->pos6 = 0; + iter->v6loop = true; + iter->pos6zero = true; + iter->buf6size = 0; + iter->buf4size = 0; + iter->result = ISC_R_FAILURE; + iter->numIF = 0; + iter->v4IF = 0; + + /* + * Create an unbound datagram socket to do the + * SIO_GET_INTERFACE_LIST WSAIoctl on. + */ + iter->socket = socket(AF_INET, SOCK_DGRAM, 0); + if (iter->socket == INVALID_SOCKET) { + error = WSAGetLastError(); + if (error == WSAEAFNOSUPPORT) { + goto inet6_only; + } + strerror_r(error, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "making interface scan socket: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto socket_failure; + } + + /* + * Get the interface configuration, allocating more memory if + * necessary. + */ + iter->buf4size = IFCONF_SIZE_INITIAL * sizeof(INTERFACE_INFO); + + for (;;) { + iter->buf4 = isc_mem_get(mctx, iter->buf4size); + + if (WSAIoctl(iter->socket, SIO_GET_INTERFACE_LIST, 0, 0, + iter->buf4, iter->buf4size, &bytesReturned, 0, + 0) == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error != WSAEFAULT && error != WSAENOBUFS) { + errno = error; + strerror_r(error, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "get interface configuration: " + "%s", + strbuf); + result = ISC_R_UNEXPECTED; + goto ioctl_failure; + } + /* + * EINVAL. Retry with a bigger buffer. + */ + } else { + /* + * The WSAIoctl succeeded. + * If the number of the returned bytes is the same + * as the buffer size, we will grow it just in + * case and retry. + */ + if (bytesReturned > 0 && + (bytesReturned < iter->buf4size)) + { + break; + } + } + if (iter->buf4size >= IFCONF_SIZE_MAX * sizeof(INTERFACE_INFO)) + { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "get interface configuration: " + "maximum buffer size exceeded"); + result = ISC_R_UNEXPECTED; + goto ioctl_failure; + } + isc_mem_put(mctx, iter->buf4, iter->buf4size); + + iter->buf4size += IFCONF_SIZE_INCREMENT * + sizeof(INTERFACE_INFO); + } + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ + iter->v4IF = bytesReturned / sizeof(INTERFACE_INFO); + + /* We don't need the socket any more, so close it */ + closesocket(iter->socket); + +inet6_only: + /* + * Create an unbound datagram socket to do the + * SIO_ADDRESS_LIST_QUERY WSAIoctl on. + */ + iter->socket = socket(AF_INET6, SOCK_DGRAM, 0); + if (iter->socket == INVALID_SOCKET) { + error = WSAGetLastError(); + if (error == WSAEAFNOSUPPORT) { + goto inet_only; + } + strerror_r(error, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "making interface scan socket: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto ioctl_failure; + } + + /* + * Get the interface configuration, allocating more memory if + * necessary. + */ + iter->buf6size = sizeof(SOCKET_ADDRESS_LIST) + + IFCONF_SIZE_INITIAL * sizeof(SOCKET_ADDRESS); + + for (;;) { + iter->buf6 = isc_mem_get(mctx, iter->buf6size); + + if (WSAIoctl(iter->socket, SIO_ADDRESS_LIST_QUERY, 0, 0, + iter->buf6, iter->buf6size, &bytesReturned, 0, + 0) == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error != WSAEFAULT && error != WSAENOBUFS) { + errno = error; + strerror_r(error, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "sio address list query: %s", + strbuf); + result = ISC_R_UNEXPECTED; + goto ioctl6_failure; + } + /* + * EINVAL. Retry with a bigger buffer. + */ + } else { + break; + } + + if (iter->buf6size >= IFCONF_SIZE_MAX * sizeof(SOCKET_ADDRESS)) + { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "get interface configuration: " + "maximum buffer size exceeded"); + result = ISC_R_UNEXPECTED; + goto ioctl6_failure; + } + isc_mem_put(mctx, iter->buf6, iter->buf6size); + + iter->buf6size += IFCONF_SIZE_INCREMENT * + sizeof(SOCKET_ADDRESS); + } + + closesocket(iter->socket); + +inet_only: + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + +ioctl6_failure: + isc_mem_put(mctx, iter->buf6, iter->buf6size); + +ioctl_failure: + if (iter->buf4 != NULL) { + isc_mem_put(mctx, iter->buf4, iter->buf4size); + } + if (iter->socket != INVALID_SOCKET) { + (void)closesocket(iter->socket); + } + +socket_failure: + isc_mem_put(mctx, iter, sizeof(*iter)); + return (result); +} + +/* + * Get information about the current interface to iter->current. + * If successful, return ISC_R_SUCCESS. + * If the interface has an unsupported address family, or if + * some operation on it fails, return ISC_R_IGNORE to make + * the higher-level iterator code ignore it. + */ + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { + BOOL ifNamed = FALSE; + unsigned long flags; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE(iter->numIF >= 0); + + memset(&iter->current, 0, sizeof(iter->current)); + iter->current.af = AF_INET; + + get_addr(AF_INET, &iter->current.address, + (struct sockaddr *)&(iter->IFData.iiAddress)); + + /* + * Get interface flags. + */ + + iter->current.flags = 0; + flags = iter->IFData.iiFlags; + + if ((flags & IFF_UP) != 0) { + iter->current.flags |= INTERFACE_F_UP; + } + + if ((flags & IFF_POINTTOPOINT) != 0) { + iter->current.flags |= INTERFACE_F_POINTTOPOINT; + snprintf(iter->current.name, sizeof(iter->current.name), + "PPP Interface %d", iter->numIF); + ifNamed = TRUE; + } + + if ((flags & IFF_LOOPBACK) != 0) { + iter->current.flags |= INTERFACE_F_LOOPBACK; + snprintf(iter->current.name, sizeof(iter->current.name), + "Loopback Interface %d", iter->numIF); + ifNamed = TRUE; + } + + /* + * If the interface is point-to-point, get the destination address. + */ + if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { + get_addr(AF_INET, &iter->current.dstaddress, + (struct sockaddr *)&(iter->IFData.iiBroadcastAddress)); + } + + if (ifNamed == FALSE) { + snprintf(iter->current.name, sizeof(iter->current.name), + "TCP/IP Interface %d", iter->numIF); + } + + /* + * Get the network mask. + */ + get_addr(AF_INET, &iter->current.netmask, + (struct sockaddr *)&(iter->IFData.iiNetmask)); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +internal_current6(isc_interfaceiter_t *iter) { + SOCKET fd; + int i; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE(iter->buf6 != NULL); + + memset(&iter->current, 0, sizeof(iter->current)); + iter->current.af = AF_INET6; + + if (!iter->pos6zero) { + if (iter->pos6 == 0U) { + iter->pos6zero = true; + } + get_addr(AF_INET6, &iter->current.address, + iter->buf6->Address[iter->pos6].lpSockaddr); + + /* + * Set interface flags. + */ + + iter->current.flags = INTERFACE_F_UP; + + snprintf(iter->current.name, sizeof(iter->current.name), + "TCP/IPv6 Interface %u", iter->pos6 + 1); + + for (i = 0; i < 16; i++) { + iter->current.netmask.type.in6.s6_addr[i] = 0xff; + } + iter->current.netmask.family = AF_INET6; + if (IN6_IS_ADDR_LOOPBACK(&iter->current.address.type.in6)) { + iter->v6loop = true; + } + } else { + /* + * See if we can bind to the ::1 and if so return ::1. + */ + struct sockaddr_in6 sin6; + + iter->v6loop = true; /* So we don't loop forever. */ + + fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (fd == INVALID_SOCKET) { + return (ISC_R_IGNORE); + } + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr.s6_addr[15] = 1; + if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) { + closesocket(fd); + return (ISC_R_IGNORE); + } + closesocket(fd); + + iter->current.flags = INTERFACE_F_UP | INTERFACE_F_LOOPBACK; + snprintf(iter->current.name, sizeof(iter->current.name), + "TCP/IPv6 Loopback Interface"); + for (i = 0; i < 16; i++) { + if (i != 15) { + iter->current.address.type.in6.s6_addr[i] = 0; + } else { + iter->current.address.type.in6.s6_addr[i] = 1; + } + iter->current.netmask.type.in6.s6_addr[i] = 0xff; + } + iter->current.address.family = AF_INET6; + iter->current.netmask.family = AF_INET6; + } + return (ISC_R_SUCCESS); +} + +/* + * Step the iterator to the next interface. Unlike + * isc_interfaceiter_next(), this may leave the iterator + * positioned on an interface that will ultimately + * be ignored. Return ISC_R_NOMORE if there are no more + * interfaces, otherwise ISC_R_SUCCESS. + */ +static isc_result_t +internal_next(isc_interfaceiter_t *iter) { + if (iter->numIF >= iter->v4IF) { + return (ISC_R_NOMORE); + } + + /* + * The first one needs to be set up to point to the last + * Element of the array. Go to the end and back up + * Microsoft's implementation is peculiar for returning + * the list in reverse order + */ + + if (iter->numIF == 0) { + iter->pos4 = (INTERFACE_INFO *)(iter->buf4 + (iter->v4IF)); + } + + iter->pos4--; + if (&(iter->pos4) < &(iter->buf4)) { + return (ISC_R_NOMORE); + } + + memset(&(iter->IFData), 0, sizeof(INTERFACE_INFO)); + memmove(&(iter->IFData), iter->pos4, sizeof(INTERFACE_INFO)); + iter->numIF++; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +internal_next6(isc_interfaceiter_t *iter) { + if (iter->pos6 == 0U && iter->v6loop) { + return (ISC_R_NOMORE); + } + if (iter->pos6 != 0U) { + iter->pos6--; + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_interfaceiter_current(isc_interfaceiter_t *iter, isc_interface_t *ifdata) { + REQUIRE(iter->result == ISC_R_SUCCESS); + memmove(ifdata, &iter->current, sizeof(*ifdata)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_interfaceiter_first(isc_interfaceiter_t *iter) { + REQUIRE(VALID_IFITER(iter)); + + if (iter->buf6 != NULL) { + iter->pos6 = iter->buf6->iAddressCount; + iter->v6loop = false; + iter->pos6zero = (iter->pos6 == 0U); + } + iter->result = ISC_R_SUCCESS; + return (isc_interfaceiter_next(iter)); +} + +isc_result_t +isc_interfaceiter_next(isc_interfaceiter_t *iter) { + isc_result_t result; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE(iter->result == ISC_R_SUCCESS); + + for (;;) { + result = internal_next(iter); + if (result == ISC_R_NOMORE) { + result = internal_next6(iter); + if (result != ISC_R_SUCCESS) { + break; + } + result = internal_current6(iter); + if (result == ISC_R_IGNORE) { + continue; + } + break; + } else if (result != ISC_R_SUCCESS) { + break; + } + result = internal_current(iter); + if (result != ISC_R_IGNORE) { + break; + } + } + iter->result = result; + return (result); +} + +void +isc_interfaceiter_destroy(isc_interfaceiter_t **iterp) { + isc_interfaceiter_t *iter; + REQUIRE(iterp != NULL); + iter = *iterp; + *iterp = NULL; + REQUIRE(VALID_IFITER(iter)); + + if (iter->buf4 != NULL) { + isc_mem_put(iter->mctx, iter->buf4, iter->buf4size); + } + if (iter->buf6 != NULL) { + isc_mem_put(iter->mctx, iter->buf6, iter->buf6size); + } + + iter->magic = 0; + isc_mem_put(iter->mctx, iter, sizeof(*iter)); +} diff --git a/lib/isc/win32/ipv6.c b/lib/isc/win32/ipv6.c new file mode 100644 index 0000000..fecec98 --- /dev/null +++ b/lib/isc/win32/ipv6.c @@ -0,0 +1,18 @@ +/* + * 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 +#include + +LIBISC_EXTERNAL_DATA const struct in6_addr isc_in6addr_loopback = + IN6ADDR_LOOPBACK_INIT; diff --git a/lib/isc/win32/libgen.h b/lib/isc/win32/libgen.h new file mode 100644 index 0000000..b04ac37 --- /dev/null +++ b/lib/isc/win32/libgen.h @@ -0,0 +1,22 @@ +/* + * 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. + */ + +#ifndef LIBGEN_H +#define LIBGEN_H 1 + +char * +basename(const char *); +char * +dirname(const char *); + +#endif /* ifndef LIBGEN_H */ diff --git a/lib/isc/win32/libisc.def.exclude b/lib/isc/win32/libisc.def.exclude new file mode 100644 index 0000000..d2fe4ec --- /dev/null +++ b/lib/isc/win32/libisc.def.exclude @@ -0,0 +1,42 @@ +; These symbols are not needed by the WIN32 build, but build-tarballs +; will complain if they aren't present here. +isc_socket_accept +isc_socket_attach +isc_socket_bind +isc_socket_cancel +isc_socket_cleanunix +isc_socket_close +isc_socket_connect +isc_socket_create +isc_socket_detach +isc_socket_dscp +isc_socket_dup +isc_socket_fdwatchcreate +isc_socket_fdwatchpoke +isc_socket_filter +isc_socket_getpeername +isc_socket_getsockname +isc_socket_gettype +isc_socket_ipv6only +isc_socket_listen +isc_socket_open +isc_socket_permunix +isc_socket_recv +isc_socket_recv2 +isc_socket_recvv +isc_socket_register +isc_socket_send +isc_socket_sendto +isc_socket_sendto2 +isc_socket_sendtov +isc_socket_sendtov2 +isc_socket_sendv +isc_socketmgr_create +isc_socketmgr_create2 +isc_socketmgr_destroy +isc_socketmgr_setstats +isc_print_fprintf +isc_print_printf +isc_print_snprintf +isc_print_sprintf +isc_print_vsnprintf diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in new file mode 100644 index 0000000..0ee055d --- /dev/null +++ b/lib/isc/win32/libisc.def.in @@ -0,0 +1,805 @@ +LIBRARY libisc + +; Exported Functions +EXPORTS + +NTReportError +closelog +@IF PKCS11 +getpassphrase +@END PKCS11 +isc_app_block +isc_app_ctxfinish +isc_app_ctxonrun +isc_app_ctxrun +isc_app_ctxshutdown +isc_app_ctxstart +isc_app_ctxsuspend +isc_app_finish +isc_app_onrun +isc_app_reload +isc_app_run +isc_app_shutdown +isc_app_start +isc_app_unblock +isc_appctx_create +isc_appctx_destroy +isc_astack_destroy +isc_astack_new +isc_astack_pop +isc_astack_trypush +isc__buffer_activeregion +isc__buffer_add +isc__buffer_availableregion +isc__buffer_back +isc__buffer_clear +isc__buffer_consumedregion +isc__buffer_first +isc__buffer_forward +isc__buffer_init +isc__buffer_initnull +isc__buffer_invalidate +isc__buffer_putmem +isc__buffer_putstr +isc__buffer_putuint16 +isc__buffer_putuint24 +isc__buffer_putuint32 +isc__buffer_putuint48 +isc__buffer_putuint8 +isc__buffer_region +isc__buffer_remainingregion +isc__buffer_setactive +isc__buffer_subtract +isc__buffer_usedregion +isc__mem_allocate +isc__mem_free +isc__mem_get +isc__mem_printactive +isc__mem_put +isc__mem_putanddetach +isc__mem_reallocate +isc__mem_strdup +isc__mem_strndup +isc__mempool_get +isc__mempool_put +isc__md_md5 +isc__md_sha1 +isc__md_sha224 +isc__md_sha256 +isc__md_sha384 +isc__md_sha512 +isc_socket_accept +isc_socket_attach +isc_socket_bind +isc_socket_cancel +isc_socket_cleanunix +isc_socket_close +isc_socket_connect +isc_socket_create +isc_socket_detach +isc_socket_dscp +isc_socket_dup +isc_socket_filter +isc_socket_getfd +isc_socket_getname +isc_socket_getpeername +isc_socket_getsockname +isc_socket_gettag +isc_socket_gettype +isc_socket_hasreuseport +isc_socket_ipv6only +isc_socket_listen +isc_socket_open +isc_socket_permunix +isc_socket_recv +isc_socket_recv2 +isc_socket_send +isc_socket_sendto +isc_socket_sendto2 +isc_socket_setname +isc_socketmgr_create +isc_socketmgr_create2 +isc_socketmgr_destroy +isc_socketmgr_getmaxsockets +isc_socketmgr_setreserved +isc_socketmgr_setstats +isc_aes128_crypt +isc_aes192_crypt +isc_aes256_crypt +isc_app_block +isc_app_ctxfinish +isc_app_ctxonrun +isc_app_ctxrun +isc_app_ctxshutdown +isc_app_ctxstart +isc_app_ctxsuspend +isc_app_finish +isc_app_isrunning +isc_app_onrun +isc_app_reload +isc_app_run +isc_app_shutdown +isc_app_start +isc_app_unblock +isc_appctx_create +isc_appctx_destroy +isc_assertion_failed +isc_assertion_setcallback +isc_assertion_typetotext +isc_backtrace_getsymbol +isc_backtrace_getsymbolfromindex +isc_backtrace_gettrace +isc_base32_decoderegion +isc_base32_decodestring +isc_base32_tobuffer +isc_base32_totext +isc_base32hex_decoderegion +isc_base32hex_decodestring +isc_base32hex_tobuffer +isc_base32hex_totext +isc_base32hexnp_decoderegion +isc_base32hexnp_decodestring +isc_base32hexnp_tobuffer +isc_base32hexnp_totext +isc_base64_decodestring +isc_base64_tobuffer +isc_base64_totext +isc_buffer_allocate +isc_buffer_compact +isc_buffer_copyregion +isc_buffer_dup +isc_buffer_free +isc_buffer_getuint16 +isc_buffer_getuint32 +isc_buffer_getuint48 +isc_buffer_getuint8 +isc_buffer_printf +isc_buffer_putdecint +isc_buffer_reinit +isc_buffer_reserve +isc_buffer_setautorealloc +isc_bufferlist_availablecount +isc_bufferlist_usedcount +isc_commandline_parse +isc_commandline_strtoargv +isc_condition_broadcast +isc_condition_destroy +isc_condition_init +isc_condition_signal +isc_condition_wait +isc_condition_waituntil +isc_counter_attach +isc_counter_create +isc_counter_detach +isc_counter_increment +isc_counter_setlimit +isc_counter_used +isc_crc64_final +isc_crc64_init +isc_crc64_update +isc_dir_chdir +isc_dir_chroot +isc_dir_close +isc_dir_createunique +isc_dir_init +isc_dir_open +isc_dir_read +isc_dir_reset +isc_enable_constructors +isc_entropy_get +isc_errno_toresult +isc_error_fatal +isc_error_runtimecheck +isc_error_setfatal +isc_error_setunexpected +isc_error_unexpected +isc_event_allocate +isc_event_constallocate +isc_event_free +isc_file_absolutepath +isc_file_basename +isc_file_bopenunique +isc_file_bopenuniquemode +isc_file_bopenuniqueprivate +isc_file_exists +isc_file_getmodtime +isc_file_getsize +isc_file_getsizefd +isc_file_isabsolute +isc_file_ischdiridempotent +isc_file_iscurrentdir +isc_file_isdirectory +isc_file_isdirwritable +isc_file_isplainfile +isc_file_isplainfilefd +isc_file_mktemplate +isc_file_mmap +isc_file_mode +isc_file_munmap +isc_file_openunique +isc_file_openuniquemode +isc_file_openuniqueprivate +isc_file_progname +isc_file_remove +isc_file_rename +isc_file_renameunique +isc_file_safecreate +isc_file_safemovefile +isc_file_sanitize +isc_file_settime +isc_file_splitpath +isc_file_template +isc_file_truncate +isc_fsaccess_add +isc_fsaccess_changeowner +isc_fsaccess_remove +isc_fsaccess_set +isc_hash32 +isc_hash64 +isc_hash_get_initializer +isc_hash_set_initializer +isc_heap_create +isc_heap_decreased +isc_heap_delete +isc_heap_destroy +isc_heap_foreach +isc_heap_element +isc_heap_increased +isc_heap_insert +isc_hex_decodestring +isc_hex_tobuffer +isc_hex_totext +isc_hmac +isc_hmac_new +isc_hmac_free +isc_hmac_init +isc_hmac_reset +isc_hmac_update +isc_hmac_final +isc_hmac_get_md_type +isc_hmac_get_size +isc_hmac_get_block_size +isc_ht_add +isc_ht_count +isc_ht_delete +isc_ht_destroy +isc_ht_find +isc_ht_init +isc_ht_iter_create +isc_ht_iter_current +isc_ht_iter_currentkey +isc_ht_iter_delcurrent_next +isc_ht_iter_destroy +isc_ht_iter_first +isc_ht_iter_next +isc_httpd_addheader +isc_httpd_addheaderuint +isc_httpd_endheaders +isc_httpd_response +isc_httpd_setfinishhook +isc_httpdmgr_addurl +isc_httpdmgr_addurl2 +isc_httpdmgr_create +isc_httpdmgr_shutdown +isc_interfaceiter_create +isc_interfaceiter_current +isc_interfaceiter_destroy +isc_interfaceiter_first +isc_interfaceiter_next +isc_interval_iszero +isc_interval_set +isc_iterated_hash +isc_lex_close +isc_lex_create +isc_lex_destroy +isc_lex_getcomments +isc_lex_getlasttokentext +isc_lex_getmastertoken +isc_lex_getoctaltoken +isc_lex_getsourceline +isc_lex_getsourcename +isc_lex_getspecials +isc_lex_gettoken +isc_lex_isfile +isc_lex_openbuffer +isc_lex_openfile +isc_lex_openstream +isc_lex_setcomments +isc_lex_setsourceline +isc_lex_setsourcename +isc_lex_setspecials +isc_lex_ungettoken +isc_lfsr_generate +isc_lfsr_generate32 +isc_lfsr_init +isc_lfsr_skip +isc_lib_ntservice +isc_lib_register +isc_log_categorybyname +isc_log_closefilelogs +isc_log_create +isc_log_createchannel +isc_log_destroy +isc_log_getdebuglevel +isc_log_getduplicateinterval +isc_log_gettag +isc_log_modulebyname +isc_log_opensyslog +isc_log_registercategories +isc_log_registermodules +isc_log_setcontext +isc_log_setdebuglevel +isc_log_setduplicateinterval +isc_log_settag +isc_log_usechannel +isc_log_vwrite +isc_log_vwrite1 +isc_log_wouldlog +isc_log_write +isc_log_write1 +isc_logconfig_create +isc_logconfig_destroy +isc_logconfig_use +isc_logfile_roll +isc_managers_create +isc_managers_destroy +isc_md_new +isc_md_init +isc_md_reset +isc_md_update +isc_md_final +isc_md_free +isc_md_get_md_type +isc_md_get_size +isc_md_get_block_size +isc_md_type_get_size +isc_md_type_get_block_size +isc_md +isc__mem_checkdestroyed +isc__mem_initialize +isc__mem_shutdown +isc_mem_attach +isc_mem_checkdestroyed +isc_mem_create +isc_mem_destroy +isc_mem_detach +isc_mem_getname +isc_mem_gettag +isc_mem_inuse +isc_mem_isovermem +isc_mem_maxinuse +isc_mem_references +@IF NOTYET +isc_mem_renderjson +@END NOTYET +@IF LIBXML2 +isc_mem_renderxml +@END LIBXML2 +isc_mem_setdestroycheck +isc_mem_setname +isc_mem_setwater +isc_mem_stats +isc_mem_total +isc_mem_waterack +isc_meminfo_totalphys +isc_mempool_create +isc_mempool_destroy +isc_mempool_getallocated +isc_mempool_getfillcount +isc_mempool_getfreecount +isc_mempool_getfreemax +isc_mempool_getmaxalloc +isc_mempool_setfillcount +isc_mempool_setfreemax +isc_mempool_setmaxalloc +isc_mempool_setname +isc_mutexblock_destroy +isc_mutexblock_init +isc_net_disableipv4 +isc_net_disableipv6 +isc_net_enableipv4 +isc_net_enableipv6 +isc_net_getudpportrange +isc_net_probe_ipv6only +isc_net_probe_ipv6pktinfo +isc_net_probedscp +isc_net_probeipv4 +isc_net_probeipv6 +isc_net_probeunix +isc_netaddr_any +isc_netaddr_any6 +isc_netaddr_eqprefix +isc_netaddr_equal +isc_netaddr_format +isc_netaddr_fromin +isc_netaddr_fromin6 +isc_netaddr_frompath +isc_netaddr_fromsockaddr +isc_netaddr_fromv4mapped +isc_netaddr_getzone +isc_netaddr_isexperimental +isc_netaddr_islinklocal +isc_netaddr_isloopback +isc_netaddr_ismulticast +isc_netaddr_isnetzero +isc_netaddr_issitelocal +isc_netaddr_masktoprefixlen +isc_netaddr_prefixok +isc_netaddr_setzone +isc_netaddr_totext +isc_netaddr_unspec +isc_netscope_pton +isc__nmhandle_attach +isc__nmhandle_detach +isc_nmhandle_cleartimeout +isc_nmhandle_getdata +isc_nmhandle_getextra +isc_nmhandle_is_stream +isc_nmhandle_keepalive +isc_nmhandle_netmgr +isc_nmhandle_localaddr +isc_nmhandle_peeraddr +isc_nmhandle_setdata +isc_nmhandle_settimeout +isc_nmhandle_setwritetimeout +isc_nm_attach +isc_nm_cancelread +isc_nm_detach +isc_nm_getloadbalancesockets +isc_nm_gettimeouts +isc_nm_listentcp +isc_nm_listentcpdns +isc_nm_listenudp +isc_nm_maxudp +isc_nm_pause +isc_nm_pauseread +isc_nm_read +isc_nm_resume +isc_nm_resumeread +isc_nm_send +isc_nm_setloadbalancesockets +isc_nm_setstats +isc_nm_settimeouts +isc_nm_stoplistening +isc_nm_task_enqueue +isc_nm_tcpconnect +isc_nm_tcpdnsconnect +isc_nm_tcpdns_sequential +isc_nm_tid +isc_nm_timer_create +isc_nm_timer_attach +isc_nm_timer_detach +isc_nm_timer_start +isc_nm_timer_stop +isc_nm_udpconnect +isc_nm_work_offload +isc_nmsocket_close +isc__nm_acquire_interlocked +isc__nm_drop_interlocked +isc__nm_acquire_interlocked_force +isc_nonce_buf +isc_ntpaths_get +isc_ntpaths_init +isc_once_do +isc_os_ncpus +isc_parse_uint16 +isc_parse_uint32 +isc_parse_uint8 +isc_pool_count +isc_pool_create +isc_pool_destroy +isc_pool_expand +isc_pool_get +isc_portset_add +isc_portset_addrange +isc_portset_create +isc_portset_destroy +isc_portset_isset +isc_portset_nports +isc_portset_remove +isc_portset_removerange +isc_quota_attach +isc_quota_attach_cb +isc_quota_cb_init +isc_quota_destroy +isc_quota_detach +isc_quota_getmax +isc_quota_getsoft +isc_quota_getused +isc_quota_init +isc_quota_max +isc_quota_soft +isc_radix_create +isc_radix_destroy +isc_radix_insert +isc_radix_process +isc_radix_remove +isc_radix_search +isc_random8 +isc_random16 +isc_random32 +isc_random_buf +isc_random_uniform +isc_ratelimiter_attach +isc_ratelimiter_create +isc_ratelimiter_dequeue +isc_ratelimiter_detach +isc_ratelimiter_enqueue +isc_ratelimiter_release +isc_ratelimiter_setinterval +isc_ratelimiter_setpertic +isc_ratelimiter_setpushpop +isc_ratelimiter_shutdown +isc_ratelimiter_stall +isc_regex_validate +isc_region_compare +isc_resource_getcurlimit +isc_resource_getlimit +isc_resource_setlimit +isc_result_register +isc_result_registerids +isc_result_toid +isc_result_totext +isc_rwlock_destroy +isc_rwlock_downgrade +isc_rwlock_init +isc_rwlock_lock +isc_rwlock_trylock +isc_rwlock_tryupgrade +isc_rwlock_unlock +isc_safe_memequal +isc_safe_memwipe +isc_serial_eq +isc_serial_ge +isc_serial_gt +isc_serial_le +isc_serial_lt +isc_serial_ne +isc_halfsiphash24 +isc_siphash24 +isc_sockaddr_any +isc_sockaddr_any6 +isc_sockaddr_anyofpf +isc_sockaddr_compare +isc_sockaddr_eqaddr +isc_sockaddr_eqaddrprefix +isc_sockaddr_equal +isc_sockaddr_format +isc_sockaddr_fromin +isc_sockaddr_fromin6 +isc_sockaddr_fromnetaddr +isc_sockaddr_frompath +isc_sockaddr_fromsockaddr +isc_sockaddr_getport +isc_sockaddr_hash +isc_sockaddr_isexperimental +isc_sockaddr_islinklocal +isc_sockaddr_ismulticast +isc_sockaddr_isnetzero +isc_sockaddr_issitelocal +isc_sockaddr_pf +isc_sockaddr_setport +isc_sockaddr_totext +isc_sockaddr_v6fromin +isc_socket_socketevent +isc_socketmgr_maxudp +@IF NOTYET +isc_socketmgr_renderjson +@END NOTYET +@IF LIBXML2 +isc_socketmgr_renderxml +@END LIBXML2 +isc_stats_attach +isc_stats_create +isc_stats_decrement +isc_stats_detach +isc_stats_dump +isc_stats_get_counter +isc_stats_increment +isc_stats_ncounters +isc_stats_resize +isc_stats_set +isc_stats_update_if_greater +isc_stdio_close +isc_stdio_flush +isc_stdio_open +isc_stdio_read +isc_stdio_seek +isc_stdio_sync +isc_stdio_tell +isc_stdio_write +isc_stdtime_get +isc_stdtime_tostring +isc_string_strerror_r +isc_symtab_count +isc_symtab_create +isc_symtab_define +isc_symtab_destroy +isc_symtab_lookup +isc_symtab_undefine +isc_syslog_facilityfromstring +isc_task_attach +isc_task_beginexclusive +isc_task_create +isc_task_create_bound +isc_task_destroy +isc_task_detach +isc_task_endexclusive +isc_task_exiting +isc_task_getname +isc_task_unsendrange +isc_task_getcurrenttime +isc_task_getcurrenttimex +isc_task_getnetmgr +isc_task_getprivilege +isc_task_gettag +isc_task_onshutdown +isc_task_pause +isc_task_privileged +isc_task_purge +isc_task_purgeevent +isc_task_purgerange +isc_task_ready +isc_task_run +isc_task_send +isc_task_sendanddetach +isc_task_sendto +isc_task_sendtoanddetach +isc_task_setname +isc_task_setprivilege +isc_task_setquantum +isc_task_shutdown +isc_task_unpause +isc_task_unsend +isc_taskmgr_attach +isc_taskmgr_detach +isc_taskmgr_excltask +isc_taskmgr_mode +@IF NOTYET +isc_taskmgr_renderjson +@END NOTYET +@IF LIBXML2 +isc_taskmgr_renderxml +@END LIBXML2 +isc_taskmgr_setexcltask +isc_taskmgr_setmode +isc_taskpool_create +isc_taskpool_destroy +isc_taskpool_expand +isc_taskpool_gettask +isc_taskpool_size +isc_thread_create +isc_thread_join +isc_thread_setaffinity +isc_thread_setconcurrency +isc_thread_setname +gmtime_r +localtime_r +nanosleep +usleep +isc_time_add +isc_time_compare +isc_time_formatISO8601 +isc_time_formatISO8601L +isc_time_formatISO8601Lms +isc_time_formatISO8601Lus +isc_time_formatISO8601ms +isc_time_formatISO8601us +isc_time_formathttptimestamp +isc_time_formatshorttimestamp +isc_time_formattimestamp +isc_time_isepoch +isc_time_microdiff +isc_time_nanoseconds +isc_time_now +isc_time_now_hires +isc_time_nowplusinterval +isc_time_parsehttptimestamp +isc_time_secondsastimet +isc_time_seconds +isc_time_set +isc_time_settoepoch +isc_time_subtract +isc_timer_create +isc_timer_destroy +isc_timer_gettype +isc_timer_reset +isc_timer_touch +isc_timermgr_create +isc_timermgr_destroy +isc_timermgr_poke +isc__tls_initialize +isc__tls_shutdown +isc__trampoline_initialize +isc__trampoline_shutdown +isc__trampoline_get +isc__trampoline_run +isc_tm_timegm +isc_tm_strptime +isc_url_parse +isc_utf8_bom +isc_utf8_valid +isc_win32os_versioncheck +openlog +@IF PKCS11 +pk11_attribute_bytype +pk11_attribute_first +pk11_attribute_next +pk11_dump_tokens +pk11_error_fatalcheck +pk11_finalize +pk11_get_best_token +pk11_get_lib_name +pk11_get_load_error_message +pk11_get_session +pk11_initialize +pk11_mem_get +pk11_mem_put +pk11_numbits +pk11_parse_uri +pk11_result_register +pk11_result_totext +pk11_return_session +pk11_set_lib_name +pkcs_C_CloseSession +pkcs_C_CreateObject +pkcs_C_DeriveKey +pkcs_C_DestroyObject +pkcs_C_DigestFinal +pkcs_C_DigestInit +pkcs_C_DigestUpdate +pkcs_C_Encrypt +pkcs_C_EncryptInit +pkcs_C_Finalize +pkcs_C_FindObjects +pkcs_C_FindObjectsFinal +pkcs_C_FindObjectsInit +pkcs_C_GenerateKey +pkcs_C_GenerateKeyPair +pkcs_C_GenerateRandom +pkcs_C_GetAttributeValue +pkcs_C_GetMechanismInfo +pkcs_C_GetSlotList +pkcs_C_GetTokenInfo +pkcs_C_Initialize +pkcs_C_Login +pkcs_C_Logout +pkcs_C_OpenSession +pkcs_C_SeedRandom +pkcs_C_SetAttributeValue +pkcs_C_Sign +pkcs_C_SignFinal +pkcs_C_SignInit +pkcs_C_SignUpdate +pkcs_C_Verify +pkcs_C_VerifyFinal +pkcs_C_VerifyInit +pkcs_C_VerifyUpdate +@END PKCS11 +strlcat +strlcpy +strnstr +syslog + +@IF NOLONGER +; Exported Data + +EXPORTS + +isc__backtrace_nsymbols DATA +isc__backtrace_symtable DATA +isc_bind9 DATA +isc_commandline_argument DATA +isc_commandline_errprint DATA +isc_commandline_index DATA +isc_commandline_option DATA +isc_commandline_progname DATA +isc_commandline_reset DATA +isc_dscp_check_value DATA +isc_hashctx DATA +isc_mem_debugging DATA +isc_tid_v DATA +@IF PKCS11 +pk11_verbose_init DATA +@END PKCS11 +@END NOLONGER diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in new file mode 100644 index 0000000..75a4c80 --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -0,0 +1,671 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {289562c2-1bdd-4582-b6bd-3f598ee23cbd} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {d03c3e6a-e78e-4a01-bd77-64c839b1adfe} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Win32 Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + +@IF PKCS11 + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Library Header Files + + + Pkcs11 Header Files + +@END PKCS11 + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Win32 Header Files + + + Library Header Files + + + Library Header Files + + + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + + + Win32 Source Files + +@IF PKCS11 + + Win32 Source Files + +@END PKCS11 + + Win32 Source Files + + + Win32 Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + + + Library Source Files + +@IF PKCS11 + + Library Source Files + + + Library Source Files + +@END PKCS11 + + diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in new file mode 100644 index 0000000..8fd9381 --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.in @@ -0,0 +1,483 @@ + + + + + Debug + @PLATFORM@ + + + Release + @PLATFORM@ + + + + {3840E563-D180-4761-AA9C-E6155F02EAFF} + Win32Proj + libisc + @WINDOWS_TARGET_PLATFORM_VERSION@ + + + + DynamicLibrary + true + MultiByte + @PLATFORM_TOOLSET@ + + + DynamicLibrary + false + true + MultiByte + @PLATFORM_TOOLSET@ + + + + + + + + + + + + + true + ..\..\..\Build\$(Configuration)\ + .\$(Configuration)\ + None + + + false + ..\..\..\Build\$(Configuration)\ + .\$(Configuration)\ + None + + + + + + Level4 + false + Disabled +@IF PKCS11 + BIND9;@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) + ..\..\..\config.h + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) +@ELSE PKCS11 + BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) + ..\..\..\config.h + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) +@END PKCS11 + true + .\$(Configuration)\$(TargetName).pch + .\$(Configuration)\ + .\$(Configuration)\ + $(OutDir)$(TargetName).pdb + true + CompileAsC + + + Console + true + ..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt) + @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) + $(ProjectName).def + .\$(Configuration)\$(ProjectName).lib + + + cd ..\..\..\win32utils + +if NOT Exist ..\Build mkdir ..\Build +if NOT Exist ..\Build\Debug mkdir ..\Build\Debug + +echo Copying documentation. + +copy ..\*.md ..\Build\Debug +copy ..\CHANGES* ..\Build\Debug +copy ..\COPYRIGHT ..\Build\Debug +copy ..\LICENSE ..\Build\Debug + +echo Copying COPYRIGHT notice. + +copy ..\COPYRIGHT ..\Build\Debug + +echo Copying the OpenSSL DLL and LICENSE. + +copy @OPENSSL_DLLCRYPTO@ ..\Build\Debug\ +copy @OPENSSL_DLLSSL@ ..\Build\Debug\ +copy @OPENSSL_PATH@\LICENSE ..\Build\Debug\OpenSSL-LICENSE + +echo Copying libuv DLL. +copy @LIBUV_DLL@ ..\Build\Debug\ + +@IF LIBXML2 +echo Copying the libxml DLL. + +copy @LIBXML2_DLL@ ..\Build\Debug\ +@END LIBXML2 + +@IF GSSAPI +echo Copying the GSSAPI and KRB5 DLLs. + +copy @GSSAPI_DLL@ ..\Build\Debug\ +copy @KRB5_DLL@ ..\Build\Debug\ +copy @COMERR_DLL@ ..\Build\Debug\ +copy @K5SPRT_DLL@ ..\Build\Debug\ +copy @WSHELP_DLL@ ..\Build\Debug\ +@END GSSAPI + +@IF IDNKIT +echo Copying the IDN kit DLL. + +copy @IDN_DLL@ ..\Build\Debug\ +copy @ICONV_DLL@ ..\Build\Debug\ +@END IDNKIT + +@IF ZLIB +echo Copying the zlib DLL. + +copy @ZLIB_DLL@ ..\Build\Debug\ +@END ZLIB + +echo Copying Visual C x86 Redistributable Installer. + +copy /Y @VCREDIST_PATH@ ..\Build\Debug\ + +echo Copying install files (flags and file list). + +copy InstallFlags ..\Build\Debug\ +copy InstallFiles ..\Build\Debug\ + + + + + + + Level1 + true + + + MaxSpeed + true + @INTRINSIC@ +@IF PKCS11 + BIND9;@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) + ..\..\..\config.h + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories) +@ELSE PKCS11 + BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions) + ..\..\..\config.h + .\;..\..\..\;@LIBXML2_INC@@LIBUV_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;..\;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories) +@END PKCS11 + OnlyExplicitInline + false + true + .\$(Configuration)\$(TargetName).pch + .\$(Configuration)\ + .\$(Configuration)\ + $(OutDir)$(TargetName).pdb + CompileAsC + + + Console + false + true + true + ..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt) + @OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@@LIBUV_LIB@@LIBXML2_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies) + $(ProjectName).def + .\$(Configuration)\$(ProjectName).lib + Default + + + cd ..\..\..\win32utils + +if NOT Exist ..\Build mkdir ..\Build +if NOT Exist ..\Build\Release mkdir ..\Build\Release + +echo Copying documentation. + +copy ..\*.md ..\Build\Release +copy ..\CHANGES* ..\Build\Release +copy ..\COPYRIGHT ..\Build\Release +copy ..\LICENSE ..\Build\Release + +echo Copying the OpenSSL DLL and LICENSE. + +copy @OPENSSL_DLLCRYPTO@ ..\Build\Release\ +copy @OPENSSL_DLLSSL@ ..\Build\Release\ +copy @OPENSSL_PATH@\LICENSE ..\Build\Release\OpenSSL-LICENSE + +echo Copying libuv DLL. +copy @LIBUV_DLL@ ..\Build\Release\ + +@IF LIBXML2 +echo Copying the libxml DLL. + +copy @LIBXML2_DLL@ ..\Build\Release\ +@END LIBXML2 + +@IF GSSAPI +echo Copying the GSSAPI and KRB5 DLLs. + +copy @GSSAPI_DLL@ ..\Build\Release\ +copy @KRB5_DLL@ ..\Build\Release\ +copy @COMERR_DLL@ ..\Build\Release\ +copy @K5SPRT_DLL@ ..\Build\Release\ +copy @WSHELP_DLL@ ..\Build\Release\ +@END GSSAPI + +@IF IDNKIT +echo Copying the IDN kit DLL. + +copy @IDN_DLL@ ..\Build\Release\ +copy @ICONV_DLL@ ..\Build\Release\ +@END IDNKIT + +@IF ZLIB +echo Copying the zlib DLL. + +copy @ZLIB_DLL@ ..\Build\Release\ +@END ZLIB + +echo Copying Visual C x86 Redistributable Installer. + +copy /Y @VCREDIST_PATH@ ..\Build\Release\ + +echo Copying install files (flags and file list). + +copy InstallFlags ..\Build\Release\ +copy InstallFiles ..\Build\Release\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@IF PKCS11 + + + + + +@END PKCS11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@IF PKCS11 + + +@END PKCS11 + + + + + + + + + + + + + + + + + + + + + + + +@IF PKCS11 + +@END PKCS11 + + + + + diff --git a/lib/isc/win32/libisc.vcxproj.user b/lib/isc/win32/libisc.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/lib/isc/win32/meminfo.c b/lib/isc/win32/meminfo.c new file mode 100644 index 0000000..ab10b89 --- /dev/null +++ b/lib/isc/win32/meminfo.c @@ -0,0 +1,26 @@ +/* + * 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 +#include + +#include + +uint64_t +isc_meminfo_totalphys(void) { + MEMORYSTATUSEX statex; + + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + return ((uint64_t)statex.ullTotalPhys); +} diff --git a/lib/isc/win32/net.c b/lib/isc/win32/net.c new file mode 100644 index 0000000..ea4656d --- /dev/null +++ b/lib/isc/win32/net.c @@ -0,0 +1,301 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include + +/*% + * Definitions about UDP port range specification. This is a total mess of + * portability variants: some use sysctl (but the sysctl names vary), some use + * system-specific interfaces, some have the same interface for IPv4 and IPv6, + * some separate them, etc... + */ + +/*% + * The last resort defaults: use all non well known port space + */ +#ifndef ISC_NET_PORTRANGELOW +#define ISC_NET_PORTRANGELOW 32768 +#endif /* ISC_NET_PORTRANGELOW */ +#ifndef ISC_NET_PORTRANGEHIGH +#define ISC_NET_PORTRANGEHIGH 65535 +#endif /* ISC_NET_PORTRANGEHIGH */ + +static isc_once_t once = ISC_ONCE_INIT; +static isc_once_t once_ipv6only = ISC_ONCE_INIT; +static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; +static isc_result_t ipv4_result = ISC_R_NOTFOUND; +static isc_result_t ipv6_result = ISC_R_NOTFOUND; +static isc_result_t ipv6only_result = ISC_R_NOTFOUND; +static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; + +void +InitSockets(void); + +static isc_result_t +try_proto(int domain) { + SOCKET s; + char strbuf[ISC_STRERRORSIZE]; + int errval; + + s = socket(domain, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + errval = WSAGetLastError(); + switch (errval) { + case WSAEAFNOSUPPORT: + case WSAEPROTONOSUPPORT: + case WSAEINVAL: + return (ISC_R_NOTFOUND); + default: + strerror_r(errval, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() failed: %s", strbuf); + return (ISC_R_UNEXPECTED); + } + } + + closesocket(s); + + return (ISC_R_SUCCESS); +} + +static void +initialize_action(void) { + InitSockets(); + ipv4_result = try_proto(PF_INET); + ipv6_result = try_proto(PF_INET6); +} + +static void +initialize(void) { + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); +} + +isc_result_t +isc_net_probeipv4(void) { + initialize(); + return (ipv4_result); +} + +isc_result_t +isc_net_probeipv6(void) { + initialize(); + return (ipv6_result); +} + +isc_result_t +isc_net_probeunix(void) { + return (ISC_R_NOTFOUND); +} + +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + SOCKET s; + int on; + char strbuf[ISC_STRERRORSIZE]; +#endif /* ifdef IPV6_V6ONLY */ + isc_result_t result; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6only_result = result; + return; + } + +#ifndef IPV6_V6ONLY + ipv6only_result = ISC_R_NOTFOUND; + return; +#else /* ifndef IPV6_V6ONLY */ + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, + sizeof(on)) < 0) + { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + closesocket(s); + + /* check for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s == INVALID_SOCKET) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&on, + sizeof(on)) < 0) + { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6only_result = ISC_R_SUCCESS; + +close: + closesocket(s); + return; +#endif /* IPV6_V6ONLY */ +} + +static void +initialize_ipv6only(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) == + ISC_R_SUCCESS); +} + +#ifdef __notyet__ +/* + * XXXMPA requires win32/socket.c to be updated to support + * WSASendMsg and WSARecvMsg which are themselves Winsock + * and compiler version dependent. + */ +static void +try_ipv6pktinfo(void) { + SOCKET s; + int on; + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result; + int optname; + + result = isc_net_probeipv6(); + if (result != ISC_R_SUCCESS) { + ipv6pktinfo_result = result; + return; + } + + /* we only use this for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == INVALID_SOCKET) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s", + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else /* ifdef IPV6_RECVPKTINFO */ + optname = IPV6_PKTINFO; +#endif /* ifdef IPV6_RECVPKTINFO */ + on = 1; + if (setsockopt(s, IPPROTO_IPV6, optname, (const char *)&on, + sizeof(on)) < 0) + { + ipv6pktinfo_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6pktinfo_result = ISC_R_SUCCESS; + +close: + closesocket(s); + return; +} + +static void +initialize_ipv6pktinfo(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo) == + ISC_R_SUCCESS); +} +#endif /* __notyet__ */ + +isc_result_t +isc_net_probe_ipv6only(void) { + initialize_ipv6only(); + return (ipv6only_result); +} + +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +#ifdef __notyet__ + initialize_ipv6pktinfo(); +#endif /* __notyet__ */ + return (ipv6pktinfo_result); +} + +isc_result_t +isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { + int result = ISC_R_FAILURE; + + REQUIRE(low != NULL && high != NULL); + + UNUSED(af); + + if (result != ISC_R_SUCCESS) { + *low = ISC_NET_PORTRANGELOW; + *high = ISC_NET_PORTRANGEHIGH; + } + + return (ISC_R_SUCCESS); /* we currently never fail in this function */ +} + +void +isc_net_disableipv4(void) { + initialize(); + if (ipv4_result == ISC_R_SUCCESS) { + ipv4_result = ISC_R_DISABLED; + } +} + +void +isc_net_disableipv6(void) { + initialize(); + if (ipv6_result == ISC_R_SUCCESS) { + ipv6_result = ISC_R_DISABLED; + } +} + +void +isc_net_enableipv4(void) { + initialize(); + if (ipv4_result == ISC_R_DISABLED) { + ipv4_result = ISC_R_SUCCESS; + } +} + +void +isc_net_enableipv6(void) { + initialize(); + if (ipv6_result == ISC_R_DISABLED) { + ipv6_result = ISC_R_SUCCESS; + } +} + +unsigned int +isc_net_probedscp(void) { + return (0); +} diff --git a/lib/isc/win32/netdb.h b/lib/isc/win32/netdb.h new file mode 100644 index 0000000..5a5c122 --- /dev/null +++ b/lib/isc/win32/netdb.h @@ -0,0 +1,182 @@ +/* + * 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. + */ + +#ifndef NETDB_H +#define NETDB_H 1 + +#include +#include + +/* + * Define if does not declare struct addrinfo. + */ + +#if _MSC_VER < 1600 +struct addrinfo { + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and + * IPv6 */ + size_t ai_addrlen; /* Length of ai_addr */ + char *ai_canonname; /* Canonical name for hostname */ + struct sockaddr *ai_addr; /* Binary address */ + struct addrinfo *ai_next; /* Next structure in linked list */ +}; +#endif /* if _MSC_VER < 1600 */ + +/* + * Undefine all \#defines we are interested in as may or may not have + * defined them. + */ + +/* + * Error return codes from gethostbyname() and gethostbyaddr() + * (left in extern int h_errno). + */ + +#undef NETDB_INTERNAL +#undef NETDB_SUCCESS +#undef HOST_NOT_FOUND +#undef TRY_AGAIN +#undef NO_RECOVERY +#undef NO_DATA +#undef NO_ADDRESS + +#define NETDB_INTERNAL -1 /* see errno */ +#define NETDB_SUCCESS 0 /* no problem */ +#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */ +#define TRY_AGAIN 2 /* Non-Authoritative Host not found, or SERVERFAIL */ +#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ +#define NO_DATA 4 /* Valid name, no data record of requested type */ +#define NO_ADDRESS NO_DATA /* no address, look for MX record */ + +/* + * Error return codes from getaddrinfo() + */ + +#undef EAI_ADDRFAMILY +#undef EAI_AGAIN +#undef EAI_BADFLAGS +#undef EAI_FAIL +#undef EAI_FAMILY +#undef EAI_MEMORY +#undef EAI_NODATA +#undef EAI_NONAME +#undef EAI_SERVICE +#undef EAI_SOCKTYPE +#undef EAI_SYSTEM +#undef EAI_BADHINTS +#undef EAI_PROTOCOL +#undef EAI_MAX + +#define EAI_ADDRFAMILY 1 /* address family for hostname not supported */ +#define EAI_AGAIN 2 /* temporary failure in name resolution */ +#define EAI_BADFLAGS 3 /* invalid value for ai_flags */ +#define EAI_FAIL 4 /* non-recoverable failure in name resolution */ +#define EAI_FAMILY 5 /* ai_family not supported */ +#define EAI_MEMORY 6 /* memory allocation failure */ +#define EAI_NODATA 7 /* no address associated with hostname */ +#define EAI_NONAME 8 /* hostname nor servname provided, or not known */ +#define EAI_SERVICE 9 /* servname not supported for ai_socktype */ +#define EAI_SOCKTYPE 10 /* ai_socktype not supported */ +#define EAI_SYSTEM 11 /* system error returned in errno */ +#define EAI_BADHINTS 12 +#define EAI_PROTOCOL 13 +#define EAI_MAX 14 + +/* + * Flag values for getaddrinfo() + */ +#undef AI_PASSIVE +#undef AI_CANONNAME +#undef AI_NUMERICHOST + +#define AI_PASSIVE 0x00000001 +#define AI_CANONNAME 0x00000002 +#define AI_NUMERICHOST 0x00000004 + +/* + * Flag values for getipnodebyname() + */ +#undef AI_V4MAPPED +#undef AI_ALL +#undef AI_ADDRCONFIG +#undef AI_DEFAULT + +#define AI_V4MAPPED 0x00000008 +#define AI_ALL 0x00000010 +#define AI_ADDRCONFIG 0x00000020 +#define AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG) + +/* + * Constants for getnameinfo() + */ +#undef NI_MAXHOST +#undef NI_MAXSERV + +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 + +/* + * Flag values for getnameinfo() + */ +#undef NI_NOFQDN +#undef NI_NUMERICHOST +#undef NI_NAMEREQD +#undef NI_NUMERICSERV +#undef NI_DGRAM +#undef NI_NUMERICSCOPE + +#define NI_NOFQDN 0x00000001 +#define NI_NUMERICHOST 0x00000002 +#define NI_NAMEREQD 0x00000004 +#define NI_NUMERICSERV 0x00000008 +#define NI_DGRAM 0x00000010 +#define NI_NUMERICSCOPE 0x00000020 /*2553bis-00*/ + +/* + * Structures for getrrsetbyname() + */ +struct rdatainfo { + unsigned int rdi_length; + unsigned char *rdi_data; +}; + +struct rrsetinfo { + unsigned int rri_flags; + int rri_rdclass; + int rri_rdtype; + unsigned int rri_ttl; + unsigned int rri_nrdatas; + unsigned int rri_nsigs; + char *rri_name; + struct rdatainfo *rri_rdatas; + struct rdatainfo *rri_sigs; +}; + +/* + * Flags for getrrsetbyname() + */ +#define RRSET_VALIDATED 0x00000001 +/* Set was dnssec validated */ + +/* + * Return codes for getrrsetbyname() + */ +#define ERRSET_SUCCESS 0 +#define ERRSET_NOMEMORY 1 +#define ERRSET_FAIL 2 +#define ERRSET_INVAL 3 + +#endif /* NETDB_H */ diff --git a/lib/isc/win32/ntgroups.c b/lib/isc/win32/ntgroups.c new file mode 100644 index 0000000..0584d48 --- /dev/null +++ b/lib/isc/win32/ntgroups.c @@ -0,0 +1,215 @@ +/* + * 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. + */ + +/* + * The NT Groups have two groups that are not well documented and are + * not normally seen: None and Everyone. A user account belongs to + * any number of groups, but if it is not a member of any group then + * it is a member of the None Group. The None group is not listed + * anywhere. You cannot remove an account from the none group except + * by making it a member of some other group, The second group is the + * Everyone group. All accounts, no matter how many groups that they + * belong to, also belong to the Everyone group. You cannot remove an + * account from the Everyone group. + */ + +#ifndef UNICODE +#define UNICODE +#endif /* UNICODE */ + +/* + * Silence warnings. + */ +#define _CRT_SECURE_NO_DEPRECATE 1 + +/* clang-format off */ +#include +#include +#include +/* clang-format on */ + +#include +#include + +#define MAX_NAME_LENGTH 256 + +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) { \ + goto cleanup; \ + } \ + } while (0) + +isc_result_t +isc_ntsecurity_getaccountgroups(char *username, char **GroupList, + unsigned int maxgroups, + unsigned int *totalGroups) { + LPGROUP_USERS_INFO_0 pTmpBuf; + LPLOCALGROUP_USERS_INFO_0 pTmpLBuf; + DWORD i; + LPLOCALGROUP_USERS_INFO_0 pBuf = NULL; + LPGROUP_USERS_INFO_0 pgrpBuf = NULL; + DWORD dwLevel = 0; + DWORD dwFlags = LG_INCLUDE_INDIRECT; + DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH; + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus; + size_t retlen; + wchar_t user[MAX_NAME_LENGTH]; + isc_result_t result; + + *totalGroups = 0; + + retlen = mbstowcs(user, username, MAX_NAME_LENGTH); + if (retlen == (size_t)(-1)) { + return (ISC_R_FAILURE); + } + + /* + * Call the NetUserGetLocalGroups function + * specifying information level 0. + * + * The LG_INCLUDE_INDIRECT flag specifies that the + * function should also return the names of the local + * groups in which the user is indirectly a member. + */ + nStatus = NetUserGetLocalGroups(NULL, user, dwLevel, dwFlags, + (LPBYTE *)&pBuf, dwPrefMaxLen, + &dwEntriesRead, &dwTotalEntries); + /* + * See if the call succeeds, + */ + if (nStatus != NERR_Success) { + if (nStatus == ERROR_ACCESS_DENIED) { + return (ISC_R_NOPERM); + } + if (nStatus == ERROR_MORE_DATA) { + return (ISC_R_NOSPACE); + } + if (nStatus == NERR_UserNotFound) { + dwEntriesRead = 0; + } + } + + if (pBuf != NULL) { + pTmpLBuf = pBuf; + /* + * Loop through the entries + */ + for (i = 0; (i < dwEntriesRead && *totalGroups < maxgroups); + i++) + { + assert(pTmpLBuf != NULL); + if (pTmpLBuf == NULL) { + break; + } + retlen = wcslen(pTmpLBuf->lgrui0_name); + GroupList[*totalGroups] = (char *)malloc(retlen + 1); + if (GroupList[*totalGroups] == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + retlen = wcstombs(GroupList[*totalGroups], + pTmpLBuf->lgrui0_name, retlen); + if (retlen == (size_t)(-1)) { + free(GroupList[*totalGroups]); + CHECK(ISC_R_FAILURE); + } + GroupList[*totalGroups][retlen] = '\0'; + if (strcmp(GroupList[*totalGroups], "None") == 0) { + free(GroupList[*totalGroups]); + } else { + (*totalGroups)++; + } + pTmpLBuf++; + } + } + /* Free the allocated memory. */ + /* cppcheck-suppress duplicateCondition */ + if (pBuf != NULL) { + NetApiBufferFree(pBuf); + } + + /* + * Call the NetUserGetGroups function, specifying level 0. + */ + nStatus = NetUserGetGroups(NULL, user, dwLevel, (LPBYTE *)&pgrpBuf, + dwPrefMaxLen, &dwEntriesRead, + &dwTotalEntries); + /* + * See if the call succeeds, + */ + if (nStatus != NERR_Success) { + if (nStatus == ERROR_ACCESS_DENIED) { + return (ISC_R_NOPERM); + } + if (nStatus == ERROR_MORE_DATA) { + return (ISC_R_NOSPACE); + } + if (nStatus == NERR_UserNotFound) { + dwEntriesRead = 0; + } + } + + if (pgrpBuf != NULL) { + pTmpBuf = pgrpBuf; + /* + * Loop through the entries + */ + for (i = 0; (i < dwEntriesRead && *totalGroups < maxgroups); + i++) + { + assert(pTmpBuf != NULL); + + if (pTmpBuf == NULL) { + break; + } + retlen = wcslen(pTmpBuf->grui0_name); + GroupList[*totalGroups] = (char *)malloc(retlen + 1); + if (GroupList[*totalGroups] == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + retlen = wcstombs(GroupList[*totalGroups], + pTmpBuf->grui0_name, retlen); + if (retlen == (size_t)(-1)) { + free(GroupList[*totalGroups]); + CHECK(ISC_R_FAILURE); + } + GroupList[*totalGroups][retlen] = '\0'; + if (strcmp(GroupList[*totalGroups], "None") == 0) { + free(GroupList[*totalGroups]); + } else { + (*totalGroups)++; + } + pTmpBuf++; + } + } + /* + * Free the allocated memory. + */ + /* cppcheck-suppress duplicateCondition */ + if (pgrpBuf != NULL) { + NetApiBufferFree(pgrpBuf); + } + + return (ISC_R_SUCCESS); + +cleanup: + while (--(*totalGroups) > 0) { + free(GroupList[*totalGroups]); + } + return (result); +} diff --git a/lib/isc/win32/ntpaths.c b/lib/isc/win32/ntpaths.c new file mode 100644 index 0000000..793b72a --- /dev/null +++ b/lib/isc/win32/ntpaths.c @@ -0,0 +1,151 @@ +/* + * 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. + */ + +/* + * This module fetches the required path information that is specific + * to NT systems which can have its configuration and system files + * almost anywhere. It can be used to override whatever the application + * had previously assigned to the pointer. Basic information about the + * file locations are stored in the registry. + */ + +#include +#include +#include + +/* + * Module Variables + */ + +static char systemDir[MAX_PATH]; +static char namedBase[MAX_PATH]; +static char ns_confFile[MAX_PATH]; +static char rndc_confFile[MAX_PATH]; +static char ns_defaultpidfile[MAX_PATH]; +static char ns_lockfile[MAX_PATH]; +static char local_state_dir[MAX_PATH]; +static char sys_conf_dir[MAX_PATH]; +static char rndc_keyFile[MAX_PATH]; +static char session_keyFile[MAX_PATH]; +static char resolv_confFile[MAX_PATH]; +static char bind_keysFile[MAX_PATH]; + +static DWORD baseLen = MAX_PATH; +static BOOL Initialized = FALSE; + +void +isc_ntpaths_init(void) { + HKEY hKey; + BOOL keyFound = TRUE; + + memset(namedBase, 0, sizeof(namedBase)); + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, BIND_SUBKEY, 0, KEY_READ, &hKey) != + ERROR_SUCCESS) + { + keyFound = FALSE; + } + + if (keyFound == TRUE) { + /* Get the named directory */ + if (RegQueryValueEx(hKey, "InstallDir", NULL, NULL, + (LPBYTE)namedBase, + &baseLen) != ERROR_SUCCESS) + { + keyFound = FALSE; + } + RegCloseKey(hKey); + } + + GetSystemDirectory(systemDir, MAX_PATH); + + if (keyFound == FALSE) { + /* Use the System Directory as a default */ + strlcpy(namedBase, systemDir, sizeof(namedBase)); + } + + strlcpy(ns_confFile, namedBase, sizeof(ns_confFile)); + strlcat(ns_confFile, "\\etc\\named.conf", sizeof(ns_confFile)); + + strlcpy(rndc_keyFile, namedBase, sizeof(rndc_keyFile)); + strlcat(rndc_keyFile, "\\etc\\rndc.key", sizeof(rndc_keyFile)); + + strlcpy(session_keyFile, namedBase, sizeof(session_keyFile)); + strlcat(session_keyFile, "\\etc\\session.key", sizeof(session_keyFile)); + + strlcpy(rndc_confFile, namedBase, sizeof(rndc_confFile)); + strlcat(rndc_confFile, "\\etc\\rndc.conf", sizeof(rndc_confFile)); + + strlcpy(ns_defaultpidfile, namedBase, sizeof(ns_defaultpidfile)); + strlcat(ns_defaultpidfile, "\\etc\\named.pid", + sizeof(ns_defaultpidfile)); + + strlcpy(ns_lockfile, namedBase, sizeof(ns_lockfile)); + strlcat(ns_lockfile, "\\etc\\named.lock", sizeof(ns_lockfile)); + + strlcpy(local_state_dir, namedBase, sizeof(local_state_dir)); + strlcat(local_state_dir, "\\bin", sizeof(local_state_dir)); + + strlcpy(sys_conf_dir, namedBase, sizeof(sys_conf_dir)); + strlcat(sys_conf_dir, "\\etc", sizeof(sys_conf_dir)); + + /* Added to avoid an assert on NULL value */ + strlcpy(resolv_confFile, namedBase, sizeof(resolv_confFile)); + strlcat(resolv_confFile, "\\etc\\resolv.conf", sizeof(resolv_confFile)); + + strlcpy(bind_keysFile, namedBase, sizeof(bind_keysFile)); + strlcat(bind_keysFile, "\\etc\\bind.keys", sizeof(bind_keysFile)); + + Initialized = TRUE; +} + +char * +isc_ntpaths_get(int ind) { + if (!Initialized) { + isc_ntpaths_init(); + } + + switch (ind) { + case NAMED_CONF_PATH: + return (ns_confFile); + break; + case RESOLV_CONF_PATH: + return (resolv_confFile); + break; + case RNDC_CONF_PATH: + return (rndc_confFile); + break; + case NAMED_PID_PATH: + return (ns_defaultpidfile); + break; + case NAMED_LOCK_PATH: + return (ns_lockfile); + break; + case LOCAL_STATE_DIR: + return (local_state_dir); + break; + case SYS_CONF_DIR: + return (sys_conf_dir); + break; + case RNDC_KEY_PATH: + return (rndc_keyFile); + break; + case SESSION_KEY_PATH: + return (session_keyFile); + break; + case BIND_KEYS_PATH: + return (bind_keysFile); + break; + default: + return (NULL); + } +} diff --git a/lib/isc/win32/once.c b/lib/isc/win32/once.c new file mode 100644 index 0000000..23966cb --- /dev/null +++ b/lib/isc/win32/once.c @@ -0,0 +1,43 @@ +/* + * 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 + +#include +#include +#include + +isc_result_t +isc_once_do(isc_once_t *controller, void (*function)(void)) { + REQUIRE(controller != NULL && function != NULL); + + if (controller->status == ISC_ONCE_INIT_NEEDED) { + if (InterlockedDecrement(&controller->counter) == 0) { + if (controller->status == ISC_ONCE_INIT_NEEDED) { + function(); + controller->status = ISC_ONCE_INIT_DONE; + } + } else { + while (controller->status == ISC_ONCE_INIT_NEEDED) { + /* + * Sleep(0) indicates that this thread + * should be suspended to allow other + * waiting threads to execute. + */ + Sleep(0); + } + } + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/win32/os.c b/lib/isc/win32/os.c new file mode 100644 index 0000000..19a992e --- /dev/null +++ b/lib/isc/win32/os.c @@ -0,0 +1,41 @@ +/* + * 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 + +#include + +static BOOL bInit = FALSE; +static SYSTEM_INFO SystemInfo; + +static void +initialize_action(void) { + if (bInit) { + return; + } + + GetSystemInfo(&SystemInfo); + bInit = TRUE; +} + +unsigned int +isc_os_ncpus(void) { + long ncpus; + initialize_action(); + ncpus = SystemInfo.dwNumberOfProcessors; + if (ncpus <= 0) { + ncpus = 1; + } + + return ((unsigned int)ncpus); +} diff --git a/lib/isc/win32/pk11_api.c b/lib/isc/win32/pk11_api.c new file mode 100644 index 0000000..5ee8e0d --- /dev/null +++ b/lib/isc/win32/pk11_api.c @@ -0,0 +1,706 @@ +/* + * 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. + */ + +/*! \file */ + +/* missing code for WIN32 */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +char * +getpass(const char *prompt) { + static char buf[128]; + HANDLE h; + DWORD cc, mode; + int cnt; + + h = GetStdHandle(STD_INPUT_HANDLE); + fputs(prompt, stderr); + fflush(stderr); + fflush(stdout); + FlushConsoleInputBuffer(h); + GetConsoleMode(h, &mode); + SetConsoleMode(h, ENABLE_PROCESSED_INPUT); + + for (cnt = 0; cnt < sizeof(buf) - 1; cnt++) { + ReadFile(h, buf + cnt, 1, &cc, NULL); + if (buf[cnt] == '\r') { + break; + } + fputc('*', stdout); + fflush(stderr); + fflush(stdout); + } + + SetConsoleMode(h, mode); + buf[cnt] = '\0'; + fputs("\n", stderr); + return (buf); +} + +/* load PKCS11 DLL */ + +static HINSTANCE hPK11 = NULL; +static char loaderrmsg[1024]; + +CK_RV +pkcs_C_Initialize(CK_VOID_PTR pReserved) { + CK_C_Initialize sym; + const char *lib_name = pk11_get_lib_name(); + + if (hPK11 != NULL) { + return (CKR_LIBRARY_ALREADY_INITIALIZED); + } + + if (lib_name == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + /* Visual Studio conversion issue... */ + if (*lib_name == ' ') { + lib_name++; + } + + hPK11 = LoadLibraryA(lib_name); + + if (hPK11 == NULL) { + const DWORD err = GetLastError(); + snprintf(loaderrmsg, sizeof(loaderrmsg), + "LoadLibraryA(\"%s\") failed with 0x%X\n", lib_name, + err); + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + sym = (CK_C_Initialize)GetProcAddress(hPK11, "C_Initialize"); + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(pReserved)); +} + +char * +pk11_get_load_error_message(void) { + return (loaderrmsg); +} + +CK_RV +pkcs_C_Finalize(CK_VOID_PTR pReserved) { + CK_C_Finalize sym; + CK_RV rv; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + sym = (CK_C_Finalize)GetProcAddress(hPK11, "C_Finalize"); + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + rv = (*sym)(pReserved); + if ((rv == CKR_OK) && (FreeLibrary(hPK11) == 0)) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + hPK11 = NULL; + return (rv); +} + +CK_RV +pkcs_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount) { + static CK_C_GetSlotList sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GetSlotList)GetProcAddress(hPK11, "C_GetSlotList"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(tokenPresent, pSlotList, pulCount)); +} + +CK_RV +pkcs_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { + static CK_C_GetTokenInfo sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GetTokenInfo)GetProcAddress(hPK11, "C_" + "GetTokenInfo"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(slotID, pInfo)); +} + +CK_RV +pkcs_C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR pInfo) { + static CK_C_GetMechanismInfo sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GetMechanismInfo)GetProcAddress(hPK11, "C_" + "GetMechanis" + "mInfo"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(slotID, type, pInfo)); +} + +CK_RV +pkcs_C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, + CK_RV (*Notify)(CK_SESSION_HANDLE hSession, + CK_NOTIFICATION event, + CK_VOID_PTR pApplication), + CK_SESSION_HANDLE_PTR phSession) { + static CK_C_OpenSession sym = NULL; + + if (hPK11 == NULL) { + hPK11 = LoadLibraryA(pk11_get_lib_name()); + } + if (hPK11 == NULL) { + const DWORD err = GetLastError(); + snprintf(loaderrmsg, sizeof(loaderrmsg), + "LoadLibraryA(\"%s\") failed with 0x%X\n", + pk11_get_lib_name(), err); + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_OpenSession)GetProcAddress(hPK11, "C_OpenSession"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(slotID, flags, pApplication, Notify, phSession)); +} + +CK_RV +pkcs_C_CloseSession(CK_SESSION_HANDLE hSession) { + static CK_C_CloseSession sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_CloseSession)GetProcAddress(hPK11, "C_" + "CloseSession"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession)); +} + +CK_RV +pkcs_C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, + CK_CHAR_PTR pPin, CK_ULONG usPinLen) { + static CK_C_Login sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_Login)GetProcAddress(hPK11, "C_Login"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, userType, pPin, usPinLen)); +} + +CK_RV +pkcs_C_Logout(CK_SESSION_HANDLE hSession) { + static CK_C_Logout sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_Logout)GetProcAddress(hPK11, "C_Logout"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession)); +} + +CK_RV +pkcs_C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG usCount, CK_OBJECT_HANDLE_PTR phObject) { + static CK_C_CreateObject sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_CreateObject)GetProcAddress(hPK11, "C_" + "CreateObject"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pTemplate, usCount, phObject)); +} + +CK_RV +pkcs_C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) { + static CK_C_DestroyObject sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_DestroyObject)GetProcAddress(hPK11, "C_" + "DestroyObjec" + "t"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, hObject)); +} + +CK_RV +pkcs_C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) { + static CK_C_GetAttributeValue sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GetAttributeValue)GetProcAddress(hPK11, "C_" + "GetAttribu" + "teValue"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, hObject, pTemplate, usCount)); +} + +CK_RV +pkcs_C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount) { + static CK_C_SetAttributeValue sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_SetAttributeValue)GetProcAddress(hPK11, "C_" + "SetAttribu" + "teValue"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, hObject, pTemplate, usCount)); +} + +CK_RV +pkcs_C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG usCount) { + static CK_C_FindObjectsInit sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_FindObjectsInit)GetProcAddress(hPK11, "C_" + "FindObjectsI" + "nit"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pTemplate, usCount)); +} + +CK_RV +pkcs_C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, + CK_ULONG usMaxObjectCount, CK_ULONG_PTR pusObjectCount) { + static CK_C_FindObjects sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_FindObjects)GetProcAddress(hPK11, "C_FindObjects"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, phObject, usMaxObjectCount, pusObjectCount)); +} + +CK_RV +pkcs_C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { + static CK_C_FindObjectsFinal sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_FindObjectsFinal)GetProcAddress(hPK11, "C_" + "FindObjects" + "Final"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession)); +} + +CK_RV +pkcs_C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) { + static CK_C_EncryptInit sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_EncryptInit)GetProcAddress(hPK11, "C_EncryptInit"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, hKey)); +} + +CK_RV +pkcs_C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, + CK_ULONG_PTR pulEncryptedDataLen) { + static CK_C_Encrypt sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_Encrypt)GetProcAddress(hPK11, "C_Encrypt"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pData, ulDataLen, pEncryptedData, + pulEncryptedDataLen)); +} + +CK_RV +pkcs_C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { + static CK_C_DigestInit sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_DigestInit)GetProcAddress(hPK11, "C_DigestInit"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism)); +} + +CK_RV +pkcs_C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) { + static CK_C_DigestUpdate sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_DigestUpdate)GetProcAddress(hPK11, "C_" + "DigestUpdate"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pPart, ulPartLen)); +} + +CK_RV +pkcs_C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) { + static CK_C_DigestFinal sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_DigestFinal)GetProcAddress(hPK11, "C_DigestFinal"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pDigest, pulDigestLen)); +} + +CK_RV +pkcs_C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) { + static CK_C_SignInit sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_SignInit)GetProcAddress(hPK11, "C_SignInit"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, hKey)); +} + +CK_RV +pkcs_C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { + static CK_C_Sign sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_Sign)GetProcAddress(hPK11, "C_Sign"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pData, ulDataLen, pSignature, + pulSignatureLen)); +} + +CK_RV +pkcs_C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) { + static CK_C_SignUpdate sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_SignUpdate)GetProcAddress(hPK11, "C_SignUpdate"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pPart, ulPartLen)); +} + +CK_RV +pkcs_C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) { + static CK_C_SignFinal sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_SignFinal)GetProcAddress(hPK11, "C_SignFinal"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pSignature, pulSignatureLen)); +} + +CK_RV +pkcs_C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey) { + static CK_C_VerifyInit sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_VerifyInit)GetProcAddress(hPK11, "C_VerifyInit"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, hKey)); +} + +CK_RV +pkcs_C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, + CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { + static CK_C_Verify sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_Verify)GetProcAddress(hPK11, "C_Verify"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pData, ulDataLen, pSignature, ulSignatureLen)); +} + +CK_RV +pkcs_C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) { + static CK_C_VerifyUpdate sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_VerifyUpdate)GetProcAddress(hPK11, "C_" + "VerifyUpdate"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pPart, ulPartLen)); +} + +CK_RV +pkcs_C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen) { + static CK_C_VerifyFinal sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_VerifyFinal)GetProcAddress(hPK11, "C_VerifyFinal"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pSignature, ulSignatureLen)); +} + +CK_RV +pkcs_C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey) { + static CK_C_GenerateKey sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GenerateKey)GetProcAddress(hPK11, "C_GenerateKey"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, pTemplate, ulCount, phKey)); +} + +CK_RV +pkcs_C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG usPublicKeyAttributeCount, + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG usPrivateKeyAttributeCount, + CK_OBJECT_HANDLE_PTR phPrivateKey, + CK_OBJECT_HANDLE_PTR phPublicKey) { + static CK_C_GenerateKeyPair sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GenerateKeyPair)GetProcAddress(hPK11, "C_" + "GenerateKeyP" + "air"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, pPublicKeyTemplate, + usPublicKeyAttributeCount, pPrivateKeyTemplate, + usPrivateKeyAttributeCount, phPrivateKey, phPublicKey)); +} + +CK_RV +pkcs_C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { + static CK_C_DeriveKey sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_DeriveKey)GetProcAddress(hPK11, "C_DeriveKey"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pMechanism, hBaseKey, pTemplate, + ulAttributeCount, phKey)); +} + +CK_RV +pkcs_C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, + CK_ULONG ulSeedLen) { + static CK_C_SeedRandom sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_SeedRandom)GetProcAddress(hPK11, "C_SeedRandom"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, pSeed, ulSeedLen)); +} + +CK_RV +pkcs_C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, + CK_ULONG ulRandomLen) { + static CK_C_GenerateRandom sym = NULL; + + if (hPK11 == NULL) { + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if (sym == NULL) { + sym = (CK_C_GenerateRandom)GetProcAddress(hPK11, "C_" + "GenerateRando" + "m"); + } + if (sym == NULL) { + return (CKR_SYMBOL_RESOLUTION_FAILED); + } + return ((*sym)(hSession, RandomData, ulRandomLen)); +} diff --git a/lib/isc/win32/resource.c b/lib/isc/win32/resource.c new file mode 100644 index 0000000..b44b322 --- /dev/null +++ b/lib/isc/win32/resource.c @@ -0,0 +1,66 @@ +/* + * 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 + +#include +#include +#include +#include + +#include "errno2result.h" + +/* + * Windows limits the maximum number of open files to 2048 + */ + +#define WIN32_MAX_OPEN_FILES 2048 + +isc_result_t +isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value) { + isc_resourcevalue_t rlim_value; + int wresult; + + if (resource != isc_resource_openfiles) { + return (ISC_R_NOTIMPLEMENTED); + } + + if (value == ISC_RESOURCE_UNLIMITED) { + rlim_value = WIN32_MAX_OPEN_FILES; + } else { + rlim_value = min(value, WIN32_MAX_OPEN_FILES); + } + + wresult = _setmaxstdio((int)rlim_value); + + if (wresult > 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value) { + if (resource != isc_resource_openfiles) { + return (ISC_R_NOTIMPLEMENTED); + } + + *value = WIN32_MAX_OPEN_FILES; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value) { + return (isc_resource_getlimit(resource, value)); +} diff --git a/lib/isc/win32/socket.c b/lib/isc/win32/socket.c new file mode 100644 index 0000000..c154175 --- /dev/null +++ b/lib/isc/win32/socket.c @@ -0,0 +1,3965 @@ +/* + * 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. + */ + +/* This code uses functions which are only available on Server 2003 and + * higher, and Windows XP and higher. + * + * This code is by nature multithreaded and takes advantage of various + * features to pass on information through the completion port for + * when I/O is completed. All sends, receives, accepts, and connects are + * completed through the completion port. + * + * The number of Completion Port Worker threads used is the total number + * of CPU's + 1. This increases the likelihood that a Worker Thread is + * available for processing a completed request. + * + * XXXPDM 5 August, 2002 + */ + +#define MAKE_EXTERNAL 1 + +#include + +#ifndef _WINSOCKAPI_ +#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ +#endif /* ifndef _WINSOCKAPI_ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* clang-format off */ +/* U Can't Touch This */ +#include +/* clang-format on */ + +#ifdef HAVE_JSON_C +#include +#endif /* HAVE_JSON_C */ + +#ifdef HAVE_LIBXML2 +#include +#define ISC_XMLCHAR (const xmlChar *) +#endif /* HAVE_LIBXML2 */ + +#include "errno2result.h" + +/* + * Set by the -T dscp option on the command line. If set to a value + * other than -1, we check to make sure DSCP values match it, and + * assert if not. + */ +LIBISC_EXTERNAL_DATA int isc_dscp_check_value = -1; + +/* + * How in the world can Microsoft exist with APIs like this? + * We can't actually call this directly, because it turns out + * no library exports this function. Instead, we need to + * issue a runtime call to get the address. + */ +LPFN_CONNECTEX ISCConnectEx; +LPFN_ACCEPTEX ISCAcceptEx; +LPFN_GETACCEPTEXSOCKADDRS ISCGetAcceptExSockaddrs; + +/* + * Run expensive internal consistency checks. + */ +#ifdef ISC_SOCKET_CONSISTENCY_CHECKS +#define CONSISTENT(sock) consistent(sock) +#else /* ifdef ISC_SOCKET_CONSISTENCY_CHECKS */ +#define CONSISTENT(sock) \ + do { \ + } while (0) +#endif /* ifdef ISC_SOCKET_CONSISTENCY_CHECKS */ +static void +consistent(isc_socket_t *sock); + +/* + * Define this macro to control the behavior of connection + * resets on UDP sockets. See Microsoft KnowledgeBase Article Q263823 + * for details. + * NOTE: This requires that Windows 2000 systems install Service Pack 2 + * or later. + */ +#ifndef SIO_UDP_CONNRESET +#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) +#endif /* ifndef SIO_UDP_CONNRESET */ + +/* + * Define what the possible "soft" errors can be. These are non-fatal returns + * of various network related functions, like recv() and so on. + */ +#define SOFT_ERROR(e) \ + ((e) == WSAEINTR || (e) == WSAEWOULDBLOCK || (e) == EWOULDBLOCK || \ + (e) == EINTR || (e) == EAGAIN || (e) == 0) + +/* + * Pending errors are not really errors and should be + * kept separate + */ +#define PENDING_ERROR(e) ((e) == WSA_IO_PENDING || (e) == 0) + +#define DOIO_SUCCESS 0 /* i/o ok, event sent */ +#define DOIO_SOFT 1 /* i/o ok, soft error, no event sent */ +#define DOIO_HARD 2 /* i/o error, event sent */ +#define DOIO_EOF 3 /* EOF, no event sent */ +#define DOIO_PENDING 4 /* status when i/o is in process */ +#define DOIO_NEEDMORE \ + 5 /* IO was processed, but we need more due to minimum \ + */ + +#define DLVL(x) ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(x) + +/* + * DLVL(90) -- Function entry/exit and other tracing. + * DLVL(70) -- Socket "correctness" -- including returning of events, etc. + * DLVL(60) -- Socket data send/receive + * DLVL(50) -- Event tracing, including receiving/sending completion events. + * DLVL(20) -- Socket creation/destruction. + */ +#define TRACE_LEVEL 90 +#define CORRECTNESS_LEVEL 70 +#define IOEVENT_LEVEL 60 +#define EVENT_LEVEL 50 +#define CREATION_LEVEL 20 + +#define TRACE DLVL(TRACE_LEVEL) +#define CORRECTNESS DLVL(CORRECTNESS_LEVEL) +#define IOEVENT DLVL(IOEVENT_LEVEL) +#define EVENT DLVL(EVENT_LEVEL) +#define CREATION DLVL(CREATION_LEVEL) + +typedef isc_event_t intev_t; + +/* + * Socket State + */ +enum { + SOCK_INITIALIZED, /* Socket Initialized */ + SOCK_OPEN, /* Socket opened but nothing yet to do */ + SOCK_DATA, /* Socket sending or receiving data */ + SOCK_LISTEN, /* TCP Socket listening for connects */ + SOCK_ACCEPT, /* TCP socket is waiting to accept */ + SOCK_CONNECT, /* TCP Socket connecting */ + SOCK_CLOSED, /* Socket has been closed */ +}; + +#define SOCKET_MAGIC ISC_MAGIC('I', 'O', 'i', 'o') +#define VALID_SOCKET(t) ISC_MAGIC_VALID(t, SOCKET_MAGIC) + +/* + * IPv6 control information. If the socket is an IPv6 socket we want + * to collect the destination address and interface so the client can + * set them on outgoing packets. + */ +#ifndef USE_CMSG +#define USE_CMSG 1 +#endif /* ifndef USE_CMSG */ + +/* + * We really don't want to try and use these control messages. Win32 + * doesn't have this mechanism before XP. + */ +#undef USE_CMSG + +/* + * Message header for recvmsg and sendmsg calls. + * Used value-result for recvmsg, value only for sendmsg. + */ +struct msghdr { + SOCKADDR_STORAGE to_addr; /* UDP send/recv address */ + int to_addr_len; /* length of the address */ + WSABUF *msg_iov; /* scatter/gather array */ + u_int msg_iovlen; /* # elements in msg_iov */ + void *msg_control; /* ancillary data, see below */ + u_int msg_controllen; /* ancillary data buffer len */ + u_int msg_totallen; /* total length of this message */ +} msghdr; + +/* + * The size to raise the receive buffer to. + */ +#define RCVBUFSIZE (32 * 1024) + +/* + * The number of times a send operation is repeated if the result + * is WSAEINTR. + */ +#define NRETRIES 10 + +struct isc_socket { + /* Not locked. */ + unsigned int magic; + isc_socketmgr_t *manager; + isc_mutex_t lock; + isc_sockettype_t type; + + /* Pointers to scatter/gather buffers */ + WSABUF iov[ISC_SOCKET_MAXSCATTERGATHER]; + + /* Locked by socket lock. */ + ISC_LINK(isc_socket_t) link; + isc_refcount_t references; /* EXTERNAL references */ + SOCKET fd; /* file handle */ + int pf; /* protocol family */ + char name[16]; + void *tag; + + /* + * Each recv() call uses this buffer. It is a per-socket receive + * buffer that allows us to decouple the system recv() from the + * recv_list done events. This means the items on the recv_list + * can be removed without having to cancel pending system recv() + * calls. It also allows us to read-ahead in some cases. + */ + struct { + SOCKADDR_STORAGE from_addr; /* UDP send/recv address */ + int from_addr_len; /* length of the address */ + char *base; /* the base of the buffer */ + char *consume_position; /* where to start + * copying data from + * next */ + unsigned int len; /* the actual size of this buffer */ + unsigned int remaining; /* the number of bytes + * remaining */ + } recvbuf; + + ISC_LIST(isc_socketevent_t) send_list; + ISC_LIST(isc_socketevent_t) recv_list; + ISC_LIST(isc_socket_newconnev_t) accept_list; + ISC_LIST(isc_socket_connev_t) connect_list; + + isc_sockaddr_t address; /* remote address */ + + unsigned int listener : 1, /* listener socket */ + connected : 1, pending_connect : 1, /* connect + * pending */ + bound : 1, /* bound to local addr */ + dupped : 1; /* created by isc_socket_dup() */ + unsigned int pending_iocp; /* Should equal the counters below. + * Debug. */ + unsigned int pending_recv; /* Number of outstanding recv() calls. + * */ + unsigned int pending_send; /* Number of outstanding send() calls. + * */ + unsigned int pending_accept; /* Number of outstanding accept() + * calls. */ + unsigned int state; /* Socket state. Debugging and consistency + * checking. + */ + int state_lineno; /* line which last touched state */ +}; + +#define _set_state(sock, _state) \ + do { \ + (sock)->state = (_state); \ + (sock)->state_lineno = __LINE__; \ + } while (0) + +/* + * I/O Completion ports Info structures + */ + +static HANDLE hHeapHandle = NULL; +typedef struct IoCompletionInfo { + OVERLAPPED overlapped; + isc_socketevent_t *dev; /* send()/recv() done event */ + isc_socket_connev_t *cdev; /* connect() done event */ + isc_socket_newconnev_t *adev; /* accept() done event */ + void *acceptbuffer; + DWORD received_bytes; + int request_type; + struct msghdr messagehdr; + void *buf; + unsigned int buflen; +} IoCompletionInfo; + +/* + * Define a maximum number of I/O Completion Port worker threads + * to handle the load on the Completion Port. The actual number + * used is the number of CPU's + 1. + */ +#define MAX_IOCPTHREADS 20 + +#define SOCKET_MANAGER_MAGIC ISC_MAGIC('I', 'O', 'm', 'g') +#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, SOCKET_MANAGER_MAGIC) + +struct isc_socketmgr { + /* Not locked. */ + unsigned int magic; + isc_mem_t *mctx; + isc_mutex_t lock; + isc_stats_t *stats; + + /* Locked by manager lock. */ + ISC_LIST(isc_socket_t) socklist; + bool bShutdown; + isc_condition_t shutdown_ok; + HANDLE hIoCompletionPort; + int maxIOCPThreads; + HANDLE hIOCPThreads[MAX_IOCPTHREADS]; + size_t maxudp; + + /* + * Debugging. + * Modified by InterlockedIncrement() and InterlockedDecrement() + */ + LONG totalSockets; + LONG iocp_total; +}; + +enum { SOCKET_RECV, SOCKET_SEND, SOCKET_ACCEPT, SOCKET_CONNECT }; + +/* + * send() and recv() iovec counts + */ +#define MAXSCATTERGATHER_SEND (ISC_SOCKET_MAXSCATTERGATHER) +#define MAXSCATTERGATHER_RECV (ISC_SOCKET_MAXSCATTERGATHER) + +static isc_result_t +socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, + isc_socket_t **socketp, isc_socket_t *dup_socket); +static isc_threadresult_t WINAPI +SocketIoThread(LPVOID ThreadContext); +static void +maybe_free_socket(isc_socket_t **, int); +static void +free_socket(isc_socket_t **, int); +static bool +senddone_is_active(isc_socket_t *sock, isc_socketevent_t *dev); +static bool +acceptdone_is_active(isc_socket_t *sock, isc_socket_newconnev_t *dev); +static bool +connectdone_is_active(isc_socket_t *sock, isc_socket_connev_t *dev); +static void +send_recvdone_event(isc_socket_t *sock, isc_socketevent_t **dev); +static void +send_senddone_event(isc_socket_t *sock, isc_socketevent_t **dev); +static void +send_acceptdone_event(isc_socket_t *sock, isc_socket_newconnev_t **adev); +static void +send_connectdone_event(isc_socket_t *sock, isc_socket_connev_t **cdev); +static void +send_recvdone_abort(isc_socket_t *sock, isc_result_t result); +static void +send_connectdone_abort(isc_socket_t *sock, isc_result_t result); +static void +queue_receive_event(isc_socket_t *sock, isc_task_t *task, + isc_socketevent_t *dev); +static void +queue_receive_request(isc_socket_t *sock); + +/* + * This is used to dump the contents of the sock structure + * You should make sure that the sock is locked before + * dumping it. Since the code uses simple printf() statements + * it should only be used interactively. + */ +void +sock_dump(isc_socket_t *sock) { + isc_socketevent_t *ldev; + isc_socket_newconnev_t *ndev; + isc_socket_connev_t *cdev; + +#if 0 + isc_sockaddr_t addr; + char socktext[ISC_SOCKADDR_FORMATSIZE]; + isc_result_t result; + + result = isc_socket_getpeername(sock,&addr); + if (result == ISC_R_SUCCESS) { + isc_sockaddr_format(&addr,socktext,sizeof(socktext)); + printf("Remote Socket: %s\n",socktext); + } + result = isc_socket_getsockname(sock,&addr); + if (result == ISC_R_SUCCESS) { + isc_sockaddr_format(&addr,socktext,sizeof(socktext)); + printf("This Socket: %s\n",socktext); + } +#endif /* if 0 */ + + printf("\n\t\tSock Dump\n"); + printf("\t\tfd: %Iu\n", sock->fd); + printf("\t\treferences: %" PRIuFAST32 "\n", + isc_refcount_current(&sock->references)); + printf("\t\tpending_accept: %u\n", sock->pending_accept); + printf("\t\tconnecting: %u\n", sock->pending_connect); + printf("\t\tconnected: %u\n", sock->connected); + printf("\t\tbound: %u\n", sock->bound); + printf("\t\tpending_iocp: %u\n", sock->pending_iocp); + printf("\t\tsocket type: %d\n", sock->type); + + printf("\n\t\tSock Recv List\n"); + ldev = ISC_LIST_HEAD(sock->recv_list); + while (ldev != NULL) { + printf("\t\tdev: %p\n", ldev); + ldev = ISC_LIST_NEXT(ldev, ev_link); + } + + printf("\n\t\tSock Send List\n"); + ldev = ISC_LIST_HEAD(sock->send_list); + while (ldev != NULL) { + printf("\t\tdev: %p\n", ldev); + ldev = ISC_LIST_NEXT(ldev, ev_link); + } + + printf("\n\t\tSock Accept List\n"); + ndev = ISC_LIST_HEAD(sock->accept_list); + while (ndev != NULL) { + printf("\t\tdev: %p\n", ldev); + ndev = ISC_LIST_NEXT(ndev, ev_link); + } + + printf("\n\t\tSock Connect List\n"); + cdev = ISC_LIST_HEAD(sock->connect_list); + while (cdev != NULL) { + printf("\t\tdev: %p\n", cdev); + cdev = ISC_LIST_NEXT(cdev, ev_link); + } +} + +static void +socket_log(int lineno, isc_socket_t *sock, const isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + const char *fmt, ...) ISC_FORMAT_PRINTF(10, 11); + +/* This function will add an entry to the I/O completion port + * that will signal the I/O thread to exit (gracefully) + */ +static void +signal_iocompletionport_exit(isc_socketmgr_t *manager) { + int i; + int errval; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_MANAGER(manager)); + for (i = 0; i < manager->maxIOCPThreads; i++) { + if (!PostQueuedCompletionStatus(manager->hIoCompletionPort, 0, + 0, 0)) + { + errval = GetLastError(); + strerror_r(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + "Can't request service thread to exit: %s", + strbuf); + } + } +} + +/* + * Create the worker threads for the I/O Completion Port + */ +void +iocompletionport_createthreads(int total_threads, isc_socketmgr_t *manager) { + int errval; + char strbuf[ISC_STRERRORSIZE]; + int i; + + INSIST(total_threads > 0); + REQUIRE(VALID_MANAGER(manager)); + /* + * We need at least one + */ + for (i = 0; i < total_threads; i++) { + isc_thread_create(SocketIoThread, manager, + &manager->hIOCPThreads[i]); + } +} + +/* + * Create/initialise the I/O completion port + */ +void +iocompletionport_init(isc_socketmgr_t *manager) { + int errval; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_MANAGER(manager)); + /* + * Create a private heap to handle the socket overlapped structure + * The minimum number of structures is 10, there is no maximum + */ + hHeapHandle = HeapCreate(0, 10 * sizeof(IoCompletionInfo), 0); + if (hHeapHandle == NULL) { + errval = GetLastError(); + strerror_r(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + "HeapCreate() failed during initialization: %s", + strbuf); + } + + /* Now Create the Completion Port */ + manager->hIoCompletionPort = CreateIoCompletionPort( + INVALID_HANDLE_VALUE, NULL, 0, manager->maxIOCPThreads); + if (manager->hIoCompletionPort == NULL) { + errval = GetLastError(); + strerror_r(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + "CreateIoCompletionPort() failed during " + "initialization: %s", + strbuf); + } + + /* + * Worker threads for servicing the I/O + */ + iocompletionport_createthreads(manager->maxIOCPThreads, manager); +} + +/* + * Associate a socket with an IO Completion Port. This allows us to queue + * events for it and have our worker pool of threads process them. + */ +void +iocompletionport_update(isc_socket_t *sock) { + HANDLE hiocp; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_SOCKET(sock)); + + hiocp = CreateIoCompletionPort((HANDLE)sock->fd, + sock->manager->hIoCompletionPort, + (ULONG_PTR)sock, 0); + + if (hiocp == NULL) { + DWORD errval = GetLastError(); + strerror_r(errval, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "iocompletionport_update: failed to open io " + "completion port: %s", + strbuf); + + /* XXXMLG temporary hack to make failures detected. + * This function should return errors to the caller, not + * exit here. + */ + FATAL_ERROR(__FILE__, __LINE__, + "CreateIoCompletionPort() failed during " + "initialization: %s", + strbuf); + } + + InterlockedIncrement(&sock->manager->iocp_total); +} + +/* + * Routine to cleanup and then close the socket. + * Only close the socket here if it is NOT associated + * with an event, otherwise the WSAWaitForMultipleEvents + * may fail due to the fact that the Wait should not + * be running while closing an event or a socket. + * The socket is locked before calling this function + */ +void +socket_close(isc_socket_t *sock) { + REQUIRE(sock != NULL); + + if (sock->fd != INVALID_SOCKET) { + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + _set_state(sock, SOCK_CLOSED); + InterlockedDecrement(&sock->manager->totalSockets); + } +} + +static isc_once_t initialise_once = ISC_ONCE_INIT; +static bool initialised = false; + +static void +initialise(void) { + WORD wVersionRequested; + WSADATA wsaData; + int err; + SOCKET sock; + GUID GUIDConnectEx = WSAID_CONNECTEX; + GUID GUIDAcceptEx = WSAID_ACCEPTEX; + GUID GUIDGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; + DWORD dwBytes; + + /* Need Winsock 2.2 or better */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + char strbuf[ISC_STRERRORSIZE]; + strerror_r(err, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, "WSAStartup() failed: %s", + strbuf); + } + /* + * The following APIs do not exist as functions in a library, but + * we must ask winsock for them. They are "extensions" -- but why + * they cannot be actual functions is beyond me. So, ask winsock + * for the pointers to the functions we need. + */ + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + INSIST(sock != INVALID_SOCKET); + err = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GUIDConnectEx, + sizeof(GUIDConnectEx), &ISCConnectEx, + sizeof(ISCConnectEx), &dwBytes, NULL, NULL); + INSIST(err == 0); + + err = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &GUIDAcceptEx, + sizeof(GUIDAcceptEx), &ISCAcceptEx, sizeof(ISCAcceptEx), + &dwBytes, NULL, NULL); + INSIST(err == 0); + + err = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, + &GUIDGetAcceptExSockaddrs, + sizeof(GUIDGetAcceptExSockaddrs), + &ISCGetAcceptExSockaddrs, + sizeof(ISCGetAcceptExSockaddrs), &dwBytes, NULL, NULL); + INSIST(err == 0); + + closesocket(sock); + + initialised = true; +} + +/* + * Initialize socket services + */ +void +InitSockets(void) { + RUNTIME_CHECK(isc_once_do(&initialise_once, initialise) == + ISC_R_SUCCESS); + if (!initialised) { + exit(1); + } +} + +int +internal_sendmsg(isc_socket_t *sock, IoCompletionInfo *lpo, + struct msghdr *messagehdr, int flags, int *Error) { + int Result; + DWORD BytesSent; + DWORD Flags = flags; + int total_sent; + + *Error = 0; + Result = WSASendTo(sock->fd, messagehdr->msg_iov, + messagehdr->msg_iovlen, &BytesSent, Flags, + (SOCKADDR *)&messagehdr->to_addr, + messagehdr->to_addr_len, (LPWSAOVERLAPPED)lpo, NULL); + + total_sent = (int)BytesSent; + + /* Check for errors.*/ + if (Result == SOCKET_ERROR) { + *Error = WSAGetLastError(); + + switch (*Error) { + case WSA_IO_INCOMPLETE: + case WSA_WAIT_IO_COMPLETION: + case WSA_IO_PENDING: + case NO_ERROR: /* Strange, but okay */ + sock->pending_iocp++; + sock->pending_send++; + break; + + default: + return (-1); + break; + } + } else { + sock->pending_iocp++; + sock->pending_send++; + } + + if (lpo != NULL) { + return (0); + } else { + return (total_sent); + } +} + +static void +queue_receive_request(isc_socket_t *sock) { + DWORD Flags = 0; + DWORD NumBytes = 0; + int Result; + int Error; + int need_retry; + WSABUF iov[1]; + IoCompletionInfo *lpo = NULL; + isc_result_t isc_result; + +retry: + need_retry = false; + + /* + * If we already have a receive pending, do nothing. + */ + if (sock->pending_recv > 0) { + if (lpo != NULL) { + HeapFree(hHeapHandle, 0, lpo); + } + return; + } + + /* + * If no one is waiting, do nothing. + */ + if (ISC_LIST_EMPTY(sock->recv_list)) { + if (lpo != NULL) { + HeapFree(hHeapHandle, 0, lpo); + } + return; + } + + INSIST(sock->recvbuf.remaining == 0); + INSIST(sock->fd != INVALID_SOCKET); + + iov[0].len = sock->recvbuf.len; + iov[0].buf = sock->recvbuf.base; + + if (lpo == NULL) { + lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle, + HEAP_ZERO_MEMORY, + sizeof(IoCompletionInfo)); + RUNTIME_CHECK(lpo != NULL); + } else { + ZeroMemory(lpo, sizeof(IoCompletionInfo)); + } + lpo->request_type = SOCKET_RECV; + + sock->recvbuf.from_addr_len = sizeof(sock->recvbuf.from_addr); + + Error = 0; + Result = WSARecvFrom((SOCKET)sock->fd, iov, 1, &NumBytes, &Flags, + (SOCKADDR *)&sock->recvbuf.from_addr, + &sock->recvbuf.from_addr_len, (LPWSAOVERLAPPED)lpo, + NULL); + + /* Check for errors. */ + if (Result == SOCKET_ERROR) { + Error = WSAGetLastError(); + + switch (Error) { + case WSA_IO_PENDING: + sock->pending_iocp++; + sock->pending_recv++; + break; + + /* direct error: no completion event */ + case ERROR_HOST_UNREACHABLE: + case WSAENETRESET: + case WSAECONNRESET: + if (!sock->connected) { + /* soft error */ + need_retry = true; + break; + } + FALLTHROUGH; + + default: + isc_result = isc__errno2result(Error); + if (isc_result == ISC_R_UNEXPECTED) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "WSARecvFrom: Windows error " + "code: %d, isc result %d", + Error, isc_result); + } + send_recvdone_abort(sock, isc_result); + HeapFree(hHeapHandle, 0, lpo); + lpo = NULL; + break; + } + } else { + /* + * The recv() finished immediately, but we will still get + * a completion event. Rather than duplicate code, let + * that thread handle sending the data along its way. + */ + sock->pending_iocp++; + sock->pending_recv++; + } + + socket_log(__LINE__, sock, NULL, IOEVENT, + "queue_io_request: fd %d result %d error %d", sock->fd, + Result, Error); + + CONSISTENT(sock); + + if (need_retry) { + goto retry; + } +} + +static void +manager_log(isc_socketmgr_t *sockmgr, isc_logcategory_t *category, + isc_logmodule_t *module, int level, const char *fmt, ...) { + char msgbuf[2048]; + va_list ap; + + if (!isc_log_wouldlog(isc_lctx, level)) { + return; + } + + va_start(ap, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + va_end(ap); + + isc_log_write(isc_lctx, category, module, level, "sockmgr %p: %s", + sockmgr, msgbuf); +} + +static void +socket_log(int lineno, isc_socket_t *sock, const isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + const char *fmt, ...) { + char msgbuf[2048]; + char peerbuf[256]; + va_list ap; + + if (!isc_log_wouldlog(isc_lctx, level)) { + return; + } + + va_start(ap, fmt); + vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + va_end(ap); + + if (address == NULL) { + isc_log_write(isc_lctx, category, module, level, + "socket %p line %d: %s", sock, lineno, msgbuf); + } else { + isc_sockaddr_format(address, peerbuf, sizeof(peerbuf)); + isc_log_write(isc_lctx, category, module, level, + "socket %p line %d %s: %s", sock, lineno, peerbuf, + msgbuf); + } +} + +/* + * Make an fd SOCKET non-blocking. + */ +static isc_result_t +make_nonblock(SOCKET fd) { + int ret; + unsigned long flags = 1; + char strbuf[ISC_STRERRORSIZE]; + + /* Set the socket to non-blocking */ + ret = ioctlsocket(fd, FIONBIO, &flags); + + if (ret == -1) { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "ioctlsocket(%d, FIOBIO, %d): %s", fd, flags, + strbuf); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +/* + * Windows 2000 systems incorrectly cause UDP sockets using WSARecvFrom + * to not work correctly, returning a WSACONNRESET error when a WSASendTo + * fails with an "ICMP port unreachable" response and preventing the + * socket from using the WSARecvFrom in subsequent operations. + * The function below fixes this, but requires that Windows 2000 + * Service Pack 2 or later be installed on the system. NT 4.0 + * systems are not affected by this and work correctly. + * See Microsoft Knowledge Base Article Q263823 for details of this. + */ +isc_result_t +connection_reset_fix(SOCKET fd) { + DWORD dwBytesReturned = 0; + BOOL bNewBehavior = FALSE; + DWORD status; + + if (isc_win32os_versioncheck(5, 0, 0, 0) < 0) { + return (ISC_R_SUCCESS); /* NT 4.0 has no problem */ + } + /* disable bad behavior using IOCTL: SIO_UDP_CONNRESET */ + status = WSAIoctl(fd, SIO_UDP_CONNRESET, &bNewBehavior, + sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, + NULL); + if (status != SOCKET_ERROR) { + return (ISC_R_SUCCESS); + } else { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "WSAIoctl(SIO_UDP_CONNRESET, oldBehaviour) " + "failed"); + return (ISC_R_UNEXPECTED); + } +} + +/* + * Construct an iov array and attach it to the msghdr passed in. This is + * the SEND constructor, which will use the used region of the buffer + * (if using a buffer list) or will use the internal region (if a single + * buffer I/O is requested). + * + * Nothing can be NULL, and the done event must list at least one buffer + * on the buffer linked list for this function to be meaningful. + */ +static void +build_msghdr_send(isc_socket_t *sock, isc_socketevent_t *dev, + struct msghdr *msg, char *cmsg, WSABUF *iov, + IoCompletionInfo *lpo) { + unsigned int iovcount; + size_t write_count; + + memset(msg, 0, sizeof(*msg)); + + memmove(&msg->to_addr, &dev->address.type, dev->address.length); + msg->to_addr_len = dev->address.length; + + write_count = 0; + iovcount = 0; + + /* + * Single buffer I/O? Skip what we've done so far in this region. + */ + write_count = dev->region.length - dev->n; + lpo->buf = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, write_count); + RUNTIME_CHECK(lpo->buf != NULL); + + socket_log(__LINE__, sock, NULL, TRACE, "alloc_buffer %p %d", lpo->buf, + write_count); + + memmove(lpo->buf, (dev->region.base + dev->n), write_count); + lpo->buflen = (unsigned int)write_count; + iov[0].buf = lpo->buf; + iov[0].len = (u_long)write_count; + iovcount = 1; + + msg->msg_iov = iov; + msg->msg_iovlen = iovcount; + msg->msg_totallen = (u_int)write_count; +} + +static void +set_dev_address(const isc_sockaddr_t *address, isc_socket_t *sock, + isc_socketevent_t *dev) { + if (sock->type == isc_sockettype_udp) { + if (address != NULL) { + dev->address = *address; + } else { + dev->address = sock->address; + } + } else if (sock->type == isc_sockettype_tcp) { + INSIST(address == NULL); + dev->address = sock->address; + } +} + +static void +destroy_socketevent(isc_event_t *event) { + isc_socketevent_t *ev = (isc_socketevent_t *)event; + + (ev->destroy)(event); +} + +static isc_socketevent_t * +allocate_socketevent(isc_mem_t *mctx, isc_socket_t *sock, + isc_eventtype_t eventtype, isc_taskaction_t action, + void *arg) { + isc_socketevent_t *ev; + + ev = (isc_socketevent_t *)isc_event_allocate(mctx, sock, eventtype, + action, arg, sizeof(*ev)); + + ev->result = ISC_R_IOERROR; /* XXXMLG temporary change to detect failure + */ + /* to set */ + ISC_LINK_INIT(ev, ev_link); + ev->region.base = NULL; + ev->n = 0; + ev->offset = 0; + ev->attributes = 0; + ev->destroy = ev->ev_destroy; + ev->ev_destroy = destroy_socketevent; + ev->dscp = 0; + + return (ev); +} + +#if defined(ISC_SOCKET_DEBUG) +static void +dump_msg(struct msghdr *msg, isc_socket_t *sock) { + unsigned int i; + + printf("MSGHDR %p, Socket #: %Iu\n", msg, sock->fd); + printf("\tname %p, namelen %d\n", msg->msg_name, msg->msg_namelen); + printf("\tiov %p, iovlen %d\n", msg->msg_iov, msg->msg_iovlen); + for (i = 0; i < (unsigned int)msg->msg_iovlen; i++) { + printf("\t\t%u\tbase %p, len %u\n", i, msg->msg_iov[i].buf, + msg->msg_iov[i].len); + } +} +#endif /* if defined(ISC_SOCKET_DEBUG) */ + +/* + * map the error code + */ +int +map_socket_error(isc_socket_t *sock, int windows_errno, int *isc_errno, + char *errorstring, size_t bufsize) { + int doreturn; + switch (windows_errno) { + case WSAECONNREFUSED: + *isc_errno = ISC_R_CONNREFUSED; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAENETUNREACH: + case ERROR_NETWORK_UNREACHABLE: + *isc_errno = ISC_R_NETUNREACH; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case ERROR_PORT_UNREACHABLE: + case ERROR_HOST_UNREACHABLE: + case WSAEHOSTUNREACH: + *isc_errno = ISC_R_HOSTUNREACH; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAENETDOWN: + *isc_errno = ISC_R_NETDOWN; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAEHOSTDOWN: + *isc_errno = ISC_R_HOSTDOWN; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAEACCES: + *isc_errno = ISC_R_NOPERM; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAECONNRESET: + case WSAENETRESET: + case WSAECONNABORTED: + case WSAEDISCON: + *isc_errno = ISC_R_CONNECTIONRESET; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case WSAENOTCONN: + *isc_errno = ISC_R_NOTCONNECTED; + if (sock->connected) { + doreturn = DOIO_HARD; + } else { + doreturn = DOIO_SOFT; + } + break; + case ERROR_OPERATION_ABORTED: + case ERROR_CONNECTION_ABORTED: + case ERROR_REQUEST_ABORTED: + *isc_errno = ISC_R_CONNECTIONRESET; + doreturn = DOIO_HARD; + break; + case WSAENOBUFS: + *isc_errno = ISC_R_NORESOURCES; + doreturn = DOIO_HARD; + break; + case WSAEAFNOSUPPORT: + *isc_errno = ISC_R_FAMILYNOSUPPORT; + doreturn = DOIO_HARD; + break; + case WSAEADDRNOTAVAIL: + *isc_errno = ISC_R_ADDRNOTAVAIL; + doreturn = DOIO_HARD; + break; + case WSAEDESTADDRREQ: + *isc_errno = ISC_R_BADADDRESSFORM; + doreturn = DOIO_HARD; + break; + case ERROR_NETNAME_DELETED: + *isc_errno = ISC_R_NETDOWN; + doreturn = DOIO_HARD; + break; + default: + *isc_errno = ISC_R_IOERROR; + doreturn = DOIO_HARD; + break; + } + if (doreturn == DOIO_HARD) { + strerror_r(windows_errno, errorstring, bufsize); + } + return (doreturn); +} + +static void +fill_recv(isc_socket_t *sock, isc_socketevent_t *dev) { + int copylen; + + INSIST(dev->n < dev->minimum); + INSIST(sock->recvbuf.remaining > 0); + INSIST(sock->pending_recv == 0); + + if (sock->type == isc_sockettype_udp) { + dev->address.length = sock->recvbuf.from_addr_len; + memmove(&dev->address.type, &sock->recvbuf.from_addr, + sock->recvbuf.from_addr_len); + if (isc_sockaddr_getport(&dev->address) == 0) { + if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) { + socket_log(__LINE__, sock, &dev->address, + IOEVENT, + "dropping source port zero packet"); + } + sock->recvbuf.remaining = 0; + return; + } + /* + * Simulate a firewall blocking UDP responses bigger than + * 'maxudp' bytes. + */ + if (sock->manager->maxudp != 0 && + sock->recvbuf.remaining > sock->manager->maxudp) + { + sock->recvbuf.remaining = 0; + return; + } + } else if (sock->type == isc_sockettype_tcp) { + dev->address = sock->address; + } + + copylen = min(dev->region.length - dev->n, sock->recvbuf.remaining); + memmove(dev->region.base + dev->n, sock->recvbuf.consume_position, + copylen); + sock->recvbuf.consume_position += copylen; + sock->recvbuf.remaining -= copylen; + dev->n += copylen; + + /* + * UDP receives are all-consuming. That is, if we have 4k worth of + * data in our receive buffer, and the caller only gave us + * 1k of space, we will toss the remaining 3k of data. TCP + * will keep the extra data around and use it for later requests. + */ + if (sock->type == isc_sockettype_udp) { + sock->recvbuf.remaining = 0; + } +} + +/* + * Copy out as much data from the internal buffer to done events. + * As each done event is filled, send it along its way. + */ +static void +completeio_recv(isc_socket_t *sock) { + isc_socketevent_t *dev; + + /* + * If we are in the process of filling our buffer, we cannot + * touch it yet, so don't. + */ + if (sock->pending_recv > 0) { + return; + } + + while (sock->recvbuf.remaining > 0 && !ISC_LIST_EMPTY(sock->recv_list)) + { + dev = ISC_LIST_HEAD(sock->recv_list); + + /* + * See if we have sufficient data in our receive buffer + * to handle this. If we do, copy out the data. + */ + fill_recv(sock, dev); + + /* + * Did we satisfy it? + */ + if (dev->n >= dev->minimum) { + dev->result = ISC_R_SUCCESS; + send_recvdone_event(sock, &dev); + } + } +} + +/* + * Returns: + * DOIO_SUCCESS The operation succeeded. dev->result contains + * ISC_R_SUCCESS. + * + * DOIO_HARD A hard or unexpected I/O error was encountered. + * dev->result contains the appropriate error. + * + * DOIO_SOFT A soft I/O error was encountered. No senddone + * event was sent. The operation should be retried. + * + * No other return values are possible. + */ +static int +completeio_send(isc_socket_t *sock, isc_socketevent_t *dev, + struct msghdr *messagehdr, int cc, int send_errno) { + char strbuf[ISC_STRERRORSIZE]; + + if (send_errno != 0) { + if (SOFT_ERROR(send_errno)) { + return (DOIO_SOFT); + } + + return (map_socket_error(sock, send_errno, &dev->result, strbuf, + sizeof(strbuf))); + } + + /* + * If we write less than we expected, update counters, poke. + */ + dev->n += cc; + if (cc != messagehdr->msg_totallen) { + return (DOIO_SOFT); + } + + /* + * Exactly what we wanted to write. We're done with this + * entry. Post its completion event. + */ + dev->result = ISC_R_SUCCESS; + return (DOIO_SUCCESS); +} + +static int +startio_send(isc_socket_t *sock, isc_socketevent_t *dev, int *nbytes, + int *send_errno) { + char *cmsg = NULL; + char strbuf[ISC_STRERRORSIZE]; + IoCompletionInfo *lpo; + int status; + struct msghdr *mh; + + /* + * Simulate a firewall blocking UDP responses bigger than + * 'maxudp' bytes. + */ + if (sock->type == isc_sockettype_udp && sock->manager->maxudp != 0 && + dev->region.length - dev->n > sock->manager->maxudp) + { + *nbytes = dev->region.length - dev->n; + return (DOIO_SUCCESS); + } + + lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, + sizeof(IoCompletionInfo)); + RUNTIME_CHECK(lpo != NULL); + lpo->request_type = SOCKET_SEND; + lpo->dev = dev; + mh = &lpo->messagehdr; + memset(mh, 0, sizeof(struct msghdr)); + + build_msghdr_send(sock, dev, mh, cmsg, sock->iov, lpo); + + *nbytes = internal_sendmsg(sock, lpo, mh, 0, send_errno); + + if (*nbytes <= 0) { + /* + * I/O has been initiated + * completion will be through the completion port + */ + if (PENDING_ERROR(*send_errno)) { + status = DOIO_PENDING; + goto done; + } + + if (SOFT_ERROR(*send_errno)) { + status = DOIO_SOFT; + goto done; + } + + /* + * If we got this far then something is wrong + */ + if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) { + strerror_r(*send_errno, strbuf, sizeof(strbuf)); + socket_log(__LINE__, sock, NULL, IOEVENT, + "startio_send: internal_sendmsg(%d) %d " + "bytes, err %d/%s", + sock->fd, *nbytes, *send_errno, strbuf); + } + status = DOIO_HARD; + goto done; + } + dev->result = ISC_R_SUCCESS; + status = DOIO_SOFT; +done: + _set_state(sock, SOCK_DATA); + return (status); +} + +static void +use_min_mtu(isc_socket_t *sock) { +#ifdef IPV6_USE_MIN_MTU + /* use minimum MTU */ + if (sock->pf == AF_INET6) { + int on = 1; + (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU, + (void *)&on, sizeof(on)); + } +#else /* ifdef IPV6_USE_MIN_MTU */ + UNUSED(sock); +#endif /* ifdef IPV6_USE_MIN_MTU */ +} + +static isc_result_t +allocate_socket(isc_socketmgr_t *manager, isc_sockettype_t type, + isc_socket_t **socketp) { + isc_socket_t *sock; + + sock = isc_mem_get(manager->mctx, sizeof(*sock)); + + sock->magic = 0; + isc_refcount_init(&sock->references, 0); + + sock->manager = manager; + sock->type = type; + sock->fd = INVALID_SOCKET; + + ISC_LINK_INIT(sock, link); + + /* + * Set up list of readers and writers to be initially empty. + */ + ISC_LIST_INIT(sock->recv_list); + ISC_LIST_INIT(sock->send_list); + ISC_LIST_INIT(sock->accept_list); + ISC_LIST_INIT(sock->connect_list); + sock->pending_accept = 0; + sock->pending_recv = 0; + sock->pending_send = 0; + sock->pending_iocp = 0; + sock->listener = 0; + sock->connected = 0; + sock->pending_connect = 0; + sock->bound = 0; + sock->dupped = 0; + memset(sock->name, 0, sizeof(sock->name)); /* zero the name field */ + _set_state(sock, SOCK_INITIALIZED); + + sock->recvbuf.len = 65536; + sock->recvbuf.consume_position = sock->recvbuf.base; + sock->recvbuf.remaining = 0; + sock->recvbuf.base = isc_mem_get(manager->mctx, + sock->recvbuf.len); /* max buffer */ + /* size */ + + /* + * Initialize the lock. + */ + isc_mutex_init(&sock->lock); + + socket_log(__LINE__, sock, NULL, EVENT, "allocated"); + + sock->magic = SOCKET_MAGIC; + *socketp = sock; + + return (ISC_R_SUCCESS); +} + +/* + * Verify that the socket state is consistent. + */ +static void +consistent(isc_socket_t *sock) { + isc_socketevent_t *dev; + isc_socket_newconnev_t *nev; + unsigned int count; + char *crash_reason; + bool crash = false; + + REQUIRE(sock->pending_iocp == sock->pending_recv + sock->pending_send + + sock->pending_accept + + sock->pending_connect); + + dev = ISC_LIST_HEAD(sock->send_list); + count = 0; + while (dev != NULL) { + count++; + dev = ISC_LIST_NEXT(dev, ev_link); + } + if (count > sock->pending_send) { + crash = true; + crash_reason = "send_list > sock->pending_send"; + } + + nev = ISC_LIST_HEAD(sock->accept_list); + count = 0; + while (nev != NULL) { + count++; + nev = ISC_LIST_NEXT(nev, ev_link); + } + if (count > sock->pending_accept) { + crash = true; + crash_reason = "accept_list > sock->pending_accept"; + } + + if (crash) { + socket_log(__LINE__, sock, NULL, CREATION, + "SOCKET INCONSISTENT: %s", crash_reason); + sock_dump(sock); + INSIST(!crash); + } +} + +/* + * Maybe free the socket. + * + * This function will verify that the socket is no longer in use in any way, + * either internally or externally. This is the only place where this + * check is to be made; if some bit of code believes that IT is done with + * the socket (e.g., some reference counter reaches zero), it should call + * this function. + * + * When calling this function, the socket must be locked, and the manager + * must be unlocked. + * + * When this function returns, *socketp will be NULL. No tricks to try + * to hold on to this pointer are allowed. + */ +static void +maybe_free_socket(isc_socket_t **socketp, int lineno) { + isc_socket_t *sock = *socketp; + *socketp = NULL; + + INSIST(VALID_SOCKET(sock)); + CONSISTENT(sock); + + if (sock->pending_iocp > 0 || sock->pending_recv > 0 || + sock->pending_send > 0 || sock->pending_accept > 0 || + isc_refcount_current(&sock->references) > 0 || + sock->pending_connect == 1 || !ISC_LIST_EMPTY(sock->recv_list) || + !ISC_LIST_EMPTY(sock->send_list) || + !ISC_LIST_EMPTY(sock->accept_list) || + !ISC_LIST_EMPTY(sock->connect_list) || sock->fd != INVALID_SOCKET) + { + UNLOCK(&sock->lock); + return; + } + UNLOCK(&sock->lock); + + free_socket(&sock, lineno); +} + +void +free_socket(isc_socket_t **sockp, int lineno) { + isc_socketmgr_t *manager; + isc_socket_t *sock = *sockp; + *sockp = NULL; + + /* + * Seems we can free the socket after all. + */ + manager = sock->manager; + socket_log(__LINE__, sock, NULL, CREATION, + "freeing socket line %d fd %d lock %p semaphore %p", lineno, + sock->fd, &sock->lock, sock->lock.LockSemaphore); + + sock->magic = 0; + isc_mutex_destroy(&sock->lock); + + if (sock->recvbuf.base != NULL) { + isc_mem_put(manager->mctx, sock->recvbuf.base, + sock->recvbuf.len); + } + + LOCK(&manager->lock); + if (ISC_LINK_LINKED(sock, link)) { + ISC_LIST_UNLINK(manager->socklist, sock, link); + } + isc_mem_put(manager->mctx, sock, sizeof(*sock)); + + if (ISC_LIST_EMPTY(manager->socklist)) { + SIGNAL(&manager->shutdown_ok); + } + UNLOCK(&manager->lock); +} + +/* + * Create a new 'type' socket managed by 'manager'. Events + * will be posted to 'task' and when dispatched 'action' will be + * called with 'arg' as the arg value. The new socket is returned + * in 'socketp'. + */ +static isc_result_t +socket_create(isc_socketmgr_t *manager, int pf, isc_sockettype_t type, + isc_socket_t **socketp, isc_socket_t *dup_socket) { + isc_socket_t *sock = NULL; + isc_result_t result; +#if defined(USE_CMSG) + int on = 1; +#endif /* if defined(USE_CMSG) */ +#if defined(SO_RCVBUF) + socklen_t optlen; + int size; +#endif /* if defined(SO_RCVBUF) */ + int socket_errno; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(socketp != NULL && *socketp == NULL); + +#ifndef SOCK_RAW + if (type == isc_sockettype_raw) { + return (ISC_R_NOTIMPLEMENTED); + } +#endif /* ifndef SOCK_RAW */ + + result = allocate_socket(manager, type, &sock); + if (result != ISC_R_SUCCESS) { + return (result); + } + + sock->pf = pf; + switch (type) { + case isc_sockettype_udp: + sock->fd = socket(pf, SOCK_DGRAM, IPPROTO_UDP); + if (sock->fd != INVALID_SOCKET) { + result = connection_reset_fix(sock->fd); + if (result != ISC_R_SUCCESS) { + socket_log(__LINE__, sock, NULL, EVENT, + "closed %d %d %" PRIuFAST32 " " + "con_reset_fix_failed", + sock->pending_recv, + sock->pending_send, + isc_refcount_current( + &sock->references)); + closesocket(sock->fd); + _set_state(sock, SOCK_CLOSED); + sock->fd = INVALID_SOCKET; + free_socket(&sock, __LINE__); + return (result); + } + } + break; + case isc_sockettype_tcp: + sock->fd = socket(pf, SOCK_STREAM, IPPROTO_TCP); + break; +#ifdef SOCK_RAW + case isc_sockettype_raw: + sock->fd = socket(pf, SOCK_RAW, 0); +#ifdef PF_ROUTE + if (pf == PF_ROUTE) { + sock->bound = 1; + } +#endif /* ifdef PF_ROUTE */ + break; +#endif /* ifdef SOCK_RAW */ + } + + if (sock->fd == INVALID_SOCKET) { + socket_errno = WSAGetLastError(); + free_socket(&sock, __LINE__); + + switch (socket_errno) { + case WSAEMFILE: + case WSAENOBUFS: + return (ISC_R_NORESOURCES); + + case WSAEPROTONOSUPPORT: + case WSAEPFNOSUPPORT: + case WSAEAFNOSUPPORT: + return (ISC_R_FAMILYNOSUPPORT); + + default: + strerror_r(socket_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() failed: %s", strbuf); + return (ISC_R_UNEXPECTED); + } + } + + result = make_nonblock(sock->fd); + if (result != ISC_R_SUCCESS) { + socket_log(__LINE__, sock, NULL, EVENT, + "closed %d %d %" PRIuFAST32 " make_nonblock_failed", + sock->pending_recv, sock->pending_send, + isc_refcount_current(&sock->references)); + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + free_socket(&sock, __LINE__); + return (result); + } + + /* + * Use minimum mtu if possible. + */ + use_min_mtu(sock); + +#if defined(USE_CMSG) || defined(SO_RCVBUF) + if (type == isc_sockettype_udp) { +#if defined(USE_CMSG) +#ifdef IPV6_RECVPKTINFO + /* 2292bis */ + if ((pf == AF_INET6) && + (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (char *)&on, sizeof(on)) < 0)) + { + strerror_r(WSAGetLastError(), strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_RECVPKTINFO) " + "failed: %s", + sock->fd, strbuf); + } +#else /* ifdef IPV6_RECVPKTINFO */ + /* 2292 */ + if ((pf == AF_INET6) && + (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, + (char *)&on, sizeof(on)) < 0)) + { + strerror_r(WSAGetLastError(), strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_PKTINFO) %s: %s", + sock->fd, strbuf); + } +#endif /* IPV6_RECVPKTINFO */ +#endif /* defined(USE_CMSG) */ + +#if defined(SO_RCVBUF) + optlen = sizeof(size); + if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, (char *)&size, + &optlen) >= 0 && + size < RCVBUFSIZE) + { + size = RCVBUFSIZE; + (void)setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, + (char *)&size, sizeof(size)); + } +#endif /* if defined(SO_RCVBUF) */ + } +#endif /* defined(USE_CMSG) || defined(SO_RCVBUF) */ + + _set_state(sock, SOCK_OPEN); + isc_refcount_init(&sock->references, 1); + *socketp = sock; + + iocompletionport_update(sock); + + if (dup_socket) { +#ifndef ISC_ALLOW_MAPPED + isc_socket_ipv6only(sock, true); +#endif /* ifndef ISC_ALLOW_MAPPED */ + + if (dup_socket->bound) { + isc_sockaddr_t local; + + result = isc_socket_getsockname(dup_socket, &local); + if (result != ISC_R_SUCCESS) { + isc_socket_close(sock); + return (result); + } + result = isc_socket_bind(sock, &local, + ISC_SOCKET_REUSEADDRESS); + if (result != ISC_R_SUCCESS) { + isc_socket_close(sock); + return (result); + } + } + sock->dupped = 1; + } + + /* + * Note we don't have to lock the socket like we normally would because + * there are no external references to it yet. + */ + LOCK(&manager->lock); + ISC_LIST_APPEND(manager->socklist, sock, link); + InterlockedIncrement(&manager->totalSockets); + UNLOCK(&manager->lock); + + socket_log(__LINE__, sock, NULL, CREATION, "created %u type %u", + sock->fd, type); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socket_create(isc_socketmgr_t *manager, int pf, isc_sockettype_t type, + isc_socket_t **socketp) { + return (socket_create(manager, pf, type, socketp, NULL)); +} + +isc_result_t +isc_socket_dup(isc_socket_t *sock, isc_socket_t **socketp) { + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + return (socket_create(sock->manager, sock->pf, sock->type, socketp, + sock)); +} + +isc_result_t +isc_socket_open(isc_socket_t *sock) { + REQUIRE(VALID_SOCKET(sock)); + + return (ISC_R_NOTIMPLEMENTED); +} + +/* + * Attach to a socket. Caller must explicitly detach when it is done. + */ +void +isc_socket_attach(isc_socket_t *sock, isc_socket_t **socketp) { + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + LOCK(&sock->lock); + CONSISTENT(sock); + UNLOCK(&sock->lock); + + isc_refcount_increment0(&sock->references); + + *socketp = sock; +} + +/* + * Dereference a socket. If this is the last reference to it, clean things + * up by destroying the socket. + */ +void +isc_socket_detach(isc_socket_t **socketp) { + isc_socket_t *sock; + uint32_t references; + + REQUIRE(socketp != NULL); + sock = *socketp; + *socketp = NULL; + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + references = isc_refcount_decrement(&sock->references); + + socket_log(__LINE__, sock, NULL, EVENT, + "detach_socket %d %d %" PRIuFAST32, sock->pending_recv, + sock->pending_send, isc_refcount_current(&sock->references)); + + if (references == 1 && sock->fd != INVALID_SOCKET) { + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + _set_state(sock, SOCK_CLOSED); + } + + maybe_free_socket(&sock, __LINE__); /* Also unlocks the socket lock */ +} + +isc_result_t +isc_socket_close(isc_socket_t *sock) { + REQUIRE(VALID_SOCKET(sock)); + + return (ISC_R_NOTIMPLEMENTED); +} + +/* + * Dequeue an item off the given socket's read queue, set the result code + * in the done event to the one provided, and send it to the task it was + * destined for. + * + * If the event to be sent is on a list, remove it before sending. If + * asked to, send and detach from the task as well. + * + * Caller must have the socket locked if the event is attached to the socket. + */ +static void +send_recvdone_event(isc_socket_t *sock, isc_socketevent_t **dev) { + isc_task_t *task; + + task = (*dev)->ev_sender; + (*dev)->ev_sender = sock; + + if (ISC_LINK_LINKED(*dev, ev_link)) { + ISC_LIST_DEQUEUE(sock->recv_list, *dev, ev_link); + } + + if (((*dev)->attributes & ISC_SOCKEVENTATTR_ATTACHED) != 0) { + isc_task_sendanddetach(&task, (isc_event_t **)dev); + } else { + isc_task_send(task, (isc_event_t **)dev); + } + + CONSISTENT(sock); +} + +/* + * See comments for send_recvdone_event() above. + */ +static void +send_senddone_event(isc_socket_t *sock, isc_socketevent_t **dev) { + isc_task_t *task; + + INSIST(dev != NULL && *dev != NULL); + + task = (*dev)->ev_sender; + (*dev)->ev_sender = sock; + + if (ISC_LINK_LINKED(*dev, ev_link)) { + ISC_LIST_DEQUEUE(sock->send_list, *dev, ev_link); + } + + if (((*dev)->attributes & ISC_SOCKEVENTATTR_ATTACHED) != 0) { + isc_task_sendanddetach(&task, (isc_event_t **)dev); + } else { + isc_task_send(task, (isc_event_t **)dev); + } + + CONSISTENT(sock); +} + +/* + * See comments for send_recvdone_event() above. + */ +static void +send_acceptdone_event(isc_socket_t *sock, isc_socket_newconnev_t **adev) { + isc_task_t *task; + + INSIST(adev != NULL && *adev != NULL); + + task = (*adev)->ev_sender; + (*adev)->ev_sender = sock; + + if (ISC_LINK_LINKED(*adev, ev_link)) { + ISC_LIST_DEQUEUE(sock->accept_list, *adev, ev_link); + } + + isc_task_sendanddetach(&task, (isc_event_t **)adev); + + CONSISTENT(sock); +} + +/* + * See comments for send_recvdone_event() above. + */ +static void +send_connectdone_event(isc_socket_t *sock, isc_socket_connev_t **cdev) { + isc_task_t *task; + + INSIST(cdev != NULL && *cdev != NULL); + + task = (*cdev)->ev_sender; + (*cdev)->ev_sender = sock; + + if (ISC_LINK_LINKED(*cdev, ev_link)) { + ISC_LIST_DEQUEUE(sock->connect_list, *cdev, ev_link); + } + + isc_task_sendanddetach(&task, (isc_event_t **)cdev); + + CONSISTENT(sock); +} + +/* + * On entry to this function, the event delivered is the internal + * readable event, and the first item on the accept_list should be + * the done event we want to send. If the list is empty, this is a no-op, + * so just close the new connection, unlock, and return. + * + * Note the socket is locked before entering here + */ +static void +internal_accept(isc_socket_t *sock, IoCompletionInfo *lpo, int accept_errno) { + isc_socket_newconnev_t *adev; + isc_result_t result = ISC_R_SUCCESS; + isc_socket_t *nsock; + struct sockaddr *localaddr; + int localaddr_len = sizeof(*localaddr); + struct sockaddr *remoteaddr; + int remoteaddr_len = sizeof(*remoteaddr); + + INSIST(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + socket_log(__LINE__, sock, NULL, TRACE, "internal_accept called"); + + INSIST(sock->listener); + + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_accept > 0); + sock->pending_accept--; + + adev = lpo->adev; + + /* + * If the event is no longer in the list we can just return. + */ + if (!acceptdone_is_active(sock, adev)) { + goto done; + } + + nsock = adev->newsocket; + + /* + * Pull off the done event. + */ + ISC_LIST_UNLINK(sock->accept_list, adev, ev_link); + + /* + * Extract the addresses from the socket, copy them into the structure, + * and return the new socket. + */ + ISCGetAcceptExSockaddrs( + lpo->acceptbuffer, 0, sizeof(SOCKADDR_STORAGE) + 16, + sizeof(SOCKADDR_STORAGE) + 16, (LPSOCKADDR *)&localaddr, + &localaddr_len, (LPSOCKADDR *)&remoteaddr, &remoteaddr_len); + memmove(&adev->address.type, remoteaddr, remoteaddr_len); + adev->address.length = remoteaddr_len; + nsock->address = adev->address; + nsock->pf = adev->address.type.sa.sa_family; + + socket_log(__LINE__, nsock, &nsock->address, TRACE, + "internal_accept parent %p", sock); + + result = make_nonblock(adev->newsocket->fd); + INSIST(result == ISC_R_SUCCESS); + + /* + * Use minimum mtu if possible. + */ + use_min_mtu(adev->newsocket); + + INSIST(setsockopt(nsock->fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char *)&sock->fd, sizeof(sock->fd)) == 0); + + /* + * Hook it up into the manager. + */ + nsock->bound = 1; + nsock->connected = 1; + _set_state(nsock, SOCK_OPEN); + + LOCK(&nsock->manager->lock); + ISC_LIST_APPEND(nsock->manager->socklist, nsock, link); + InterlockedIncrement(&nsock->manager->totalSockets); + UNLOCK(&nsock->manager->lock); + + socket_log(__LINE__, sock, &nsock->address, CREATION, + "accepted_connection new_socket %p fd %d", nsock, nsock->fd); + + adev->result = result; + send_acceptdone_event(sock, &adev); + +done: + CONSISTENT(sock); + UNLOCK(&sock->lock); + + HeapFree(hHeapHandle, 0, lpo->acceptbuffer); + lpo->acceptbuffer = NULL; +} + +/* + * Called when a socket with a pending connect() finishes. + * Note that the socket is locked before entering. + */ +static void +internal_connect(isc_socket_t *sock, IoCompletionInfo *lpo, int connect_errno) { + isc_socket_connev_t *cdev; + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_connect == 1); + sock->pending_connect = 0; + + /* + * If the event is no longer in the list we can just close and return. + */ + cdev = lpo->cdev; + if (!connectdone_is_active(sock, cdev)) { + sock->pending_connect = 0; + if (sock->fd != INVALID_SOCKET) { + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + _set_state(sock, SOCK_CLOSED); + } + CONSISTENT(sock); + UNLOCK(&sock->lock); + return; + } + + /* + * Check possible Windows network event error status here. + */ + if (connect_errno != 0) { + /* + * If the error is SOFT, just try again on this + * fd and pretend nothing strange happened. + */ + if (SOFT_ERROR(connect_errno) || + connect_errno == WSAEINPROGRESS) + { + sock->pending_connect = 1; + CONSISTENT(sock); + UNLOCK(&sock->lock); + return; + } + + /* + * Translate other errors into ISC_R_* flavors. + */ + switch (connect_errno) { +#define ERROR_MATCH(a, b) \ + case a: \ + result = b; \ + break; + ERROR_MATCH(WSAEACCES, ISC_R_NOPERM); + ERROR_MATCH(WSAEADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(WSAEAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(WSAECONNREFUSED, ISC_R_CONNREFUSED); + ERROR_MATCH(WSAEHOSTUNREACH, ISC_R_HOSTUNREACH); + ERROR_MATCH(WSAEHOSTDOWN, ISC_R_HOSTDOWN); + ERROR_MATCH(WSAENETUNREACH, ISC_R_NETUNREACH); + ERROR_MATCH(WSAENETDOWN, ISC_R_NETDOWN); + ERROR_MATCH(WSAENOBUFS, ISC_R_NORESOURCES); + ERROR_MATCH(WSAECONNRESET, ISC_R_CONNECTIONRESET); + ERROR_MATCH(WSAECONNABORTED, ISC_R_CONNECTIONRESET); + ERROR_MATCH(WSAETIMEDOUT, ISC_R_TIMEDOUT); +#undef ERROR_MATCH + default: + result = ISC_R_UNEXPECTED; + strerror_r(connect_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "internal_connect: connect() %s", + strbuf); + } + } else { + INSIST(setsockopt(sock->fd, SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == 0); + result = ISC_R_SUCCESS; + sock->connected = 1; + socket_log(__LINE__, sock, &sock->address, IOEVENT, + "internal_connect: success"); + } + + do { + cdev->result = result; + send_connectdone_event(sock, &cdev); + cdev = ISC_LIST_HEAD(sock->connect_list); + } while (cdev != NULL); + + UNLOCK(&sock->lock); +} + +/* + * Loop through the socket, returning ISC_R_EOF for each done event pending. + */ +static void +send_recvdone_abort(isc_socket_t *sock, isc_result_t result) { + isc_socketevent_t *dev; + + while (!ISC_LIST_EMPTY(sock->recv_list)) { + dev = ISC_LIST_HEAD(sock->recv_list); + dev->result = result; + send_recvdone_event(sock, &dev); + } +} + +/* + * Loop through the socket, returning result for each done event pending. + */ +static void +send_connectdone_abort(isc_socket_t *sock, isc_result_t result) { + isc_socket_connev_t *dev; + + while (!ISC_LIST_EMPTY(sock->connect_list)) { + dev = ISC_LIST_HEAD(sock->connect_list); + dev->result = result; + send_connectdone_event(sock, &dev); + } +} + +/* + * Take the data we received in our private buffer, and if any recv() calls on + * our list are satisfied, send the corresponding done event. + * + * If we need more data (there are still items on the recv_list after we consume + * all our data) then arrange for another system recv() call to fill our + * buffers. + */ +static void +internal_recv(isc_socket_t *sock, int nbytes) { + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + socket_log(__LINE__, sock, NULL, IOEVENT, + "internal_recv: %d bytes received", nbytes); + + /* + * If we got here, the I/O operation succeeded. However, we might + * still have removed this event from our notification list (or never + * placed it on it due to immediate completion.) + * Handle the reference counting here, and handle the cancellation + * event just after. + */ + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_recv > 0); + sock->pending_recv--; + + /* + * The only way we could have gotten here is that our I/O has + * successfully completed. Update our pointers, and move on. + * The only odd case here is that we might not have received + * enough data on a TCP stream to satisfy the minimum requirements. + * If this is the case, we will re-issue the recv() call for what + * we need. + * + * We do check for a recv() of 0 bytes on a TCP stream. This + * means the remote end has closed. + */ + if (nbytes == 0 && sock->type == isc_sockettype_tcp) { + send_recvdone_abort(sock, ISC_R_EOF); + maybe_free_socket(&sock, __LINE__); + return; + } + sock->recvbuf.remaining = nbytes; + sock->recvbuf.consume_position = sock->recvbuf.base; + completeio_recv(sock); + + /* + * If there are more receivers waiting for data, queue another receive + * here. + */ + queue_receive_request(sock); + + /* + * Unlock and/or destroy if we are the last thing this socket has left + * to do. + */ + maybe_free_socket(&sock, __LINE__); +} + +static void +internal_send(isc_socket_t *sock, isc_socketevent_t *dev, + struct msghdr *messagehdr, int nbytes, int send_errno, + IoCompletionInfo *lpo) { + /* + * Find out what socket this is and lock it. + */ + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + socket_log(__LINE__, sock, NULL, IOEVENT, + "internal_send: task got socket event %p", dev); + + if (lpo->buf != NULL) { + socket_log(__LINE__, sock, NULL, TRACE, "free_buffer %p", + lpo->buf); + + HeapFree(hHeapHandle, 0, lpo->buf); + lpo->buf = NULL; + lpo->buflen = 0; + } + + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_send > 0); + sock->pending_send--; + + /* If the event is no longer in the list we can just return */ + if (!senddone_is_active(sock, dev)) { + goto done; + } + + /* + * Set the error code and send things on its way. + */ + switch (completeio_send(sock, dev, messagehdr, nbytes, send_errno)) { + case DOIO_SOFT: + break; + case DOIO_HARD: + case DOIO_SUCCESS: + send_senddone_event(sock, &dev); + break; + } + +done: + maybe_free_socket(&sock, __LINE__); +} + +/* + * These return if the done event passed in is on the list. + * Using these ensures we will not double-send an event. + */ +static bool +senddone_is_active(isc_socket_t *sock, isc_socketevent_t *dev) { + isc_socketevent_t *ldev; + + ldev = ISC_LIST_HEAD(sock->send_list); + while (ldev != NULL && ldev != dev) { + ldev = ISC_LIST_NEXT(ldev, ev_link); + } + + return (ldev == NULL ? false : true); +} + +static bool +acceptdone_is_active(isc_socket_t *sock, isc_socket_newconnev_t *dev) { + isc_socket_newconnev_t *ldev; + + ldev = ISC_LIST_HEAD(sock->accept_list); + while (ldev != NULL && ldev != dev) { + ldev = ISC_LIST_NEXT(ldev, ev_link); + } + + return (ldev == NULL ? false : true); +} + +static bool +connectdone_is_active(isc_socket_t *sock, isc_socket_connev_t *dev) { + isc_socket_connev_t *cdev; + + cdev = ISC_LIST_HEAD(sock->connect_list); + while (cdev != NULL && cdev != dev) { + cdev = ISC_LIST_NEXT(cdev, ev_link); + } + + return (cdev == NULL ? false : true); +} + +/* */ +/* The Windows network stack seems to have two very distinct paths depending */ +/* on what is installed. Specifically, if something is looking at network */ +/* connections (like an anti-virus or anti-malware application, such as */ +/* McAfee products) Windows may return additional error conditions which */ +/* were not previously returned. */ +/* */ +/* One specific one is when a TCP SYN scan is used. In this situation, */ +/* Windows responds with the SYN-ACK, but the scanner never responds with */ +/* the 3rd packet, the ACK. Windows considers this a partially open connection. + */ +/* Most Unix networking stacks, and Windows without McAfee installed, will */ +/* not return this to the caller. However, with this product installed, */ +/* Windows returns this as a failed status on the Accept() call. Here, we */ +/* will just re-issue the ISCAcceptEx() call as if nothing had happened. */ +/* */ +/* This code should only be called when the listening socket has received */ +/* such an error. Additionally, the "parent" socket must be locked. */ +/* Additionally, the lpo argument is re-used here, and must not be freed */ +/* by the caller. */ +/* */ +static isc_result_t +restart_accept(isc_socket_t *parent, IoCompletionInfo *lpo) { + isc_socket_t *nsock = lpo->adev->newsocket; + SOCKET new_fd; + + /* + * AcceptEx() requires we pass in a socket. Note that we carefully + * do not close the previous socket in case of an error message returned + * by our new socket() call. If we return an error here, our caller + * will clean up. + */ + new_fd = socket(parent->pf, SOCK_STREAM, IPPROTO_TCP); + if (nsock->fd == INVALID_SOCKET) { + return (ISC_R_FAILURE); /* parent will ask windows for error */ + /* message */ + } + closesocket(nsock->fd); + nsock->fd = new_fd; + + memset(&lpo->overlapped, 0, sizeof(lpo->overlapped)); + + ISCAcceptEx(parent->fd, nsock->fd, /* Accepted Socket */ + lpo->acceptbuffer, /* Buffer for initial Recv */ + 0, /* Length of Buffer */ + sizeof(SOCKADDR_STORAGE) + 16, /* Local address length + 16 + */ + sizeof(SOCKADDR_STORAGE) + 16, /* Remote address length + 16 + */ + (LPDWORD)&lpo->received_bytes, /* Bytes Recved */ + (LPOVERLAPPED)lpo /* Overlapped structure */ + ); + + InterlockedDecrement(&nsock->manager->iocp_total); + iocompletionport_update(nsock); + + return (ISC_R_SUCCESS); +} + +/* + * This is the I/O Completion Port Worker Function. It loops forever + * waiting for I/O to complete and then forwards them for further + * processing. There are a number of these in separate threads. + */ +static isc_threadresult_t WINAPI +SocketIoThread(LPVOID ThreadContext) { + isc_socketmgr_t *manager = ThreadContext; + DWORD nbytes; + IoCompletionInfo *lpo = NULL; + isc_socket_t *sock = NULL; + int request; + struct msghdr *messagehdr = NULL; + int errval; + char strbuf[ISC_STRERRORSIZE]; + int errstatus; + + REQUIRE(VALID_MANAGER(manager)); + + /* + * Set the thread priority high enough so I/O will + * preempt normal recv packet processing, but not + * higher than the timer sync thread. + */ + if (!SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_ABOVE_NORMAL)) + { + errval = GetLastError(); + strerror_r(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, "Can't set thread priority: %s", + strbuf); + } + + /* + * Loop forever waiting on I/O Completions and then processing them + */ + while (TRUE) { + BOOL bSuccess; + + wait_again: + bSuccess = GetQueuedCompletionStatus( + manager->hIoCompletionPort, &nbytes, (PULONG_PTR)&sock, + (LPWSAOVERLAPPED *)&lpo, INFINITE); + if (lpo == NULL) { /* Received request to exit */ + break; + } + + REQUIRE(VALID_SOCKET(sock)); + + request = lpo->request_type; + + if (!bSuccess) { + errstatus = GetLastError(); + } else { + errstatus = 0; + } + if (!bSuccess && errstatus != ERROR_MORE_DATA) { + isc_result_t isc_result; + + /* + * Did the I/O operation complete? + */ + isc_result = isc__errno2result(errstatus); + + LOCK(&sock->lock); + CONSISTENT(sock); + switch (request) { + case SOCKET_RECV: + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_recv > 0); + sock->pending_recv--; + if (!sock->connected && + ((errstatus == ERROR_HOST_UNREACHABLE) || + (errstatus == WSAENETRESET) || + (errstatus == WSAECONNRESET))) + { + /* ignore soft errors */ + queue_receive_request(sock); + break; + } + send_recvdone_abort(sock, isc_result); + if (isc_result == ISC_R_UNEXPECTED) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "SOCKET_RECV: Windows " + "error code: %d, " + "returning ISC error " + "%d", + errstatus, isc_result); + } + break; + + case SOCKET_SEND: + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_send > 0); + sock->pending_send--; + if (senddone_is_active(sock, lpo->dev)) { + lpo->dev->result = isc_result; + socket_log(__LINE__, sock, NULL, EVENT, + "canceled_send"); + send_senddone_event(sock, &lpo->dev); + } + break; + + case SOCKET_ACCEPT: + INSIST(sock->pending_iocp > 0); + INSIST(sock->pending_accept > 0); + + socket_log(__LINE__, sock, NULL, EVENT, + "Accept: errstatus=%d isc_result=%d", + errstatus, isc_result); + + if (acceptdone_is_active(sock, lpo->adev)) { + if (restart_accept(sock, lpo) == + ISC_R_SUCCESS) + { + UNLOCK(&sock->lock); + goto wait_again; + } else { + errstatus = GetLastError(); + isc_result = isc__errno2result( + errstatus); + socket_log(__LINE__, sock, NULL, + EVENT, + "restart_accept() " + "failed: " + "errstatus=%d " + "isc_result=%d", + errstatus, + isc_result); + } + } + + sock->pending_iocp--; + sock->pending_accept--; + if (acceptdone_is_active(sock, lpo->adev)) { + closesocket(lpo->adev->newsocket->fd); + lpo->adev->newsocket->fd = + INVALID_SOCKET; + isc_refcount_decrementz( + &lpo->adev->newsocket + ->references); + free_socket(&lpo->adev->newsocket, + __LINE__); + lpo->adev->result = isc_result; + socket_log(__LINE__, sock, NULL, EVENT, + "canceled_accept"); + send_acceptdone_event(sock, &lpo->adev); + } + break; + + case SOCKET_CONNECT: + INSIST(sock->pending_iocp > 0); + sock->pending_iocp--; + INSIST(sock->pending_connect == 1); + sock->pending_connect = 0; + if (connectdone_is_active(sock, lpo->cdev)) { + socket_log(__LINE__, sock, NULL, EVENT, + "canceled_connect"); + send_connectdone_abort(sock, + isc_result); + } + break; + } + maybe_free_socket(&sock, __LINE__); + + if (lpo != NULL) { + HeapFree(hHeapHandle, 0, lpo); + } + continue; + } + + messagehdr = &lpo->messagehdr; + + switch (request) { + case SOCKET_RECV: + internal_recv(sock, nbytes); + break; + case SOCKET_SEND: + internal_send(sock, lpo->dev, messagehdr, nbytes, + errstatus, lpo); + break; + case SOCKET_ACCEPT: + internal_accept(sock, lpo, errstatus); + break; + case SOCKET_CONNECT: + internal_connect(sock, lpo, errstatus); + break; + } + + if (lpo != NULL) { + HeapFree(hHeapHandle, 0, lpo); + } + } + + /* + * Exit Completion Port Thread + */ + manager_log(manager, TRACE, "SocketIoThread exiting"); + return ((isc_threadresult_t)0); +} + +/* + * Create a new socket manager. + */ +isc_result_t +isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp) { + return (isc_socketmgr_create2(mctx, managerp, 0, 1)); +} + +isc_result_t +isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks, int nthreads) { + isc_socketmgr_t *manager; + + REQUIRE(managerp != NULL && *managerp == NULL); + + if (maxsocks != 0) { + return (ISC_R_NOTIMPLEMENTED); + } + + manager = isc_mem_get(mctx, sizeof(*manager)); + + InitSockets(); + + manager->magic = SOCKET_MANAGER_MAGIC; + manager->mctx = NULL; + manager->stats = NULL; + ISC_LIST_INIT(manager->socklist); + isc_mutex_init(&manager->lock); + isc_condition_init(&manager->shutdown_ok); + + isc_mem_attach(mctx, &manager->mctx); + if (nthreads == 0) { + nthreads = isc_os_ncpus() + 1; + } + manager->maxIOCPThreads = min(nthreads, MAX_IOCPTHREADS); + + iocompletionport_init(manager); /* Create the Completion Ports */ + + manager->bShutdown = false; + manager->totalSockets = 0; + manager->iocp_total = 0; + manager->maxudp = 0; + + *managerp = manager; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socketmgr_getmaxsockets(isc_socketmgr_t *manager, unsigned int *nsockp) { + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(nsockp != NULL); + + return (ISC_R_NOTIMPLEMENTED); +} + +void +isc_socketmgr_setstats(isc_socketmgr_t *manager, isc_stats_t *stats) { + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(ISC_LIST_EMPTY(manager->socklist)); + REQUIRE(manager->stats == NULL); + REQUIRE(isc_stats_ncounters(stats) == isc_sockstatscounter_max); + + isc_stats_attach(stats, &manager->stats); +} + +void +isc_socketmgr_destroy(isc_socketmgr_t **managerp) { + isc_socketmgr_t *manager; + + /* + * Destroy a socket manager. + */ + + REQUIRE(managerp != NULL); + manager = *managerp; + *managerp = NULL; + REQUIRE(VALID_MANAGER(manager)); + + LOCK(&manager->lock); + + /* + * Wait for all sockets to be destroyed. + */ + while (!ISC_LIST_EMPTY(manager->socklist)) { + manager_log(manager, CREATION, "sockets exist"); + WAIT(&manager->shutdown_ok, &manager->lock); + } + + UNLOCK(&manager->lock); + + /* + * Here, we need to had some wait code for the completion port + * thread. + */ + signal_iocompletionport_exit(manager); + manager->bShutdown = true; + + /* + * Wait for threads to exit. + */ + for (int i = 0; i < manager->maxIOCPThreads; i++) { + isc_thread_join((isc_thread_t)manager->hIOCPThreads[i], NULL); + } + /* + * Clean up. + */ + + CloseHandle(manager->hIoCompletionPort); + + (void)isc_condition_destroy(&manager->shutdown_ok); + + isc_mutex_destroy(&manager->lock); + if (manager->stats != NULL) { + isc_stats_detach(&manager->stats); + } + manager->magic = 0; + isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager)); +} + +static void +queue_receive_event(isc_socket_t *sock, isc_task_t *task, + isc_socketevent_t *dev) { + isc_task_t *ntask = NULL; + + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + /* + * Enqueue the request. + */ + INSIST(!ISC_LINK_LINKED(dev, ev_link)); + ISC_LIST_ENQUEUE(sock->recv_list, dev, ev_link); + + socket_log(__LINE__, sock, NULL, EVENT, + "queue_receive_event: event %p -> task %p", dev, ntask); +} + +/* + * Check the pending receive queue, and if we have data pending, give it to this + * caller. If we have none, queue an I/O request. If this caller is not the + * first on the list, then we will just queue this event and return. + * + * Caller must have the socket locked. + */ +static isc_result_t +socket_recv(isc_socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, + unsigned int flags) { + isc_result_t result = ISC_R_SUCCESS; + + dev->ev_sender = task; + + if (sock->fd == INVALID_SOCKET) { + return (ISC_R_EOF); + } + + /* + * Queue our event on the list of things to do. Call our function to + * attempt to fill buffers as much as possible, and return done events. + * We are going to lie about our handling of the ISC_SOCKFLAG_IMMEDIATE + * here and tell our caller that we could not satisfy it immediately. + */ + queue_receive_event(sock, task, dev); + if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0) { + result = ISC_R_INPROGRESS; + } + + completeio_recv(sock); + + /* + * If there are more receivers waiting for data, queue another receive + * here. If the + */ + queue_receive_request(sock); + + return (result); +} + +isc_result_t +isc_socket_recv(isc_socket_t *sock, isc_region_t *region, unsigned int minimum, + isc_task_t *task, isc_taskaction_t action, void *arg) { + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + INSIST(sock->bound); + + dev = allocate_socketevent(manager->mctx, sock, ISC_SOCKEVENT_RECVDONE, + action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + + ret = isc_socket_recv2(sock, region, minimum, task, dev, 0); + UNLOCK(&sock->lock); + return (ret); +} + +isc_result_t +isc_socket_recv2(isc_socket_t *sock, isc_region_t *region, unsigned int minimum, + isc_task_t *task, isc_socketevent_t *event, + unsigned int flags) { + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + event->result = ISC_R_UNEXPECTED; + event->ev_sender = sock; + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + event->region = *region; + event->n = 0; + event->offset = 0; + event->attributes = 0; + + /* + * UDP sockets are always partial read. + */ + if (sock->type == isc_sockettype_udp) { + event->minimum = 1; + } else { + if (minimum == 0) { + event->minimum = region->length; + } else { + event->minimum = minimum; + } + } + + ret = socket_recv(sock, event, task, flags); + UNLOCK(&sock->lock); + return (ret); +} + +/* + * Caller must have the socket locked. + */ +static isc_result_t +socket_send(isc_socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, + const isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags) { + int io_state; + int send_errno = 0; + int cc = 0; + isc_task_t *ntask = NULL; + isc_result_t result = ISC_R_SUCCESS; + + dev->ev_sender = task; + + set_dev_address(address, sock, dev); + if (pktinfo != NULL) { + socket_log(__LINE__, sock, NULL, TRACE, + "pktinfo structure provided, ifindex %u (set to 0)", + pktinfo->ipi6_ifindex); + + dev->attributes |= ISC_SOCKEVENTATTR_PKTINFO; + dev->pktinfo = *pktinfo; + /* + * Set the pktinfo index to 0 here, to let the kernel decide + * what interface it should send on. + */ + dev->pktinfo.ipi6_ifindex = 0; + } + + io_state = startio_send(sock, dev, &cc, &send_errno); + switch (io_state) { + case DOIO_PENDING: /* I/O started. Enqueue completion event. */ + case DOIO_SOFT: + /* + * We couldn't send all or part of the request right now, so + * queue it unless ISC_SOCKFLAG_NORETRY is set. + */ + if ((flags & ISC_SOCKFLAG_NORETRY) == 0 || + io_state == DOIO_PENDING) + { + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + /* + * Enqueue the request. + */ + INSIST(!ISC_LINK_LINKED(dev, ev_link)); + ISC_LIST_ENQUEUE(sock->send_list, dev, ev_link); + + socket_log(__LINE__, sock, NULL, EVENT, + "socket_send: event %p -> task %p", dev, + ntask); + + if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0) { + result = ISC_R_INPROGRESS; + } + break; + } + + case DOIO_SUCCESS: + break; + } + + return (result); +} + +isc_result_t +isc_socket_send(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, + isc_taskaction_t action, void *arg) { + /* + * REQUIRE() checking is performed in isc_socket_sendto(). + */ + return (isc_socket_sendto(sock, region, task, action, arg, NULL, NULL)); +} + +isc_result_t +isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, + isc_taskaction_t action, void *arg, + const isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) { + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + REQUIRE(region != NULL); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + INSIST(sock->bound); + + dev = allocate_socketevent(manager->mctx, sock, ISC_SOCKEVENT_SENDDONE, + action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + dev->region = *region; + + ret = socket_send(sock, dev, task, address, pktinfo, 0); + UNLOCK(&sock->lock); + return (ret); +} + +isc_result_t +isc_socket_sendto2(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, + const isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, unsigned int flags) { + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + REQUIRE((flags & ~(ISC_SOCKFLAG_IMMEDIATE | ISC_SOCKFLAG_NORETRY)) == + 0); + if ((flags & ISC_SOCKFLAG_NORETRY) != 0) { + REQUIRE(sock->type == isc_sockettype_udp); + } + event->ev_sender = sock; + event->result = ISC_R_UNEXPECTED; + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + event->region = *region; + event->n = 0; + event->offset = 0; + event->attributes = 0; + + ret = socket_send(sock, event, task, address, pktinfo, flags); + UNLOCK(&sock->lock); + return (ret); +} + +isc_result_t +isc_socket_bind(isc_socket_t *sock, const isc_sockaddr_t *sockaddr, + isc_socket_options_t options) { + int bind_errno; + char strbuf[ISC_STRERRORSIZE]; + int on = 1; + + REQUIRE(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + INSIST(!sock->bound); + INSIST(!sock->dupped); + + if (sock->pf != sockaddr->type.sa.sa_family) { + UNLOCK(&sock->lock); + return (ISC_R_FAMILYMISMATCH); + } + /* + * Only set SO_REUSEADDR when we want a specific port. + */ + if ((options & ISC_SOCKET_REUSEADDRESS) != 0 && + isc_sockaddr_getport(sockaddr) != (in_port_t)0 && + setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, + sizeof(on)) < 0) + { + UNEXPECTED_ERROR(__FILE__, __LINE__, "setsockopt(%d) failed", + sock->fd); + /* Press on... */ + } + if (bind(sock->fd, &sockaddr->type.sa, sockaddr->length) < 0) { + bind_errno = WSAGetLastError(); + UNLOCK(&sock->lock); + switch (bind_errno) { + case WSAEACCES: + return (ISC_R_NOPERM); + case WSAEADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case WSAEADDRINUSE: + return (ISC_R_ADDRINUSE); + case WSAEINVAL: + return (ISC_R_BOUND); + default: + strerror_r(bind_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "bind: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } + + socket_log(__LINE__, sock, sockaddr, TRACE, "bound"); + sock->bound = 1; + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socket_filter(isc_socket_t *sock, const char *filter) { + UNUSED(sock); + UNUSED(filter); + + REQUIRE(VALID_SOCKET(sock)); + return (ISC_R_NOTIMPLEMENTED); +} + +/* + * Set up to listen on a given socket. We do this by creating an internal + * event that will be dispatched when the socket has read activity. The + * watcher will send the internal event to the task when there is a new + * connection. + * + * Unlike in read, we don't preallocate a done event here. Every time there + * is a new connection we'll have to allocate a new one anyway, so we might + * as well keep things simple rather than having to track them. + */ +isc_result_t +isc_socket_listen(isc_socket_t *sock, unsigned int backlog) { + char strbuf[ISC_STRERRORSIZE]; +#if defined(ENABLE_TCP_FASTOPEN) && defined(TCP_FASTOPEN) + char on = 1; +#endif /* if defined(ENABLE_TCP_FASTOPEN) && defined(TCP_FASTOPEN) */ + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + REQUIRE(!sock->listener); + REQUIRE(sock->bound); + REQUIRE(sock->type == isc_sockettype_tcp); + + if (backlog == 0) { + backlog = SOMAXCONN; + } + + if (listen(sock->fd, (int)backlog) < 0) { + UNLOCK(&sock->lock); + strerror_r(WSAGetLastError(), strbuf, sizeof(strbuf)); + + UNEXPECTED_ERROR(__FILE__, __LINE__, "listen: %s", strbuf); + + return (ISC_R_UNEXPECTED); + } + +#if defined(ENABLE_TCP_FASTOPEN) && defined(TCP_FASTOPEN) + if (setsockopt(sock->fd, IPPROTO_TCP, TCP_FASTOPEN, &on, sizeof(on)) < + 0) + { + strerror_r(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, TCP_FASTOPEN) failed with %s", + sock->fd, strbuf); + /* TCP_FASTOPEN is experimental so ignore failures */ + } +#endif /* if defined(ENABLE_TCP_FASTOPEN) && defined(TCP_FASTOPEN) */ + + socket_log(__LINE__, sock, NULL, TRACE, "listening"); + sock->listener = 1; + _set_state(sock, SOCK_LISTEN); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +/* + * This should try to do aggressive accept() XXXMLG + */ +isc_result_t +isc_socket_accept(isc_socket_t *sock, isc_task_t *task, isc_taskaction_t action, + void *arg) { + isc_socket_newconnev_t *adev; + isc_socketmgr_t *manager; + isc_task_t *ntask = NULL; + isc_socket_t *nsock; + isc_result_t result; + IoCompletionInfo *lpo; + + REQUIRE(VALID_SOCKET(sock)); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + REQUIRE(sock->listener); + + /* + * Sender field is overloaded here with the task we will be sending + * this event to. Just before the actual event is delivered the + * actual ev_sender will be touched up to be the socket. + */ + adev = (isc_socket_newconnev_t *)isc_event_allocate( + manager->mctx, task, ISC_SOCKEVENT_NEWCONN, action, arg, + sizeof(*adev)); + ISC_LINK_INIT(adev, ev_link); + + result = allocate_socket(manager, sock->type, &nsock); + if (result != ISC_R_SUCCESS) { + isc_event_free((isc_event_t **)&adev); + UNLOCK(&sock->lock); + return (result); + } + + /* + * AcceptEx() requires we pass in a socket. + */ + nsock->fd = socket(sock->pf, SOCK_STREAM, IPPROTO_TCP); + if (nsock->fd == INVALID_SOCKET) { + free_socket(&nsock, __LINE__); + isc_event_free((isc_event_t **)&adev); + UNLOCK(&sock->lock); + return (ISC_R_FAILURE); /* XXXMLG need real error message */ + } + + /* + * Attach to socket and to task. + */ + isc_task_attach(task, &ntask); + if (isc_task_exiting(ntask)) { + free_socket(&nsock, __LINE__); + isc_task_detach(&ntask); + isc_event_free(ISC_EVENT_PTR(&adev)); + UNLOCK(&sock->lock); + return (ISC_R_SHUTTINGDOWN); + } + isc_refcount_increment0(&nsock->references); + + adev->ev_sender = ntask; + adev->newsocket = nsock; + _set_state(nsock, SOCK_ACCEPT); + + /* + * Queue io completion for an accept(). + */ + lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, + sizeof(IoCompletionInfo)); + RUNTIME_CHECK(lpo != NULL); + lpo->acceptbuffer = + (void *)HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, + (sizeof(SOCKADDR_STORAGE) + 16) * 2); + RUNTIME_CHECK(lpo->acceptbuffer != NULL); + + lpo->adev = adev; + lpo->request_type = SOCKET_ACCEPT; + + ISCAcceptEx(sock->fd, nsock->fd, /* Accepted Socket */ + lpo->acceptbuffer, /* Buffer for initial Recv */ + 0, /* Length of Buffer */ + sizeof(SOCKADDR_STORAGE) + 16, /* Local address length + 16 + */ + sizeof(SOCKADDR_STORAGE) + 16, /* Remote address length + 16 + */ + (LPDWORD)&lpo->received_bytes, /* Bytes Recved */ + (LPOVERLAPPED)lpo /* Overlapped structure */ + ); + iocompletionport_update(nsock); + + socket_log(__LINE__, sock, NULL, TRACE, "accepting for nsock %p fd %d", + nsock, nsock->fd); + + /* + * Enqueue the event + */ + ISC_LIST_ENQUEUE(sock->accept_list, adev, ev_link); + sock->pending_accept++; + sock->pending_iocp++; + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socket_connect(isc_socket_t *sock, const isc_sockaddr_t *addr, + isc_task_t *task, isc_taskaction_t action, void *arg) { + char strbuf[ISC_STRERRORSIZE]; + isc_socket_connev_t *cdev; + isc_task_t *ntask = NULL; + isc_socketmgr_t *manager; + IoCompletionInfo *lpo; + int bind_errno; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(addr != NULL); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(addr != NULL); + + if (isc_sockaddr_ismulticast(addr)) { + return (ISC_R_MULTICAST); + } + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + /* + * Windows sockets won't connect unless the socket is bound. + */ + if (!sock->bound) { + isc_sockaddr_t any; + + isc_sockaddr_anyofpf(&any, isc_sockaddr_pf(addr)); + if (bind(sock->fd, &any.type.sa, any.length) < 0) { + bind_errno = WSAGetLastError(); + UNLOCK(&sock->lock); + switch (bind_errno) { + case WSAEACCES: + return (ISC_R_NOPERM); + case WSAEADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case WSAEADDRINUSE: + return (ISC_R_ADDRINUSE); + case WSAEINVAL: + return (ISC_R_BOUND); + default: + strerror_r(bind_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "bind: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } + sock->bound = 1; + } + + cdev = (isc_socket_connev_t *)isc_event_allocate( + manager->mctx, sock, ISC_SOCKEVENT_CONNECT, action, arg, + sizeof(*cdev)); + ISC_LINK_INIT(cdev, ev_link); + + if (sock->connected) { + INSIST(isc_sockaddr_equal(&sock->address, addr)); + cdev->result = ISC_R_SUCCESS; + isc_task_send(task, ISC_EVENT_PTR(&cdev)); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); + } + + if ((sock->type == isc_sockettype_tcp) && !sock->pending_connect) { + /* + * Queue io completion for an accept(). + */ + lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle, + HEAP_ZERO_MEMORY, + sizeof(IoCompletionInfo)); + lpo->cdev = cdev; + lpo->request_type = SOCKET_CONNECT; + + sock->address = *addr; + ISCConnectEx(sock->fd, &addr->type.sa, addr->length, NULL, 0, + NULL, (LPOVERLAPPED)lpo); + + /* + * Attach to task. + */ + isc_task_attach(task, &ntask); + cdev->ev_sender = ntask; + + sock->pending_connect = 1; + _set_state(sock, SOCK_CONNECT); + + /* + * Enqueue the request. + */ + INSIST(!ISC_LINK_LINKED(cdev, ev_link)); + ISC_LIST_ENQUEUE(sock->connect_list, cdev, ev_link); + sock->pending_iocp++; + } else if (sock->type == isc_sockettype_tcp) { + INSIST(sock->pending_connect); + INSIST(isc_sockaddr_equal(&sock->address, addr)); + isc_task_attach(task, &ntask); + cdev->ev_sender = ntask; + INSIST(!ISC_LINK_LINKED(cdev, ev_link)); + ISC_LIST_ENQUEUE(sock->connect_list, cdev, ev_link); + } else { + REQUIRE(!sock->pending_connect); + WSAConnect(sock->fd, &addr->type.sa, addr->length, NULL, NULL, + NULL, NULL); + cdev->result = ISC_R_SUCCESS; + isc_task_send(task, (isc_event_t **)&cdev); + } + CONSISTENT(sock); + UNLOCK(&sock->lock); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socket_getpeername(isc_socket_t *sock, isc_sockaddr_t *addressp) { + isc_result_t result; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(addressp != NULL); + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + if (sock->connected) { + *addressp = sock->address; + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOTCONNECTED; + } + + UNLOCK(&sock->lock); + + return (result); +} + +isc_result_t +isc_socket_getsockname(isc_socket_t *sock, isc_sockaddr_t *addressp) { + socklen_t len; + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(addressp != NULL); + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + if (!sock->bound) { + result = ISC_R_NOTBOUND; + goto out; + } + + result = ISC_R_SUCCESS; + + len = sizeof(addressp->type); + if (getsockname(sock->fd, &addressp->type.sa, (void *)&len) < 0) { + strerror_r(WSAGetLastError(), strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "getsockname: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto out; + } + addressp->length = (unsigned int)len; + +out: + UNLOCK(&sock->lock); + + return (result); +} + +/* + * Run through the list of events on this socket, and cancel the ones + * queued for task "task" of type "how". "how" is a bitmask. + */ +void +isc_socket_cancel(isc_socket_t *sock, isc_task_t *task, unsigned int how) { + REQUIRE(VALID_SOCKET(sock)); + + /* + * Quick exit if there is nothing to do. Don't even bother locking + * in this case. + */ + if (how == 0) { + return; + } + + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return; + } + + /* + * All of these do the same thing, more or less. + * Each will: + * o If the internal event is marked as "posted" try to + * remove it from the task's queue. If this fails, mark it + * as canceled instead, and let the task clean it up later. + * o For each I/O request for that task of that type, post + * its done event with status of "ISC_R_CANCELED". + * o Reset any state needed. + */ + + if ((how & ISC_SOCKCANCEL_RECV) != 0) { + isc_socketevent_t *dev; + isc_socketevent_t *next; + isc_task_t *current_task; + + dev = ISC_LIST_HEAD(sock->recv_list); + while (dev != NULL) { + current_task = dev->ev_sender; + next = ISC_LIST_NEXT(dev, ev_link); + if ((task == NULL) || (task == current_task)) { + dev->result = ISC_R_CANCELED; + send_recvdone_event(sock, &dev); + } + dev = next; + } + } + how &= ~ISC_SOCKCANCEL_RECV; + + if ((how & ISC_SOCKCANCEL_SEND) != 0) { + isc_socketevent_t *dev; + isc_socketevent_t *next; + isc_task_t *current_task; + + dev = ISC_LIST_HEAD(sock->send_list); + + while (dev != NULL) { + current_task = dev->ev_sender; + next = ISC_LIST_NEXT(dev, ev_link); + if ((task == NULL) || (task == current_task)) { + dev->result = ISC_R_CANCELED; + send_senddone_event(sock, &dev); + } + dev = next; + } + } + how &= ~ISC_SOCKCANCEL_SEND; + + if (((how & ISC_SOCKCANCEL_ACCEPT) != 0) && + !ISC_LIST_EMPTY(sock->accept_list)) + { + isc_socket_newconnev_t *dev; + isc_socket_newconnev_t *next; + isc_task_t *current_task; + + dev = ISC_LIST_HEAD(sock->accept_list); + while (dev != NULL) { + current_task = dev->ev_sender; + next = ISC_LIST_NEXT(dev, ev_link); + + if ((task == NULL) || (task == current_task)) { + isc_refcount_decrementz( + &dev->newsocket->references); + closesocket(dev->newsocket->fd); + dev->newsocket->fd = INVALID_SOCKET; + free_socket(&dev->newsocket, __LINE__); + + dev->result = ISC_R_CANCELED; + send_acceptdone_event(sock, &dev); + } + + dev = next; + } + } + how &= ~ISC_SOCKCANCEL_ACCEPT; + + if (((how & ISC_SOCKCANCEL_CONNECT) != 0) && + !ISC_LIST_EMPTY(sock->connect_list)) + { + isc_socket_connev_t *dev; + isc_socket_connev_t *next; + isc_task_t *current_task; + + INSIST(sock->pending_connect); + + dev = ISC_LIST_HEAD(sock->connect_list); + + while (dev != NULL) { + current_task = dev->ev_sender; + next = ISC_LIST_NEXT(dev, ev_link); + if ((task == NULL) || (task == current_task)) { + dev->result = ISC_R_CANCELED; + send_connectdone_event(sock, &dev); + } + dev = next; + } + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + _set_state(sock, SOCK_CLOSED); + } + how &= ~ISC_SOCKCANCEL_CONNECT; + UNUSED(how); + + maybe_free_socket(&sock, __LINE__); +} + +isc_sockettype_t +isc_socket_gettype(isc_socket_t *sock) { + isc_sockettype_t type; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + /* + * make sure that the socket's not closed + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + type = sock->type; + UNLOCK(&sock->lock); + return (type); +} + +void +isc_socket_ipv6only(isc_socket_t *sock, bool yes) { +#if defined(IPV6_V6ONLY) + int onoff = yes ? 1 : 0; +#else /* if defined(IPV6_V6ONLY) */ + UNUSED(yes); +#endif /* if defined(IPV6_V6ONLY) */ + + REQUIRE(VALID_SOCKET(sock)); + +#ifdef IPV6_V6ONLY + if (sock->pf == AF_INET6) { + (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&onoff, sizeof(onoff)); + } +#endif /* ifdef IPV6_V6ONLY */ +} + +void +isc_socket_dscp(isc_socket_t *sock, isc_dscp_t dscp) { +#if !defined(IP_TOS) && !defined(IPV6_TCLASS) + UNUSED(dscp); +#else /* if !defined(IP_TOS) && !defined(IPV6_TCLASS) */ + if (dscp < 0) { + return; + } + + dscp <<= 2; + dscp &= 0xff; +#endif /* if !defined(IP_TOS) && !defined(IPV6_TCLASS) */ + + REQUIRE(VALID_SOCKET(sock)); + +#ifdef IP_TOS + if (sock->pf == AF_INET) { + (void)setsockopt(sock->fd, IPPROTO_IP, IP_TOS, (char *)&dscp, + sizeof(dscp)); + } +#endif /* ifdef IP_TOS */ +#ifdef IPV6_TCLASS + if (sock->pf == AF_INET6) { + (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, + (char *)&dscp, sizeof(dscp)); + } +#endif /* ifdef IPV6_TCLASS */ +} + +void +isc_socket_cleanunix(const isc_sockaddr_t *addr, bool active) { + UNUSED(addr); + UNUSED(active); +} + +isc_result_t +isc_socket_permunix(const isc_sockaddr_t *addr, uint32_t perm, uint32_t owner, + uint32_t group) { + UNUSED(addr); + UNUSED(perm); + UNUSED(owner); + UNUSED(group); + return (ISC_R_NOTIMPLEMENTED); +} + +void +isc_socket_setname(isc_socket_t *socket, const char *name, void *tag) { + /* + * Name 'socket'. + */ + + REQUIRE(VALID_SOCKET(socket)); + + LOCK(&socket->lock); + strlcpy(socket->name, name, sizeof(socket->name)); + socket->tag = tag; + UNLOCK(&socket->lock); +} + +const char * +isc_socket_getname(isc_socket_t *socket) { + return (socket->name); +} + +void * +isc_socket_gettag(isc_socket_t *socket) { + return (socket->tag); +} + +int +isc_socket_getfd(isc_socket_t *socket) { + return ((short)socket->fd); +} + +void +isc_socketmgr_setreserved(isc_socketmgr_t *manager, uint32_t reserved) { + UNUSED(manager); + UNUSED(reserved); +} + +isc_socketevent_t * +isc_socket_socketevent(isc_mem_t *mctx, void *sender, isc_eventtype_t eventtype, + isc_taskaction_t action, void *arg) { + return (allocate_socketevent(mctx, sender, eventtype, action, arg)); +} + +bool +isc_socket_hasreuseport() { + return (false); +} + +#ifdef HAVE_LIBXML2 + +static const char * +_socktype(isc_sockettype_t type) { + switch (type) { + case isc_sockettype_udp: + return ("udp"); + case isc_sockettype_tcp: + return ("tcp"); + case isc_sockettype_unix: + return ("unix"); + default: + return ("not-initialized"); + } +} + +#define TRY0(a) \ + do { \ + xmlrc = (a); \ + if (xmlrc < 0) \ + goto error; \ + } while (0) +int +isc_socketmgr_renderxml(isc_socketmgr_t *mgr, void *writer0) { + isc_socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + socklen_t len; + int xmlrc; + xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0; + + LOCK(&mgr->lock); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "sockets")); + sock = ISC_LIST_HEAD(mgr->socklist); + while (sock != NULL) { + LOCK(&sock->lock); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "socket")); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); + TRY0(xmlTextWriterWriteFormatString(writer, "%p", sock)); + TRY0(xmlTextWriterEndElement(writer)); + + if (sock->name[0] != 0) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", + sock->name)); + TRY0(xmlTextWriterEndElement(writer)); /* name */ + } + + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString( + writer, "%" PRIuFAST32, + isc_refcount_current(&sock->references))); + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "type", + ISC_XMLCHAR _socktype(sock->type))); + + if (sock->connected) { + isc_sockaddr_format(&sock->address, peerbuf, + sizeof(peerbuf)); + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "peer-address", + ISC_XMLCHAR peerbuf)); + } + + len = sizeof(addr); + if (getsockname(sock->fd, &addr.type.sa, (void *)&len) == 0) { + isc_sockaddr_format(&addr, peerbuf, sizeof(peerbuf)); + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "local-address", + ISC_XMLCHAR peerbuf)); + } + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "states")); + if (sock->pending_recv) { + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "state", + ISC_XMLCHAR "pending-receive")); + } + if (sock->pending_send) { + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "state", + ISC_XMLCHAR "pending-send")); + } + if (sock->pending_accept) { + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "state", + ISC_XMLCHAR "pending_accept")); + } + if (sock->listener) { + TRY0(xmlTextWriterWriteElement(writer, + ISC_XMLCHAR "state", + ISC_XMLCHAR "listener")); + } + if (sock->connected) { + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "state", + ISC_XMLCHAR "connected")); + } + if (sock->pending_connect) { + TRY0(xmlTextWriterWriteElement( + writer, ISC_XMLCHAR "state", + ISC_XMLCHAR "connecting")); + } + if (sock->bound) { + TRY0(xmlTextWriterWriteElement(writer, + ISC_XMLCHAR "state", + ISC_XMLCHAR "bound")); + } + + TRY0(xmlTextWriterEndElement(writer)); /* states */ + + TRY0(xmlTextWriterEndElement(writer)); /* socket */ + + UNLOCK(&sock->lock); + sock = ISC_LIST_NEXT(sock, link); + } + TRY0(xmlTextWriterEndElement(writer)); /* sockets */ + +error: + if (sock != NULL) { + UNLOCK(&sock->lock); + } + + UNLOCK(&mgr->lock); + + return (xmlrc); +} +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON_C +#define CHECKMEM(m) \ + do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY; \ + goto error; \ + } \ + } while (0) +isc_result_t +isc_socketmgr_renderjson(isc_socketmgr_t *mgr, void *stats0) { + isc_result_t result = ISC_R_SUCCESS; + isc_socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + socklen_t len; + json_object *obj, *array = json_object_new_array(); + json_object *stats = (json_object *)stats; + + CHECKMEM(array); + + LOCK(&mgr->lock); + +#ifdef USE_SHARED_MANAGER + obj = json_object_new_int(mgr->refs); + CHECKMEM(obj); + json_object_object_add(stats, "references", obj); +#endif /* USE_SHARED_MANAGER */ + + sock = ISC_LIST_HEAD(mgr->socklist); + while (sock != NULL) { + json_object *states, *entry = json_object_new_object(); + char buf[255]; + + CHECKMEM(entry); + json_object_array_add(array, entry); + + LOCK(&sock->lock); + + snprintf(buf, sizeof(buf), "%p", sock); + obj = json_object_new_string(buf); + CHECKMEM(obj); + json_object_object_add(entry, "id", obj); + + if (sock->name[0] != 0) { + obj = json_object_new_string(sock->name); + CHECKMEM(obj); + json_object_object_add(entry, "name", obj); + } + + obj = json_object_new_int( + isc_refcount_current(&sock->references)); + CHECKMEM(obj); + json_object_object_add(entry, "references", obj); + + obj = json_object_new_string(_socktype(sock->type)); + CHECKMEM(obj); + json_object_object_add(entry, "type", obj); + + if (sock->connected) { + isc_sockaddr_format(&sock->address, peerbuf, + sizeof(peerbuf)); + obj = json_object_new_string(peerbuf); + CHECKMEM(obj); + json_object_object_add(entry, "peer-address", obj); + } + + len = sizeof(addr); + if (getsockname(sock->fd, &addr.type.sa, (void *)&len) == 0) { + isc_sockaddr_format(&addr, peerbuf, sizeof(peerbuf)); + obj = json_object_new_string(peerbuf); + CHECKMEM(obj); + json_object_object_add(entry, "local-address", obj); + } + + states = json_object_new_array(); + CHECKMEM(states); + json_object_object_add(entry, "states", states); + + if (sock->pending_recv) { + obj = json_object_new_string("pending-receive"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->pending_send) { + obj = json_object_new_string("pending-send"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->pending_accept) { + obj = json_object_new_string("pending-accept"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->listener) { + obj = json_object_new_string("listener"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->connected) { + obj = json_object_new_string("connected"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->pending_connect) { + obj = json_object_new_string("connecting"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + if (sock->bound) { + obj = json_object_new_string("bound"); + CHECKMEM(obj); + json_object_array_add(states, obj); + } + + UNLOCK(&sock->lock); + sock = ISC_LIST_NEXT(sock, link); + } + + json_object_object_add(stats, "sockets", array); + array = NULL; + result = ISC_R_SUCCESS; + +error: + if (array != NULL) { + json_object_put(array); + } + + if (sock != NULL) { + UNLOCK(&sock->lock); + } + + UNLOCK(&mgr->lock); + + return (result); +} +#endif /* HAVE_JSON_C */ + +void +isc_socketmgr_maxudp(isc_socketmgr_t *manager, unsigned int maxudp) { + REQUIRE(VALID_MANAGER(manager)); + + manager->maxudp = maxudp; +} diff --git a/lib/isc/win32/stdio.c b/lib/isc/win32/stdio.c new file mode 100644 index 0000000..1e55c30 --- /dev/null +++ b/lib/isc/win32/stdio.c @@ -0,0 +1,158 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include "errno2result.h" + +isc_result_t +isc_stdio_open(const char *filename, const char *mode, FILE **fp) { + FILE *f; + + f = fopen(filename, mode); + if (f == NULL) { + return (isc__errno2result(errno)); + } + *fp = f; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_stdio_close(FILE *f) { + int r; + + r = fclose(f); + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_stdio_seek(FILE *f, off_t offset, int whence) { + int r; + +#ifndef _WIN64 + r = fseek(f, offset, whence); +#else /* ifndef _WIN64 */ + r = _fseeki64(f, offset, whence); +#endif /* ifndef _WIN64 */ + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_stdio_tell(FILE *f, off_t *offsetp) { +#ifndef _WIN64 + long r; +#else /* ifndef _WIN64 */ + __int64 r; +#endif /* ifndef _WIN64 */ + + REQUIRE(offsetp != NULL); + +#ifndef _WIN64 + r = ftell(f); +#else /* ifndef _WIN64 */ + r = _ftelli64(f); +#endif /* ifndef _WIN64 */ + if (r >= 0) { + *offsetp = r; + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_stdio_read(void *ptr, size_t size, size_t nmemb, FILE *f, size_t *nret) { + isc_result_t result = ISC_R_SUCCESS; + size_t r; + + clearerr(f); + r = fread(ptr, size, nmemb, f); + if (r != nmemb) { + if (feof(f)) { + result = ISC_R_EOF; + } else { + result = isc__errno2result(errno); + } + } + if (nret != NULL) { + *nret = r; + } + return (result); +} + +isc_result_t +isc_stdio_write(const void *ptr, size_t size, size_t nmemb, FILE *f, + size_t *nret) { + isc_result_t result = ISC_R_SUCCESS; + size_t r; + + clearerr(f); + r = fwrite(ptr, size, nmemb, f); + if (r != nmemb) { + result = isc__errno2result(errno); + } + if (nret != NULL) { + *nret = r; + } + return (result); +} + +isc_result_t +isc_stdio_flush(FILE *f) { + int r; + + r = fflush(f); + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} + +isc_result_t +isc_stdio_sync(FILE *f) { + struct _stat buf; + int r; + + if (_fstat(_fileno(f), &buf) != 0) { + return (isc__errno2result(errno)); + } + + /* + * Only call _commit() on regular files. + */ + if ((buf.st_mode & S_IFMT) != S_IFREG) { + return (ISC_R_SUCCESS); + } + + r = _commit(_fileno(f)); + if (r == 0) { + return (ISC_R_SUCCESS); + } else { + return (isc__errno2result(errno)); + } +} diff --git a/lib/isc/win32/stdtime.c b/lib/isc/win32/stdtime.c new file mode 100644 index 0000000..93fcd66 --- /dev/null +++ b/lib/isc/win32/stdtime.c @@ -0,0 +1,43 @@ +/* + * 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 + +#include +#include +#include + +void +isc_stdtime_get(isc_stdtime_t *t) { + /* + * Set 't' to the number of seconds past 00:00:00 UTC, January 1, 1970. + */ + + REQUIRE(t != NULL); + + (void)_time32(t); +} + +void +isc_stdtime_tostring(isc_stdtime_t t, char *out, size_t outlen) { + time_t when; + + REQUIRE(out != NULL); + /* Minimum buffer as per ctime_r() specification. */ + REQUIRE(outlen >= 26); + + /* time_t and isc_stdtime_t might be different sizes */ + when = t; + INSIST((ctime_s(out, outlen, &when) == 0)); + *(out + strlen(out) - 1) = '\0'; +} diff --git a/lib/isc/win32/syslog.c b/lib/isc/win32/syslog.c new file mode 100644 index 0000000..ad23a0f --- /dev/null +++ b/lib/isc/win32/syslog.c @@ -0,0 +1,171 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +static HANDLE hAppLog = NULL; +static FILE *log_stream; +static int debug_level = 0; + +static struct dsn_c_pvt_sfnt { + int val; + const char *strval; +} facilities[] = { { LOG_KERN, "kern" }, + { LOG_USER, "user" }, + { LOG_MAIL, "mail" }, + { LOG_DAEMON, "daemon" }, + { LOG_AUTH, "auth" }, + { LOG_SYSLOG, "syslog" }, + { LOG_LPR, "lpr" }, +#ifdef LOG_NEWS + { LOG_NEWS, "news" }, +#endif /* ifdef LOG_NEWS */ +#ifdef LOG_UUCP + { LOG_UUCP, "uucp" }, +#endif /* ifdef LOG_UUCP */ +#ifdef LOG_CRON + { LOG_CRON, "cron" }, +#endif /* ifdef LOG_CRON */ +#ifdef LOG_AUTHPRIV + { LOG_AUTHPRIV, "authpriv" }, +#endif /* ifdef LOG_AUTHPRIV */ +#ifdef LOG_FTP + { LOG_FTP, "ftp" }, +#endif /* ifdef LOG_FTP */ + { LOG_LOCAL0, "local0" }, + { LOG_LOCAL1, "local1" }, + { LOG_LOCAL2, "local2" }, + { LOG_LOCAL3, "local3" }, + { LOG_LOCAL4, "local4" }, + { LOG_LOCAL5, "local5" }, + { LOG_LOCAL6, "local6" }, + { LOG_LOCAL7, "local7" }, + { 0, NULL } }; + +isc_result_t +isc_syslog_facilityfromstring(const char *str, int *facilityp) { + int i; + + REQUIRE(str != NULL); + REQUIRE(facilityp != NULL); + + for (i = 0; facilities[i].strval != NULL; i++) { + if (strcasecmp(facilities[i].strval, str) == 0) { + *facilityp = facilities[i].val; + return (ISC_R_SUCCESS); + } + } + return (ISC_R_NOTFOUND); +} + +/* + * Log to the NT Event Log + */ +void +syslog(int level, const char *fmt, ...) { + va_list ap; + char buf[1024]; + char *str[1]; + + str[0] = buf; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + + /* Make sure that the channel is open to write the event */ + if (hAppLog != NULL) { + switch (level) { + case LOG_INFO: + case LOG_NOTICE: + case LOG_DEBUG: + ReportEvent(hAppLog, EVENTLOG_INFORMATION_TYPE, 0, + BIND_INFO_MSG, NULL, 1, 0, str, NULL); + break; + case LOG_WARNING: + ReportEvent(hAppLog, EVENTLOG_WARNING_TYPE, 0, + BIND_WARN_MSG, NULL, 1, 0, str, NULL); + break; + default: + ReportEvent(hAppLog, EVENTLOG_ERROR_TYPE, 0, + BIND_ERR_MSG, NULL, 1, 0, str, NULL); + break; + } + } +} + +/* + * Initialize event logging + */ +void +openlog(const char *name, int flags, ...) { + /* Get a handle to the Application event log */ + hAppLog = RegisterEventSource(NULL, name); +} + +/* + * Close the Handle to the application Event Log + * We don't care whether or not we succeeded so ignore return values + * In fact if we failed then we would have nowhere to put the message + */ +void +closelog(void) { + DeregisterEventSource(hAppLog); +} + +/* + * Keep event logging synced with the current debug level + */ +void +ModifyLogLevel(int level) { + debug_level = level; +} + +/* + * Initialize logging for the port section of libbind. + * Piggyback onto stream given. + */ +void +InitNTLogging(FILE *stream, int debug) { + log_stream = stream; + ModifyLogLevel(debug); +} +/* + * This function is for reporting errors to the application + * event log in case the regular syslog is not available + * mainly during startup. It should not be used under normal + * circumstances. + */ +void +NTReportError(const char *name, const char *str) { + HANDLE hNTAppLog = NULL; + const char *buf[1]; + + buf[0] = str; + + hNTAppLog = RegisterEventSource(NULL, name); + + ReportEvent(hNTAppLog, EVENTLOG_ERROR_TYPE, 0, BIND_ERR_MSG, NULL, 1, 0, + buf, NULL); + + DeregisterEventSource(hNTAppLog); +} diff --git a/lib/isc/win32/syslog.h b/lib/isc/win32/syslog.h new file mode 100644 index 0000000..e785144 --- /dev/null +++ b/lib/isc/win32/syslog.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef _SYSLOG_H +#define _SYSLOG_H + +#include + +/* Constant definitions for openlog() */ +#define LOG_PID 1 +#define LOG_CONS 2 +/* NT event log does not support facility level */ +#define LOG_KERN 0 +#define LOG_USER 0 +#define LOG_MAIL 0 +#define LOG_DAEMON 0 +#define LOG_AUTH 0 +#define LOG_SYSLOG 0 +#define LOG_LPR 0 +#define LOG_LOCAL0 0 +#define LOG_LOCAL1 0 +#define LOG_LOCAL2 0 +#define LOG_LOCAL3 0 +#define LOG_LOCAL4 0 +#define LOG_LOCAL5 0 +#define LOG_LOCAL6 0 +#define LOG_LOCAL7 0 + +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but signification condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +void +syslog(int level, const char *fmt, ...); + +void +openlog(const char *, int, ...); + +void +closelog(void); + +void +ModifyLogLevel(int level); + +void +InitNTLogging(FILE *, int); + +void +NTReportError(const char *, const char *); +/* + * Include the event codes required for logging. + */ +#include + +#endif /* ifndef _SYSLOG_H */ diff --git a/lib/isc/win32/thread.c b/lib/isc/win32/thread.c new file mode 100644 index 0000000..e373e75 --- /dev/null +++ b/lib/isc/win32/thread.c @@ -0,0 +1,82 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include "trampoline_p.h" + +void +isc_thread_create(isc_threadfunc_t start, isc_threadarg_t arg, + isc_thread_t *threadp) { + isc_thread_t thread; + unsigned int id; + isc__trampoline_t *trampoline_arg; + + trampoline_arg = isc__trampoline_get(start, arg); + + thread = (isc_thread_t)_beginthreadex(NULL, 0, isc__trampoline_run, + trampoline_arg, 0, &id); + if (thread == NULL) { + char strbuf[ISC_STRERRORSIZE]; + strerror_r(errno, strbuf, sizeof(strbuf)); + isc_error_fatal(__FILE__, __LINE__, "_beginthreadex failed: %s", + strbuf); + } + + *threadp = thread; + + return; +} + +void +isc_thread_join(isc_thread_t thread, isc_threadresult_t *rp) { + DWORD result; + + result = WaitForSingleObject(thread, INFINITE); + if (result != WAIT_OBJECT_0) { + isc_error_fatal(__FILE__, __LINE__, + "WaitForSingleObject() != WAIT_OBJECT_0"); + } + if (rp != NULL && !GetExitCodeThread(thread, rp)) { + isc_error_fatal(__FILE__, __LINE__, + "GetExitCodeThread() failed: %d", + GetLastError()); + } + (void)CloseHandle(thread); +} + +void +isc_thread_setconcurrency(unsigned int level) { + /* + * This is unnecessary on Win32 systems, but is here so that the + * call exists + */ +} + +void +isc_thread_setname(isc_thread_t thread, const char *name) { + UNUSED(thread); + UNUSED(name); +} + +isc_result_t +isc_thread_setaffinity(int cpu) { + /* no-op on Windows for now */ + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/win32/time.c b/lib/isc/win32/time.c new file mode 100644 index 0000000..c29edd3 --- /dev/null +++ b/lib/isc/win32/time.c @@ -0,0 +1,651 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * struct FILETIME uses "100-nanoseconds intervals". + * NS / S = 1000000000 (10^9). + * While it is reasonably obvious that this makes the needed + * conversion factor 10^7, it is coded this way for additional clarity. + */ +#define NS_PER_S 1000000000 +#define NS_INTERVAL 100 +#define INTERVALS_PER_S (NS_PER_S / NS_INTERVAL) + +/*** + *** Absolute Times + ***/ + +static const isc_time_t epoch = { { 0, 0 } }; +LIBISC_EXTERNAL_DATA const isc_time_t *const isc_time_epoch = &epoch; + +/*** + *** Intervals + ***/ + +static const isc_interval_t zero_interval = { 0 }; +LIBISC_EXTERNAL_DATA const isc_interval_t *const isc_interval_zero = + &zero_interval; + +void +isc_interval_set(isc_interval_t *i, unsigned int seconds, + unsigned int nanoseconds) { + REQUIRE(i != NULL); + REQUIRE(nanoseconds < NS_PER_S); + + /* + * This rounds nanoseconds up not down. + */ + i->interval = (LONGLONG)seconds * INTERVALS_PER_S + + (nanoseconds + NS_INTERVAL - 1) / NS_INTERVAL; +} + +bool +isc_interval_iszero(const isc_interval_t *i) { + REQUIRE(i != NULL); + if (i->interval == 0) { + return (true); + } + + return (false); +} + +void +isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) { + SYSTEMTIME epoch1970 = { 1970, 1, 4, 1, 0, 0, 0, 0 }; + FILETIME temp; + ULARGE_INTEGER i1; + + REQUIRE(t != NULL); + REQUIRE(nanoseconds < NS_PER_S); + + SystemTimeToFileTime(&epoch1970, &temp); + + i1.LowPart = temp.dwLowDateTime; + i1.HighPart = temp.dwHighDateTime; + + /* cppcheck-suppress unreadVariable */ + i1.QuadPart += (unsigned __int64)nanoseconds / 100; + /* cppcheck-suppress unreadVariable */ + i1.QuadPart += (unsigned __int64)seconds * 10000000; + + t->absolute.dwLowDateTime = i1.LowPart; + t->absolute.dwHighDateTime = i1.HighPart; +} + +void +isc_time_settoepoch(isc_time_t *t) { + REQUIRE(t != NULL); + + t->absolute.dwLowDateTime = 0; + t->absolute.dwHighDateTime = 0; +} + +bool +isc_time_isepoch(const isc_time_t *t) { + REQUIRE(t != NULL); + + if (t->absolute.dwLowDateTime == 0 && t->absolute.dwHighDateTime == 0) { + return (true); + } + + return (false); +} + +isc_result_t +isc_time_now(isc_time_t *t) { + REQUIRE(t != NULL); + + GetSystemTimeAsFileTime(&t->absolute); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_time_now_hires(isc_time_t *t) { + REQUIRE(t != NULL); + + GetSystemTimePreciseAsFileTime(&t->absolute); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) { + ULARGE_INTEGER i1; + + REQUIRE(t != NULL); + REQUIRE(i != NULL); + + GetSystemTimeAsFileTime(&t->absolute); + + i1.LowPart = t->absolute.dwLowDateTime; + i1.HighPart = t->absolute.dwHighDateTime; + + if (UINT64_MAX - i1.QuadPart < (unsigned __int64)i->interval) { + return (ISC_R_RANGE); + } + + /* cppcheck-suppress unreadVariable */ + i1.QuadPart += i->interval; + + t->absolute.dwLowDateTime = i1.LowPart; + t->absolute.dwHighDateTime = i1.HighPart; + + return (ISC_R_SUCCESS); +} + +int +isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) { + REQUIRE(t1 != NULL && t2 != NULL); + + return ((int)CompareFileTime(&t1->absolute, &t2->absolute)); +} + +isc_result_t +isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) { + ULARGE_INTEGER i1; + + REQUIRE(t != NULL && i != NULL && result != NULL); + + i1.LowPart = t->absolute.dwLowDateTime; + i1.HighPart = t->absolute.dwHighDateTime; + + if (UINT64_MAX - i1.QuadPart < (unsigned __int64)i->interval) { + return (ISC_R_RANGE); + } + + /* cppcheck-suppress unreadVariable */ + i1.QuadPart += i->interval; + + result->absolute.dwLowDateTime = i1.LowPart; + result->absolute.dwHighDateTime = i1.HighPart; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_time_subtract(const isc_time_t *t, const isc_interval_t *i, + isc_time_t *result) { + ULARGE_INTEGER i1; + + REQUIRE(t != NULL && i != NULL && result != NULL); + + i1.LowPart = t->absolute.dwLowDateTime; + i1.HighPart = t->absolute.dwHighDateTime; + + if (i1.QuadPart < (unsigned __int64)i->interval) { + return (ISC_R_RANGE); + } + + /* cppcheck-suppress unreadVariable */ + i1.QuadPart -= i->interval; + + result->absolute.dwLowDateTime = i1.LowPart; + result->absolute.dwHighDateTime = i1.HighPart; + + return (ISC_R_SUCCESS); +} + +uint64_t +isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) { + ULARGE_INTEGER i1, i2; + LONGLONG i3; + + REQUIRE(t1 != NULL && t2 != NULL); + + /* cppcheck-suppress unreadVariable */ + i1.LowPart = t1->absolute.dwLowDateTime; + /* cppcheck-suppress unreadVariable */ + i1.HighPart = t1->absolute.dwHighDateTime; + /* cppcheck-suppress unreadVariable */ + i2.LowPart = t2->absolute.dwLowDateTime; + /* cppcheck-suppress unreadVariable */ + i2.HighPart = t2->absolute.dwHighDateTime; + + if (i1.QuadPart <= i2.QuadPart) { + return (0); + } + + /* + * Convert to microseconds. + */ + i3 = (i1.QuadPart - i2.QuadPart) / 10; + + return (i3); +} + +uint32_t +isc_time_seconds(const isc_time_t *t) { + SYSTEMTIME epoch1970 = { 1970, 1, 4, 1, 0, 0, 0, 0 }; + FILETIME temp; + ULARGE_INTEGER i1, i2; + LONGLONG i3; + + SystemTimeToFileTime(&epoch1970, &temp); + + /* cppcheck-suppress unreadVariable */ + i1.LowPart = t->absolute.dwLowDateTime; + /* cppcheck-suppress unreadVariable */ + i1.HighPart = t->absolute.dwHighDateTime; + /* cppcheck-suppress unreadVariable */ + i2.LowPart = temp.dwLowDateTime; + /* cppcheck-suppress unreadVariable */ + i2.HighPart = temp.dwHighDateTime; + + i3 = (i1.QuadPart - i2.QuadPart) / 10000000; + + return ((uint32_t)i3); +} + +isc_result_t +isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) { + time_t seconds; + + REQUIRE(t != NULL); + + seconds = (time_t)isc_time_seconds(t); + + INSIST(sizeof(unsigned int) == sizeof(uint32_t)); + INSIST(sizeof(time_t) >= sizeof(uint32_t)); + + if (isc_time_seconds(t) > (~0U >> 1) && seconds <= (time_t)(~0U >> 1)) { + return (ISC_R_RANGE); + } + + *secondsp = seconds; + + return (ISC_R_SUCCESS); +} + +uint32_t +isc_time_nanoseconds(const isc_time_t *t) { + ULARGE_INTEGER i; + + i.LowPart = t->absolute.dwLowDateTime; + i.HighPart = t->absolute.dwHighDateTime; + return ((uint32_t)(i.QuadPart % 10000000) * 100); +} + +void +isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) { + FILETIME localft; + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToLocalFileTime(&t->absolute, &localft) && + FileTimeToSystemTime(&localft, &st)) + { + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "dd-MMM-yyyy", + DateBuf, 50); + GetTimeFormat(LOCALE_USER_DEFAULT, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + NULL, TimeBuf, 50); + + snprintf(buf, len, "%s %s.%03u", DateBuf, TimeBuf, + st.wMilliseconds); + } else { + strlcpy(buf, "99-Bad-9999 99:99:99.999", len); + } +} + +void +isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strftime() format: "%a, %d %b %Y %H:%M:%S GMT" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "ddd',' dd MMM yyyy", + DateBuf, 50); + GetTimeFormat(LOCALE_USER_DEFAULT, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + + snprintf(buf, len, "%s %s GMT", DateBuf, TimeBuf); + } else { + buf[0] = 0; + } +} + +isc_result_t +isc_time_parsehttptimestamp(char *buf, isc_time_t *t) { + struct tm t_tm; + time_t when; + char *p; + + REQUIRE(buf != NULL); + REQUIRE(t != NULL); + + p = isc_tm_strptime(buf, "%a, %d %b %Y %H:%M:%S", &t_tm); + if (p == NULL) { + return (ISC_R_UNEXPECTED); + } + when = isc_tm_timegm(&t_tm); + if (when == -1) { + return (ISC_R_UNEXPECTED); + } + isc_time_set(t, (unsigned int)when, 0); + return (ISC_R_SUCCESS); +} + +void +isc_time_formatISO8601L(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%S" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "yyyy-MM-dd", + DateBuf, 50); + GetTimeFormat(LOCALE_USER_DEFAULT, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + snprintf(buf, len, "%sT%s", DateBuf, TimeBuf); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%S.SSS" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "yyyy-MM-dd", + DateBuf, 50); + GetTimeFormat(LOCALE_USER_DEFAULT, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + snprintf(buf, len, "%sT%s.%03u", DateBuf, TimeBuf, + st.wMilliseconds); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatISO8601Lus(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%S.SSSSSS" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + ULARGE_INTEGER i; + + GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, "yyyy-MM-dd", + DateBuf, 50); + GetTimeFormat(LOCALE_USER_DEFAULT, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + i.LowPart = t->absolute.dwLowDateTime; + i.HighPart = t->absolute.dwHighDateTime; + snprintf(buf, len, "%sT%s.%06u", DateBuf, TimeBuf, + (uint32_t)(i.QuadPart % 10000000) / 10); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%SZ" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_NEUTRAL, 0, &st, "yyyy-MM-dd", DateBuf, + 50); + GetTimeFormat(LOCALE_NEUTRAL, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + snprintf(buf, len, "%sT%sZ", DateBuf, TimeBuf); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%S.SSSZ" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_NEUTRAL, 0, &st, "yyyy-MM-dd", DateBuf, + 50); + GetTimeFormat(LOCALE_NEUTRAL, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + snprintf(buf, len, "%sT%s.%03uZ", DateBuf, TimeBuf, + st.wMilliseconds); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y-%m-%dT%H:%M:%S.SSSSSSZ" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + ULARGE_INTEGER i; + + GetDateFormat(LOCALE_NEUTRAL, 0, &st, "yyyy-MM-dd", DateBuf, + 50); + GetTimeFormat(LOCALE_NEUTRAL, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hh':'mm':'ss", TimeBuf, 50); + i.LowPart = t->absolute.dwLowDateTime; + i.HighPart = t->absolute.dwHighDateTime; + snprintf(buf, len, "%sT%s.%06uZ", DateBuf, TimeBuf, + (uint32_t)(i.QuadPart % 10000000) / 10); + } else { + buf[0] = 0; + } +} + +void +isc_time_formatshorttimestamp(const isc_time_t *t, char *buf, + unsigned int len) { + SYSTEMTIME st; + char DateBuf[50]; + char TimeBuf[50]; + + /* strtime() format: "%Y%m%d%H%M%SSSS" */ + + REQUIRE(t != NULL); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + if (FileTimeToSystemTime(&t->absolute, &st)) { + GetDateFormat(LOCALE_NEUTRAL, 0, &st, "yyyyMMdd", DateBuf, 50); + GetTimeFormat(LOCALE_NEUTRAL, + TIME_NOTIMEMARKER | TIME_FORCE24HOURFORMAT, &st, + "hhmmss", TimeBuf, 50); + snprintf(buf, len, "%s%s%03u", DateBuf, TimeBuf, + st.wMilliseconds); + } else { + buf[0] = 0; + } +} + +/* + * POSIX Shims + */ + +struct tm * +gmtime_r(const time_t *clock, struct tm *result) { + errno_t ret = gmtime_s(result, clock); + if (ret != 0) { + errno = ret; + return (NULL); + } + return (result); +} + +struct tm * +localtime_r(const time_t *clock, struct tm *result) { + errno_t ret = localtime_s(result, clock); + if (ret != 0) { + errno = ret; + return (NULL); + } + return (result); +} + +#define BILLION 1000000000 + +static isc_once_t nsec_ticks_once = ISC_ONCE_INIT; +static double nsec_ticks = 0; + +static void +nsec_ticks_init(void) { + LARGE_INTEGER ticks; + RUNTIME_CHECK(QueryPerformanceFrequency(&ticks) != 0); + nsec_ticks = (double)ticks.QuadPart / 1000000000.0; + RUNTIME_CHECK(nsec_ticks != 0.0); +} + +int +nanosleep(const struct timespec *req, struct timespec *rem) { + int_fast64_t sleep_msec; + uint_fast64_t ticks, until; + LARGE_INTEGER before, after; + + RUNTIME_CHECK(isc_once_do(&nsec_ticks_once, nsec_ticks_init) == + ISC_R_SUCCESS); + + if (req->tv_nsec < 0 || BILLION <= req->tv_nsec) { + errno = EINVAL; + return (-1); + } + + /* Sleep() is not interruptible; there is no remaining delay ever */ + if (rem != NULL) { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + + if (req->tv_sec >= 0) { + /* + * For requested delays of one second or more, 15ms resolution + * granularity is sufficient. + */ + Sleep(req->tv_sec * 1000 + req->tv_nsec / 1000000); + + return (0); + } + + /* Sleep has <-8,8> ms precision, so substract 10 milliseconds */ + sleep_msec = (int64_t)req->tv_nsec / 1000000 - 10; + ticks = req->tv_nsec * nsec_ticks; + + RUNTIME_CHECK(QueryPerformanceCounter(&before) != 0); + + until = before.QuadPart + ticks; + + if (sleep_msec > 0) { + Sleep(sleep_msec); + } + + while (true) { + LARGE_INTEGER after; + RUNTIME_CHECK(QueryPerformanceCounter(&after) != 0); + if (after.QuadPart >= until) { + break; + } + } + +done: + return (0); +} + +int +usleep(useconds_t useconds) { + struct timespec req; + + req.tv_sec = useconds / 1000000; + req.tv_nsec = (useconds * 1000) % 1000000000; + + nanosleep(&req, NULL); + + return (0); +} diff --git a/lib/isc/win32/unistd.h b/lib/isc/win32/unistd.h new file mode 100644 index 0000000..e1aff40 --- /dev/null +++ b/lib/isc/win32/unistd.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/* None of these are defined in NT, so define them for our use */ +#define O_NONBLOCK 1 +#define PORT_NONBLOCK O_NONBLOCK + +/* + * fcntl() commands + */ +#define F_SETFL 0 +#define F_GETFL 1 +#define F_SETFD 2 +#define F_GETFD 3 +/* + * Enough problems not having full fcntl() without worrying about this! + */ +#undef F_DUPFD + +int +fcntl(int, int, ...); + +/* + * access() related definitions for winXP + */ +#include +#ifndef F_OK +#define F_OK 0 +#endif /* ifndef F_OK */ + +#ifndef X_OK +#define X_OK 1 +#endif /* ifndef X_OK */ + +#ifndef W_OK +#define W_OK 2 +#endif /* ifndef W_OK */ + +#ifndef R_OK +#define R_OK 4 +#endif /* ifndef R_OK */ + +#define access _access + +#include diff --git a/lib/isc/win32/version.c b/lib/isc/win32/version.c new file mode 100644 index 0000000..b49347f --- /dev/null +++ b/lib/isc/win32/version.c @@ -0,0 +1,18 @@ +/* + * 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 + +#include + +LIBISC_EXTERNAL_DATA const char isc_version[] = VERSION; diff --git a/lib/isc/win32/win32os.c b/lib/isc/win32/win32os.c new file mode 100644 index 0000000..4f86d38 --- /dev/null +++ b/lib/isc/win32/win32os.c @@ -0,0 +1,113 @@ +/* + * 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 + +#ifndef TESTVERSION +#include +#else /* ifndef TESTVERSION */ +#include + +#include +#endif /* ifndef TESTVERSION */ +#include + +int +isc_win32os_versioncheck(unsigned int major, unsigned int minor, + unsigned int spmajor, unsigned int spminor) { + OSVERSIONINFOEX osVer; + DWORD typeMask; + ULONGLONG conditionMask; + + memset(&osVer, 0, sizeof(OSVERSIONINFOEX)); + osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + typeMask = 0; + conditionMask = 0; + + /* Optimistic: likely greater */ + osVer.dwMajorVersion = major; + typeMask |= VER_MAJORVERSION; + conditionMask = VerSetConditionMask(conditionMask, VER_MAJORVERSION, + VER_GREATER); + osVer.dwMinorVersion = minor; + typeMask |= VER_MINORVERSION; + conditionMask = VerSetConditionMask(conditionMask, VER_MINORVERSION, + VER_GREATER); + osVer.wServicePackMajor = spmajor; + typeMask |= VER_SERVICEPACKMAJOR; + conditionMask = VerSetConditionMask(conditionMask, VER_SERVICEPACKMAJOR, + VER_GREATER); + osVer.wServicePackMinor = spminor; + typeMask |= VER_SERVICEPACKMINOR; + conditionMask = VerSetConditionMask(conditionMask, VER_SERVICEPACKMINOR, + VER_GREATER); + if (VerifyVersionInfo(&osVer, typeMask, conditionMask)) { + return (1); + } + + /* Failed: retry with equal */ + conditionMask = 0; + conditionMask = VerSetConditionMask(conditionMask, VER_MAJORVERSION, + VER_EQUAL); + conditionMask = VerSetConditionMask(conditionMask, VER_MINORVERSION, + VER_EQUAL); + conditionMask = VerSetConditionMask(conditionMask, VER_SERVICEPACKMAJOR, + VER_EQUAL); + conditionMask = VerSetConditionMask(conditionMask, VER_SERVICEPACKMINOR, + VER_EQUAL); + if (VerifyVersionInfo(&osVer, typeMask, conditionMask)) { + return (0); + } else { + return (-1); + } +} + +#ifdef TESTVERSION +int +main(int argc, char **argv) { + unsigned int major = 0; + unsigned int minor = 0; + unsigned int spmajor = 0; + unsigned int spminor = 0; + int ret; + + if (argc > 1) { + --argc; + ++argv; + major = (unsigned int)atoi(argv[0]); + } + if (argc > 1) { + --argc; + ++argv; + minor = (unsigned int)atoi(argv[0]); + } + if (argc > 1) { + --argc; + ++argv; + spmajor = (unsigned int)atoi(argv[0]); + } + if (argc > 1) { + --argc; + POST(argc); + ++argv; + spminor = (unsigned int)atoi(argv[0]); + } + + ret = isc_win32os_versioncheck(major, minor, spmajor, spminor); + + printf("%s major %u minor %u SP major %u SP minor %u\n", + ret > 0 ? "greater" : (ret == 0 ? "equal" : "less"), major, + minor, spmajor, spminor); + return (ret); +} +#endif /* ifdef TESTVERSION */ -- cgit v1.2.3